@qwik.dev/core 2.0.0-beta.41 → 2.0.0-beta.43

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.41-dev+4d04076
3
+ * @qwik.dev/core 2.0.0-beta.43-dev+e478dfb
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
@@ -29,6 +29,9 @@ const STYLE = qDev
29
29
  const logError = (message, ...optionalParams) => {
30
30
  return createAndLogError(false, message, ...optionalParams);
31
31
  };
32
+ const logErrorAndThrowAsync = (message, ...optionalParams) => {
33
+ return createAndLogError(true, message, ...optionalParams);
34
+ };
32
35
  const throwErrorAndStop = (message, ...optionalParams) => {
33
36
  const error = createAndLogError(false, message, ...optionalParams);
34
37
  // eslint-disable-next-line no-debugger
@@ -186,9 +189,6 @@ const isString = (v) => {
186
189
  const isFunction = (v) => {
187
190
  return typeof v === 'function';
188
191
  };
189
- const isPrimitiveOrNullUndefined = (v) => {
190
- return (typeof v !== 'object' && typeof v !== 'function') || v === null || v === undefined;
191
- };
192
192
 
193
193
  const baseUrl = 'https://qwikdev-build-v2.qwik-8nx.pages.dev/docs/errors/#q';
194
194
  const codeToText = (code, ...parts) => {
@@ -418,6 +418,8 @@ const QSlotS = 'q:s';
418
418
  const QStatePatchAttrSelector = '[q\\:patch]';
419
419
  const QSuspenseResolved = 'q:r';
420
420
  const QSuspenseResultParent = 'q:rp';
421
+ const QErrorContentHost = 'q:ebc';
422
+ const QErrorFallbackHost = 'q:ebf';
421
423
  const QStyle = 'q:style';
422
424
  const QStyleSelector = 'style[q\\:style]';
423
425
  const QStyleSSelector = 'style[q\\:sstyle]';
@@ -470,6 +472,7 @@ const QDefaultSlot = '';
470
472
  const ELEMENT_ID = 'q:id';
471
473
  const ELEMENT_KEY = 'q:key';
472
474
  const ELEMENT_PROPS = 'q:props';
475
+ const USE_ON_PLACEHOLDER_KEY = ':useOn';
473
476
  /** @internal */
474
477
  const ELEMENT_SEQ = 'q:seq';
475
478
  const ELEMENT_SEQ_IDX = 'q:seqIdx';
@@ -489,8 +492,13 @@ const debugStyleScopeIdPrefixAttr = '__scopedStyleIdPrefix__';
489
492
 
490
493
  const MAX_RETRY_ON_PROMISE_COUNT = 100;
491
494
  const isPromise = (value) => {
492
- // not using "value instanceof Promise" to have zone.js support
493
- return !!value && typeof value == 'object' && typeof value.then === 'function';
495
+ try {
496
+ // not using "value instanceof Promise" to have zone.js support
497
+ return !!value && typeof value == 'object' && typeof value.then === 'function';
498
+ }
499
+ catch {
500
+ return false;
501
+ }
494
502
  };
495
503
  const safeCall = (call, thenFn, rejectFn) => {
496
504
  try {
@@ -518,8 +526,13 @@ const delay = (timeout) => {
518
526
  });
519
527
  };
520
528
  const checkError = (e) => {
521
- if (isServer && e instanceof ReferenceError && e.message.includes('window')) {
522
- e.message = 'It seems like you forgot to add "if (isBrowser) {...}" here:' + e.message;
529
+ try {
530
+ if (isServer && e instanceof ReferenceError && e.message.includes('window')) {
531
+ e.message = 'It seems like you forgot to add "if (isBrowser) {...}" here:' + e.message;
532
+ }
533
+ }
534
+ catch {
535
+ // ignore
523
536
  }
524
537
  };
525
538
  const justThrow = (e) => {
@@ -1266,7 +1279,7 @@ const COMMA = ',';
1266
1279
  *
1267
1280
  * @public
1268
1281
  */
1269
- const version = "2.0.0-beta.41-dev+4d04076";
1282
+ const version = "2.0.0-beta.43-dev+e478dfb";
1270
1283
 
1271
1284
  const isNode = (value) => {
1272
1285
  return value && typeof value.nodeType === 'number';
@@ -3780,6 +3793,16 @@ const _propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
3780
3793
  function isSlotProp(prop) {
3781
3794
  return !prop.startsWith('q:') && !prop.startsWith(NON_SERIALIZABLE_MARKER_PREFIX);
3782
3795
  }
3796
+ function hasSlotProps(props) {
3797
+ if (props) {
3798
+ for (const prop in props) {
3799
+ if (isSlotProp(prop)) {
3800
+ return true;
3801
+ }
3802
+ }
3803
+ }
3804
+ return false;
3805
+ }
3783
3806
  /** @internal */
3784
3807
  const _restProps = (props, omit = [], target = {}) => {
3785
3808
  // The optimizer can apply this transform to plain, non-component objects and
@@ -4022,7 +4045,12 @@ function addCursorBoundary(cursorData, vNode) {
4022
4045
  if (!__EXPERIMENTAL__.suspense) {
4023
4046
  return;
4024
4047
  }
4025
- const boundary = getNearestCursorBoundary(vNode);
4048
+ let boundary = null;
4049
+ let current = vNode;
4050
+ while (current && !boundary) {
4051
+ boundary = getNearestCursorBoundary(current);
4052
+ current = current.slotParent || current.parent;
4053
+ }
4026
4054
  if (!boundary) {
4027
4055
  return;
4028
4056
  }
@@ -4169,7 +4197,7 @@ function runEventHandlerQRL(handler, event, element, ctx) {
4169
4197
  if (!(hostElement.flags & 32 /* VNodeFlags.Deleted */)) {
4170
4198
  return invokeApply(ctx, realHandler, [event, element]);
4171
4199
  }
4172
- }, (err) => container.handleError(err, hostElement));
4200
+ }, (err) => container.handleError(err, hostElement, "event" /* ErrorBoundaryPhase.Event */));
4173
4201
  }
4174
4202
  /**
4175
4203
  * This is called by qwik-loader to run a QRL. It has to be synchronous when possible.
@@ -5498,11 +5526,14 @@ function cleanup(container, journal, vNode, cursorRoot = null) {
5498
5526
  const obj = seq[i];
5499
5527
  if (isObject(obj)) {
5500
5528
  const objIsTask = isTask(obj);
5501
- if (objIsTask && obj.$flags$ & 1 /* TaskFlags.VISIBLE_TASK */) {
5502
- obj.$flags$ |= 16 /* TaskFlags.NEEDS_CLEANUP */;
5503
- markVNodeDirty(container, vCursor, 128 /* ChoreBits.CLEANUP */, cursorRoot);
5504
- // don't call cleanupDestroyable yet, do it by the scheduler
5505
- continue;
5529
+ if (objIsTask) {
5530
+ clearAllEffects(container, obj);
5531
+ if (obj.$flags$ & 1 /* TaskFlags.VISIBLE_TASK */) {
5532
+ obj.$flags$ |= 16 /* TaskFlags.NEEDS_CLEANUP */;
5533
+ markVNodeDirty(container, vCursor, 128 /* ChoreBits.CLEANUP */, cursorRoot);
5534
+ // don't call cleanupDestroyable yet, do it by the scheduler
5535
+ continue;
5536
+ }
5506
5537
  }
5507
5538
  // Stores and plain signals are only producers; their subscriptions are removed
5508
5539
  // when cleaning the consumers that read them. They don't own reactive backrefs.
@@ -5958,43 +5989,16 @@ function findFirstElementNode(jsx) {
5958
5989
  * This is necessary for headless components (components that don't render a real DOM element) to
5959
5990
  * have an anchor point for `useOn` event listeners that target the document or window.
5960
5991
  *
5961
- * @param jsx The JSX output to modify.
5992
+ * @param jsx The JSX output of the component.
5962
5993
  * @param placeholder The placeholder element to inject.
5963
- * @returns The modified JSX output.
5994
+ * @returns The JSX output with the placeholder as its last sibling.
5964
5995
  */
5965
5996
  function injectPlaceholderElement(jsx, placeholder) {
5966
- // For regular JSX nodes, we can append the placeholder to its children.
5967
- if (isJSXNode(jsx)) {
5968
- // Inline components may ignore children, while component$ children become projections.
5969
- if (jsx.type !== Fragment) {
5970
- return _jsxSorted(Fragment, null, null, [jsx, placeholder], 0, null);
5971
- }
5972
- if (jsx.children == null) {
5973
- jsx.children = placeholder;
5974
- }
5975
- else if (isArray(jsx.children)) {
5976
- jsx.children.push(placeholder);
5977
- }
5978
- else {
5979
- jsx.children = [jsx.children, placeholder];
5980
- }
5981
- return jsx;
5982
- }
5983
- // For primitives, we can't add children, so we wrap them in a fragment.
5984
- if (isPrimitiveOrNullUndefined(jsx)) {
5985
- return _jsxSorted(Fragment, null, null, [jsx, placeholder], 0, null);
5986
- }
5987
- // For an array of nodes, we inject the placeholder into the first element.
5988
- if (isArray(jsx) && jsx.length > 0) {
5989
- injectPlaceholderElement(jsx[0], placeholder);
5990
- return jsx;
5991
- }
5992
- // For anything else we do nothing.
5993
- return jsx;
5997
+ return isArray(jsx) ? [...jsx, placeholder] : [jsx, placeholder];
5994
5998
  }
5995
5999
  /** @returns An empty <script> element for adding qwik metadata attributes to */
5996
6000
  function createPlaceholderScriptNode() {
5997
- return new JSXNodeImpl('script', null, { hidden: '' }, null, 0, null);
6001
+ return new JSXNodeImpl('script', null, { hidden: '' }, null, 0, USE_ON_PLACEHOLDER_KEY);
5998
6002
  }
5999
6003
 
6000
6004
  function collectChildren(first, last = null) {
@@ -9335,7 +9339,10 @@ const runTask = (task, container, host) => {
9335
9339
  return pendingTask;
9336
9340
  }
9337
9341
  task.$flags$ &= -5 /* TaskFlags.DIRTY */;
9338
- const handleError = (reason) => container.handleError(reason, host);
9342
+ const handleError = (reason) => {
9343
+ tagErrorPhase(reason, "hook" /* ErrorBoundaryPhase.Hook */);
9344
+ container.handleError(reason, host, "hook" /* ErrorBoundaryPhase.Hook */);
9345
+ };
9339
9346
  let taskPromise = null;
9340
9347
  const result = maybeThen(cleanupAsyncDestroyable(task, handleError), () => {
9341
9348
  const iCtx = newInvokeContext(container.$locale$, host, TaskEvent);
@@ -9405,6 +9412,10 @@ function scheduleTask(_event, element) {
9405
9412
  setCaptures(deserializeCaptureDeltas(container, this));
9406
9413
  }
9407
9414
  const task = _captures[0];
9415
+ if (!task.$el$) {
9416
+ // An ErrorBoundary tore the host down; the task has nothing left to run against.
9417
+ return;
9418
+ }
9408
9419
  task.$flags$ |= 4 /* TaskFlags.DIRTY */;
9409
9420
  markVNodeDirty(container, task.$el$, 1 /* ChoreBits.TASKS */);
9410
9421
  });
@@ -9669,7 +9680,12 @@ const getStoreHandler = (value) => {
9669
9680
  return value[STORE_HANDLER];
9670
9681
  };
9671
9682
  const getStoreTarget = (value) => {
9672
- return value?.[STORE_TARGET] || null;
9683
+ try {
9684
+ return value?.[STORE_TARGET] || null;
9685
+ }
9686
+ catch {
9687
+ return null;
9688
+ }
9673
9689
  };
9674
9690
  /**
9675
9691
  * Force a store to recompute and schedule effects.
@@ -9770,11 +9786,17 @@ class StoreHandler {
9770
9786
  return this.toString;
9771
9787
  }
9772
9788
  const flags = this.$flags$;
9773
- if (flags & 1 /* StoreFlags.RECURSIVE */ &&
9774
- isObject(value) &&
9775
- !Object.isFrozen(value) &&
9776
- !isStore(value) &&
9777
- !Object.isFrozen(target)) {
9789
+ let shouldWrapRecursively = false;
9790
+ if (flags & 1 /* StoreFlags.RECURSIVE */ && isObject(value)) {
9791
+ try {
9792
+ shouldWrapRecursively =
9793
+ !Object.isFrozen(value) && !isStore(value) && !Object.isFrozen(target);
9794
+ }
9795
+ catch {
9796
+ // ignore
9797
+ }
9798
+ }
9799
+ if (shouldWrapRecursively) {
9778
9800
  return getOrCreateStore(value, this.$flags$, this.$container$);
9779
9801
  }
9780
9802
  return value;
@@ -10727,8 +10749,9 @@ class Serializer {
10727
10749
  }
10728
10750
  const out = [value.$computeQrl$, value.$effects$];
10729
10751
  if (isAsync) {
10752
+ const untrackedError = value.$untrackedError$;
10730
10753
  // After SSR, the signal is never loading, so no need to send it
10731
- out.push(value.$loadingEffects$, value.$errorEffects$, value.$untrackedError$);
10754
+ out.push(value.$loadingEffects$, value.$errorEffects$, untrackedError === undefined || isDev ? untrackedError : redactToGeneric(untrackedError));
10732
10755
  out.push(asyncFlags || undefined);
10733
10756
  }
10734
10757
  if (v !== NEEDS_COMPUTATION || concurrency !== undefined || timeout !== undefined) {
@@ -10765,8 +10788,9 @@ class Serializer {
10765
10788
  }
10766
10789
  else if (value instanceof Error) {
10767
10790
  const out = [value.message];
10768
- // flatten gives us the right output
10769
- out.push(...Object.entries(value).flat());
10791
+ for (const entry of Object.entries(value)) {
10792
+ out.push(entry[0], entry[1]);
10793
+ }
10770
10794
  /// In production we don't want to leak the stack trace.
10771
10795
  if (isDev) {
10772
10796
  out.push('stack', value.stack);
@@ -10774,15 +10798,20 @@ class Serializer {
10774
10798
  this.output(23 /* TypeIds.Error */, out);
10775
10799
  }
10776
10800
  else if (this.$serializationContext$.$isSsrNode$(value)) {
10801
+ // Paired with ssr-container's vnode-data INERT skip: drop the state root AND the path together.
10802
+ if (value.vnodeData &&
10803
+ value.vnodeData[0] & 32 /* VNodeDataFlag.INERT */ &&
10804
+ /[A-Za-z]/.test(value.id)) {
10805
+ this.output(3 /* TypeIds.Constant */, 0 /* Constants.Undefined */);
10806
+ return;
10807
+ }
10777
10808
  const rootIndex = this.$serializationContext$.$addRoot$(value);
10778
10809
  this.$serializationContext$.$setProp$(value, ELEMENT_ID, rootIndex);
10779
10810
  // we need to output before the vnode overwrites its values
10780
10811
  this.output(10 /* TypeIds.VNode */, value.id);
10781
10812
  const vNodeData = value.vnodeData;
10782
10813
  if (vNodeData) {
10783
- discoverValuesForVNodeData(vNodeData, (vNodeDataValue) => {
10784
- this.$serializationContext$.$addRoot$(vNodeDataValue);
10785
- });
10814
+ discoverValuesForVNodeData(vNodeData, this.$serializationContext$);
10786
10815
  this.$serializationContext$.$markSsrNodeForSerialization$(value, 16 /* VNodeDataFlag.SERIALIZE */);
10787
10816
  }
10788
10817
  if (value.children) {
@@ -11066,9 +11095,9 @@ function getCustomSerializerPromise(signal, value) {
11066
11095
  return data;
11067
11096
  });
11068
11097
  }
11069
- const discoverValuesForVNodeData = (vnodeData, callback) => {
11098
+ const discoverValuesForVNodeData = (vnodeData, serializationContext) => {
11070
11099
  const length = vnodeData.length;
11071
- for (let i = 0; i < length; i++) {
11100
+ for (let i = 1; i < length; i++) {
11072
11101
  const value = vnodeData[i];
11073
11102
  if (isSsrAttrs(value)) {
11074
11103
  for (const key in value) {
@@ -11081,7 +11110,7 @@ const discoverValuesForVNodeData = (vnodeData, callback) => {
11081
11110
  shouldSkipComponentProps(value, attrValue)))) {
11082
11111
  continue;
11083
11112
  }
11084
- callback(attrValue);
11113
+ serializationContext.$addRoot$(attrValue);
11085
11114
  }
11086
11115
  }
11087
11116
  }
@@ -12067,15 +12096,23 @@ const applyQwikComponentBody = (ssr, jsx, component) => {
12067
12096
 
12068
12097
  class MaybeAsyncSignal {
12069
12098
  }
12070
- const markPromiseHandled = (ssr, promise, host = null) => {
12071
- promise.catch((reason) => {
12072
- try {
12073
- ssr.handleError(reason, host);
12074
- }
12075
- catch {
12076
- // Original promise remains awaited later.
12077
- }
12078
- });
12099
+ // we need to differentiate between JSX functions and ssr container functions for error boundary
12100
+ // JSX functions need to be skipped after error boundary catch an error
12101
+ class InvokeJSXFunction {
12102
+ }
12103
+ const openBoundaryContentScope = (ssr, contentHost) => {
12104
+ const enclosing = ssr.$errorContentHost$;
12105
+ ssr.$errorContentHost$ = contentHost;
12106
+ return () => {
12107
+ ssr.$errorContentHost$ = enclosing;
12108
+ };
12109
+ };
12110
+ const isInsideFailedBoundaryContent = (ssr) => {
12111
+ const contentHost = ssr.$errorContentHost$;
12112
+ return !!contentHost && (contentHost.vnodeData[0] & 32 /* VNodeDataFlag.INERT */) !== 0;
12113
+ };
12114
+ const markPromiseHandled = (promise) => {
12115
+ promise.catch(() => { });
12079
12116
  };
12080
12117
  function setParentOptions(mutable, styleScoped, parentComponentFrame) {
12081
12118
  return () => {
@@ -12088,23 +12125,43 @@ async function _walkJSX(ssr, value, options) {
12088
12125
  const stack = [value];
12089
12126
  const enqueue = (value) => stack.push(value);
12090
12127
  const enqueuePromise = (promise) => {
12091
- markPromiseHandled(ssr, promise);
12128
+ markPromiseHandled(promise);
12092
12129
  stack.push(promise);
12093
12130
  stack.push(Promise);
12094
12131
  };
12095
12132
  const drain = async () => {
12096
12133
  while (stack.length) {
12134
+ let phase = "render" /* ErrorBoundaryPhase.Render */;
12097
12135
  try {
12098
12136
  const value = stack.pop();
12099
12137
  // Reference equality first (no prototype walk), then typeof
12100
12138
  if (value === MaybeAsyncSignal) {
12101
12139
  const trackFn = stack.pop();
12140
+ if (__EXPERIMENTAL__.errorBoundary && isInsideFailedBoundaryContent(ssr)) {
12141
+ continue;
12142
+ }
12143
+ phase = "hook" /* ErrorBoundaryPhase.Hook */;
12102
12144
  await retryOnPromise(() => stack.push(trackFn()));
12103
12145
  continue;
12104
12146
  }
12147
+ if (__EXPERIMENTAL__.errorBoundary && value === InvokeJSXFunction) {
12148
+ const fnChild = stack.pop();
12149
+ if (isInsideFailedBoundaryContent(ssr)) {
12150
+ continue;
12151
+ }
12152
+ const result = fnChild.apply(ssr);
12153
+ if (isPromise(result)) {
12154
+ await result;
12155
+ }
12156
+ continue;
12157
+ }
12105
12158
  if (typeof value === 'function') {
12106
12159
  if (value === Promise) {
12107
- stack.push(await stack.pop());
12160
+ const pending = stack.pop();
12161
+ if (__EXPERIMENTAL__.errorBoundary && isInsideFailedBoundaryContent(ssr)) {
12162
+ continue;
12163
+ }
12164
+ stack.push(await pending);
12108
12165
  }
12109
12166
  else {
12110
12167
  const result = value.apply(ssr);
@@ -12114,8 +12171,17 @@ async function _walkJSX(ssr, value, options) {
12114
12171
  }
12115
12172
  continue;
12116
12173
  }
12174
+ if (__EXPERIMENTAL__.errorBoundary && isInsideFailedBoundaryContent(ssr)) {
12175
+ if (isPromise(value)) {
12176
+ value.catch(() => { });
12177
+ }
12178
+ continue;
12179
+ }
12117
12180
  processJSXNode(ssr, enqueue, enqueuePromise, value, options);
12118
12181
  }
12182
+ catch (err) {
12183
+ ssr.handleError(err, ssr.getOrCreateLastNode(), phase);
12184
+ }
12119
12185
  finally {
12120
12186
  const pendingFlush = ssr.streamHandler.waitForPendingFlush();
12121
12187
  if (isPromise(pendingFlush)) {
@@ -12126,6 +12192,12 @@ async function _walkJSX(ssr, value, options) {
12126
12192
  };
12127
12193
  await drain();
12128
12194
  }
12195
+ function enqueueJSX(enqueue, value) {
12196
+ enqueue(value);
12197
+ if (__EXPERIMENTAL__.errorBoundary && typeof value === 'function') {
12198
+ enqueue(InvokeJSXFunction);
12199
+ }
12200
+ }
12129
12201
  function processJSXNode(ssr, enqueue, enqueuePromise, value, options) {
12130
12202
  // console.log('processJSXNode', value);
12131
12203
  if (value == null) {
@@ -12143,7 +12215,7 @@ function processJSXNode(ssr, enqueue, enqueuePromise, value, options) {
12143
12215
  else if (typeof value === 'object') {
12144
12216
  if (Array.isArray(value)) {
12145
12217
  for (let i = value.length - 1; i >= 0; i--) {
12146
- enqueue(value[i]);
12218
+ enqueueJSX(enqueue, value[i]);
12147
12219
  }
12148
12220
  }
12149
12221
  else if (isSignal(value)) {
@@ -12162,19 +12234,28 @@ function processJSXNode(ssr, enqueue, enqueuePromise, value, options) {
12162
12234
  }
12163
12235
  else if (isAsyncGenerator(value)) {
12164
12236
  enqueue(async () => {
12165
- for await (const chunk of value) {
12166
- await _walkJSX(ssr, chunk, {
12167
- currentStyleScoped: options.currentStyleScoped,
12168
- parentComponentFrame: options.parentComponentFrame,
12169
- });
12170
- await ssr.streamHandler.flush();
12237
+ if (__EXPERIMENTAL__.errorBoundary && isInsideFailedBoundaryContent(ssr)) {
12238
+ return;
12239
+ }
12240
+ const freshWalkOptions = () => ({
12241
+ currentStyleScoped: options.currentStyleScoped,
12242
+ parentComponentFrame: options.parentComponentFrame,
12243
+ });
12244
+ try {
12245
+ for await (const chunk of value) {
12246
+ await _walkJSX(ssr, chunk, freshWalkOptions());
12247
+ await ssr.streamHandler.flush();
12248
+ }
12249
+ }
12250
+ catch (err) {
12251
+ ssr.handleError(err, ssr.getOrCreateLastNode(), "render" /* ErrorBoundaryPhase.Render */);
12252
+ await _walkJSX(ssr, null, freshWalkOptions());
12171
12253
  }
12172
12254
  });
12173
12255
  }
12174
12256
  else {
12175
12257
  const jsx = value;
12176
12258
  const type = jsx.type;
12177
- // Below, JSXChildren allows functions and regexes, but we assume the dev only uses those as appropriate.
12178
12259
  if (typeof type === 'string') {
12179
12260
  appendClassIfScopedStyleExists(jsx, options.currentStyleScoped);
12180
12261
  let qwikInspectorAttrValue = null;
@@ -12188,6 +12269,9 @@ function processJSXNode(ssr, enqueue, enqueuePromise, value, options) {
12188
12269
  if (innerHTML) {
12189
12270
  ssr.htmlNode(innerHTML);
12190
12271
  }
12272
+ if (__EXPERIMENTAL__.errorBoundary && directGetPropsProxyProp(jsx, QErrorContentHost)) {
12273
+ enqueue(openBoundaryContentScope(ssr, ssr.getOrCreateLastNode()));
12274
+ }
12191
12275
  enqueue(ssr.closeElement);
12192
12276
  if (type === 'head') {
12193
12277
  ssr.emitQwikLoaderAtTopIfNeeded();
@@ -12206,10 +12290,11 @@ function processJSXNode(ssr, enqueue, enqueuePromise, value, options) {
12206
12290
  }
12207
12291
  }
12208
12292
  const children = jsx.children;
12209
- children != null && enqueue(children);
12293
+ children != null && enqueueJSX(enqueue, children);
12210
12294
  }
12211
12295
  else if (isFunction(type)) {
12212
- if (__EXPERIMENTAL__.suspense && isInternalServerComponent(type)) {
12296
+ if ((__EXPERIMENTAL__.suspense || __EXPERIMENTAL__.errorBoundary) &&
12297
+ isInternalServerComponent(type)) {
12213
12298
  enqueue(() => getInternalServerComponentHandler(type)(ssr, jsx, options, enqueue));
12214
12299
  return;
12215
12300
  }
@@ -12220,9 +12305,8 @@ function processJSXNode(ssr, enqueue, enqueuePromise, value, options) {
12220
12305
  }
12221
12306
  ssr.openFragment(attrs);
12222
12307
  enqueue(ssr.closeFragment);
12223
- // In theory we could get functions or regexes, but we assume all is well
12224
12308
  const children = jsx.children;
12225
- children != null && enqueue(children);
12309
+ children != null && enqueueJSX(enqueue, children);
12226
12310
  }
12227
12311
  else if (type === Slot) {
12228
12312
  const componentFrame = options.parentComponentFrame;
@@ -12244,7 +12328,7 @@ function processJSXNode(ssr, enqueue, enqueuePromise, value, options) {
12244
12328
  if (slotDefaultChildren && slotChildren !== slotDefaultChildren) {
12245
12329
  ssr.addUnclaimedProjection(componentFrame, QDefaultSlot, slotDefaultChildren);
12246
12330
  }
12247
- enqueue(slotChildren);
12331
+ enqueueJSX(enqueue, slotChildren);
12248
12332
  enqueue(setParentOptions(options, componentFrame.projectionScopedStyle, componentFrame.projectionComponentFrame));
12249
12333
  }
12250
12334
  else {
@@ -12291,7 +12375,7 @@ function processJSXNode(ssr, enqueue, enqueuePromise, value, options) {
12291
12375
  else if (type === SSRStreamBlock) {
12292
12376
  ssr.streamHandler.streamBlockStart();
12293
12377
  enqueue(() => ssr.streamHandler.streamBlockEnd());
12294
- enqueue(jsx.children);
12378
+ enqueueJSX(enqueue, jsx.children);
12295
12379
  }
12296
12380
  else if (isQwikComponent(type)) {
12297
12381
  // prod: use new instance of an object for props, we always modify props for a component
@@ -12301,13 +12385,13 @@ function processJSXNode(ssr, enqueue, enqueuePromise, value, options) {
12301
12385
  }
12302
12386
  ssr.openComponent(componentAttrs);
12303
12387
  const host = ssr.getOrCreateLastNode();
12388
+ enqueue(setParentOptions(options, options.currentStyleScoped, options.parentComponentFrame));
12389
+ enqueue(() => ssr.closeComponent());
12304
12390
  const componentFrame = ssr.getParentComponentFrame();
12305
12391
  componentFrame.distributeChildrenIntoSlots(jsx.children, options.currentStyleScoped, options.parentComponentFrame);
12306
12392
  const jsxOutput = applyQwikComponentBody(ssr, jsx, type);
12307
- enqueue(setParentOptions(options, options.currentStyleScoped, options.parentComponentFrame));
12308
- enqueue(() => ssr.closeComponent());
12309
12393
  if (isPromise(jsxOutput)) {
12310
- markPromiseHandled(ssr, jsxOutput, host);
12394
+ markPromiseHandled(jsxOutput);
12311
12395
  // Defer reading QScopedStyle until after the promise resolves
12312
12396
  enqueue(async () => {
12313
12397
  await ssr.streamHandler.flush();
@@ -12359,6 +12443,99 @@ function appendClassIfScopedStyleExists(jsx, styleScoped) {
12359
12443
  }
12360
12444
  }
12361
12445
 
12446
+ /** @internal */
12447
+ function handleSSRError(container, err, host, phase) {
12448
+ if (!__EXPERIMENTAL__.errorBoundary || (qDev && !isRecoverable(err))) {
12449
+ throw err;
12450
+ }
12451
+ for (let boundaryNode = host; boundaryNode; boundaryNode = boundaryNode.parentComponent) {
12452
+ const errorStore = getOwnSSRErrorBoundaryStore(boundaryNode);
12453
+ if (!errorStore || !errorStore.$fallback$) {
12454
+ continue;
12455
+ }
12456
+ if (container.$isOutOfOrderSegment$ && errorStore.$emitFallback$) {
12457
+ throw err;
12458
+ }
12459
+ markBoundaryErrored(errorStore, err, phase);
12460
+ getRootContainer(container).$hasBoundaryError$ = true;
12461
+ if (container.$isOutOfOrderSegment$) {
12462
+ markErrorFromDeferredSegment(errorStore);
12463
+ }
12464
+ markErrorBoundaryContentInert(container, boundaryNode, errorStore);
12465
+ return;
12466
+ }
12467
+ throw err;
12468
+ }
12469
+ function markErrorBoundaryContentInert(container, boundaryNode, errorStore) {
12470
+ const ancestorOwners = new Map();
12471
+ let boundaryContentOwner = null;
12472
+ let depth = 0;
12473
+ for (let node = boundaryNode; node; node = node.parentComponent) {
12474
+ if (node.id) {
12475
+ ancestorOwners.set(node.id, { node, depth: depth++ });
12476
+ }
12477
+ if (node !== boundaryNode && !boundaryContentOwner) {
12478
+ const store = getOwnSSRErrorBoundaryStore(node);
12479
+ if (store?.error !== undefined || !hasSlotProps(node.getProp(ELEMENT_PROPS))) {
12480
+ boundaryContentOwner = node;
12481
+ }
12482
+ }
12483
+ }
12484
+ const topmostSevered = { node: null, depth: -1 };
12485
+ const children = boundaryNode.children;
12486
+ if (children) {
12487
+ for (let i = 0; i < children.length; i++) {
12488
+ markErrorSubtreeInert(container, children[i], ancestorOwners, topmostSevered);
12489
+ }
12490
+ }
12491
+ if (topmostSevered.node) {
12492
+ const immediateContentOwner = topmostSevered.node.parentComponent;
12493
+ if (topmostSevered.node === boundaryNode) {
12494
+ container.$retainForResume$(boundaryContentOwner);
12495
+ }
12496
+ else if (immediateContentOwner?.id) {
12497
+ container.$retainForResume$(immediateContentOwner);
12498
+ errorStore.projectedContentOwner = immediateContentOwner;
12499
+ }
12500
+ }
12501
+ }
12502
+ function getOwnSSRErrorBoundaryStore(node) {
12503
+ const ctx = node.getProp(QCtxAttr);
12504
+ return ctx ? mapArray_get(ctx, ERROR_CONTEXT.id, 0) : null;
12505
+ }
12506
+ function markErrorSubtreeInert(container, node, ancestorOwners, topmostSevered) {
12507
+ node.vnodeData[0] |= 32 /* VNodeDataFlag.INERT */;
12508
+ const ownerId = node.getProp(QSlotParent);
12509
+ if (ownerId) {
12510
+ const owner = ancestorOwners.get(ownerId);
12511
+ if (owner) {
12512
+ owner.node.removeProp(node.getProp(QSlot) ?? QDefaultSlot);
12513
+ if (owner.depth > topmostSevered.depth) {
12514
+ topmostSevered.node = owner.node;
12515
+ topmostSevered.depth = owner.depth;
12516
+ }
12517
+ }
12518
+ }
12519
+ // Element consumers keep materializing when inert, so their bindings would keep patching hidden
12520
+ // DOM; virtual ones can't materialize and would leave producers pointing at a dead node.
12521
+ clearAllEffects(container, node);
12522
+ const seq = node.getProp(ELEMENT_SEQ);
12523
+ if (seq) {
12524
+ for (let i = 0; i < seq.length; i++) {
12525
+ const item = seq[i];
12526
+ if (isTask(item)) {
12527
+ clearAllEffects(container, item);
12528
+ }
12529
+ }
12530
+ }
12531
+ const children = node.children;
12532
+ if (children) {
12533
+ for (let i = 0; i < children.length; i++) {
12534
+ markErrorSubtreeInert(container, children[i], ancestorOwners, topmostSevered);
12535
+ }
12536
+ }
12537
+ }
12538
+
12362
12539
  // <docs markdown="../readme.md#useLexicalScope">
12363
12540
  // !!DO NOT EDIT THIS COMMENT DIRECTLY!!!
12364
12541
  // (edit ../readme.md#useLexicalScope instead and run `pnpm docs.sync`)
@@ -13836,14 +14013,179 @@ const _resolveContextWithoutSequentialScope = (context) => {
13836
14013
  };
13837
14014
 
13838
14015
  const ERROR_CONTEXT = /*#__PURE__*/ createContextId('qk-error');
13839
- const isRecoverable = (err) => {
13840
- if (err && err instanceof Error) {
13841
- if ('plugin' in err) {
13842
- return false;
14016
+ const ERROR_BOUNDARY_QRL_SYMBOL = '_ebC';
14017
+ const safeRead = (read, fallback) => {
14018
+ try {
14019
+ return read();
14020
+ }
14021
+ catch {
14022
+ return fallback;
14023
+ }
14024
+ };
14025
+ const isRecoverable = (err) => safeRead(() => !(err instanceof Error && 'plugin' in err), true);
14026
+ const GENERIC_BOUNDARY_ERROR_MESSAGE = 'An error occurred';
14027
+ const errorBoundaryDigest = (err) => hashCode(safeRead(() => err instanceof Error ? `${err.name}: ${err.message}\n${err.stack ?? ''}` : String(err), 'unknown'));
14028
+ const REDACTED = /*#__PURE__*/ Symbol();
14029
+ const redactToGeneric = (err) => {
14030
+ const redacted = new Error(GENERIC_BOUNDARY_ERROR_MESSAGE);
14031
+ redacted.digest = errorBoundaryDigest(err);
14032
+ Object.defineProperty(redacted, REDACTED, { value: true });
14033
+ return redacted;
14034
+ };
14035
+ const toBoundaryError = (raw) => {
14036
+ if (raw instanceof Error) {
14037
+ return raw;
14038
+ }
14039
+ let message;
14040
+ try {
14041
+ message = String(raw);
14042
+ }
14043
+ catch {
14044
+ try {
14045
+ message = Object.prototype.toString.call(raw);
14046
+ }
14047
+ catch {
14048
+ message = GENERIC_BOUNDARY_ERROR_MESSAGE;
13843
14049
  }
13844
14050
  }
14051
+ const wrapped = new Error(message);
14052
+ wrapped.cause = raw;
14053
+ return wrapped;
14054
+ };
14055
+ const isReadableProjection = (projected) => safeRead(() => {
14056
+ void projected.message;
14057
+ void projected.name;
14058
+ for (const key in projected) {
14059
+ void projected[key];
14060
+ }
13845
14061
  return true;
14062
+ }, false);
14063
+ const redactBoundaryErrorForDisplay = (error, showRaw, transformError) => {
14064
+ try {
14065
+ if (transformError) {
14066
+ const projected = transformError(error);
14067
+ if (projected instanceof Error) {
14068
+ return isReadableProjection(projected) ? projected : redactToGeneric(error);
14069
+ }
14070
+ if (projected != null) {
14071
+ if (showRaw && isPromise(projected)) {
14072
+ logWarn('transformError must return synchronously; an async one redacts every error.');
14073
+ }
14074
+ return redactToGeneric(error);
14075
+ }
14076
+ }
14077
+ if (safeRead(() => error instanceof Error && REDACTED in error, false)) {
14078
+ return error;
14079
+ }
14080
+ if (!showRaw) {
14081
+ return redactToGeneric(error);
14082
+ }
14083
+ if (error instanceof Error) {
14084
+ return error;
14085
+ }
14086
+ const rawMessage = error?.message;
14087
+ if (typeof rawMessage === 'string') {
14088
+ const wrapped = new Error(rawMessage);
14089
+ wrapped.cause = error;
14090
+ return wrapped;
14091
+ }
14092
+ return toBoundaryError(error);
14093
+ }
14094
+ catch {
14095
+ return redactToGeneric(error);
14096
+ }
14097
+ };
14098
+ const fireOnError = (onError, error, phase, boundaryId) => {
14099
+ if (!onError) {
14100
+ return;
14101
+ }
14102
+ try {
14103
+ const digest = errorBoundaryDigest(error);
14104
+ void Promise.resolve(onError(toBoundaryError(error), { phase, boundaryId, digest })).catch(logError);
14105
+ }
14106
+ catch (e) {
14107
+ logError(e);
14108
+ }
14109
+ };
14110
+ const boundariesWithDeferredError = /*#__PURE__*/ new WeakSet();
14111
+ const markErrorFromDeferredSegment = (store) => {
14112
+ boundariesWithDeferredError.add(store);
14113
+ };
14114
+ const isErrorFromDeferredSegment = (store) => boundariesWithDeferredError.has(store);
14115
+ const ERROR_PHASE = /*#__PURE__*/ Symbol('qErrorPhase');
14116
+ const tagErrorPhase = (err, phase) => {
14117
+ try {
14118
+ Object.defineProperty(err, ERROR_PHASE, { value: phase, configurable: true });
14119
+ }
14120
+ catch {
14121
+ // ignore
14122
+ }
14123
+ };
14124
+ const getTaggedErrorPhase = (err) => safeRead(() => err?.[ERROR_PHASE], undefined);
14125
+ const markBoundaryErrored = (store, error, phase = "render" /* ErrorBoundaryPhase.Render */) => {
14126
+ // `null` would collide with the capture-only sentinel, so wrap every nullish throw.
14127
+ store.error = error == null ? toBoundaryError(error) : error;
14128
+ const onError = store.$onError$;
14129
+ if (onError) {
14130
+ fireOnError(onError, error, getTaggedErrorPhase(error) ?? phase, store.boundaryId ?? '');
14131
+ }
14132
+ };
14133
+ const handleQError = (e) => {
14134
+ const detail = e
14135
+ .detail;
14136
+ if (detail?.importError) {
14137
+ return;
14138
+ }
14139
+ const source = detail.element;
14140
+ // The listener is document-wide, so only this document's elements may route through it.
14141
+ if (!source || source.ownerDocument !== e.currentTarget) {
14142
+ return;
14143
+ }
14144
+ // A destroyed container clears this back-pointer, so a stale target resolves to nothing.
14145
+ const container = source.closest(QContainerSelector)?.qContainer;
14146
+ const host = container?.vNodeLocate(source);
14147
+ if (!container || !host) {
14148
+ return;
14149
+ }
14150
+ try {
14151
+ container.handleError(detail.error, host, "event" /* ErrorBoundaryPhase.Event */);
14152
+ }
14153
+ catch (handlerError) {
14154
+ logError(handlerError);
14155
+ }
13846
14156
  };
14157
+ /**
14158
+ * One listener per document, routed by target, so no container is retained by the document. The
14159
+ * marker lives on the document so a second bundle on the page reuses it instead of
14160
+ * double-handling.
14161
+ */
14162
+ function installQErrorListener(doc) {
14163
+ if (!doc.qErrorHandler) {
14164
+ doc.qErrorHandler = handleQError;
14165
+ doc.addEventListener?.('qerror', handleQError);
14166
+ }
14167
+ }
14168
+ function handleDevError(container, err, host) {
14169
+ if (typeof document !== 'undefined') {
14170
+ setErrorPayload(host, err);
14171
+ markVNodeDirty(container, host, 512 /* ChoreBits.ERROR_WRAP */);
14172
+ }
14173
+ try {
14174
+ if (err instanceof Error && !('hostElement' in err)) {
14175
+ err['hostElement'] = String(host);
14176
+ }
14177
+ }
14178
+ catch {
14179
+ // ignore
14180
+ }
14181
+ if (!isRecoverable(err)) {
14182
+ throw err;
14183
+ }
14184
+ }
14185
+ function getOwnErrorBoundaryStore(container, host) {
14186
+ const ctx = container.getHostProp(host, QCtxAttr);
14187
+ return ctx ? mapArray_get(ctx, ERROR_CONTEXT.id, 0) : null;
14188
+ }
13847
14189
 
13848
14190
  /** @internal */
13849
14191
  const canRevealRegistration = (registration, isPending = (item) => item.boundary.untrackedValue > 0) => {
@@ -14721,6 +15063,9 @@ class DomContainer extends _SharedContainer {
14721
15063
  this.$setServerData$();
14722
15064
  element.qContainer = this;
14723
15065
  element.qDestroy = () => this.$destroy$();
15066
+ if (__EXPERIMENTAL__.errorBoundary) {
15067
+ installQErrorListener(this.document);
15068
+ }
14724
15069
  this.$containerDataProcessState$ = 1 /* ContainerDataProcessState.ProcessingVNode */;
14725
15070
  processVNodeData(document, element);
14726
15071
  onVNodeDataReady(document, () => {
@@ -14816,29 +15161,49 @@ class DomContainer extends _SharedContainer {
14816
15161
  this.$stateData$[id] = vParent;
14817
15162
  }
14818
15163
  parseQRL(qrlStr) {
14819
- const qrl = parseQRL(qrlStr, this);
14820
- return qrl;
15164
+ return parseQRL(qrlStr, this);
14821
15165
  }
14822
- handleError(err, host) {
15166
+ handleError(err, host, phase = "render" /* ErrorBoundaryPhase.Render */) {
14823
15167
  if (qDev && host) {
14824
- if (typeof document !== 'undefined') {
14825
- setErrorPayload(host, err);
14826
- markVNodeDirty(this, host, 512 /* ChoreBits.ERROR_WRAP */);
15168
+ handleDevError(this, err, host);
15169
+ }
15170
+ if (!__EXPERIMENTAL__.errorBoundary) {
15171
+ const errorStore = host && this.resolveContext(host, ERROR_CONTEXT);
15172
+ if (!errorStore) {
15173
+ logErrorAndThrowAsync(err);
15174
+ return;
14827
15175
  }
14828
- if (err && err instanceof Error) {
14829
- if (!('hostElement' in err)) {
14830
- err['hostElement'] = String(host);
14831
- }
15176
+ errorStore.error = err;
15177
+ return;
15178
+ }
15179
+ // `null` would collide with the capture-only sentinel, so wrap every nullish throw.
15180
+ const storedError = err == null ? toBoundaryError(err) : err;
15181
+ let current = host;
15182
+ while (current) {
15183
+ const boundaryHost = this.resolveContextHost(current, ERROR_CONTEXT);
15184
+ if (!boundaryHost) {
15185
+ break;
14832
15186
  }
14833
- if (!isRecoverable(err)) {
14834
- throw err;
15187
+ const store = getOwnErrorBoundaryStore(this, boundaryHost);
15188
+ if (store && store.error === undefined) {
15189
+ store.error = storedError;
15190
+ const boundaryProps = this.getHostProp(boundaryHost, ELEMENT_PROPS);
15191
+ fireOnError(boundaryProps?.onError$, err, phase, store.boundaryId ?? '');
15192
+ markVNodeDirty(this, boundaryHost, 4 /* ChoreBits.COMPONENT */);
15193
+ return;
14835
15194
  }
15195
+ // Capture-only sentinel for the ErrorProvider test spy; folds away in prod builds.
15196
+ if (qTest && store && store.error === null) {
15197
+ store.error = storedError;
15198
+ return;
15199
+ }
15200
+ if (boundaryHost.dirty & 4 /* ChoreBits.COMPONENT */) {
15201
+ logError(err);
15202
+ return;
15203
+ }
15204
+ current = this.getParentHost(boundaryHost);
14836
15205
  }
14837
- const errorStore = host && this.resolveContext(host, ERROR_CONTEXT);
14838
- if (!errorStore) {
14839
- throw err;
14840
- }
14841
- errorStore.error = err;
15206
+ logErrorAndThrowAsync(err);
14842
15207
  }
14843
15208
  setContext(host, context, value) {
14844
15209
  let ctx = this.getHostProp(host, QCtxAttr);
@@ -14848,14 +15213,20 @@ class DomContainer extends _SharedContainer {
14848
15213
  mapArray_set(ctx, context.id, value, 0, true);
14849
15214
  }
14850
15215
  resolveContext(host, contextId) {
15216
+ const found = this.resolveContextHost(host, contextId);
15217
+ return found
15218
+ ? mapArray_get(this.getHostProp(found, QCtxAttr), contextId.id, 0)
15219
+ : undefined;
15220
+ }
15221
+ resolveContextHost(host, contextId) {
14851
15222
  while (host) {
14852
15223
  const ctx = this.getHostProp(host, QCtxAttr);
14853
15224
  if (ctx != null && mapArray_has(ctx, contextId.id, 0)) {
14854
- return mapArray_get(ctx, contextId.id, 0);
15225
+ return host;
14855
15226
  }
14856
15227
  host = this.getParentHost(host);
14857
15228
  }
14858
- return undefined;
15229
+ return null;
14859
15230
  }
14860
15231
  getParentHost(host) {
14861
15232
  let vNode = host.parent;
@@ -16867,13 +17238,217 @@ const useTask$ = /*#__PURE__*/ implicit$FirstArg(useTaskQrl);
16867
17238
  // We need to cast to help out the api extractor
16868
17239
  const useVisibleTask$ = /*#__PURE__*/ implicit$FirstArg(useVisibleTaskQrl);
16869
17240
 
16870
- /** @public */
16871
- const useErrorBoundary = () => {
16872
- const error = useStore({ error: undefined });
17241
+ const createErrorBoundaryStore = () => Object.defineProperty({ error: undefined }, 'error', { enumerable: false });
17242
+ /** @internal */
17243
+ const useErrorBoundaryStore = () => {
17244
+ const error = useStore(createErrorBoundaryStore);
16873
17245
  useContextProvider(ERROR_CONTEXT, error);
16874
17246
  return error;
16875
17247
  };
16876
17248
 
17249
+ /** Finalizes a deferred segment and emits its swap scripts; shared by Suspense and ErrorBoundary. */
17250
+ async function finalizeAndSwapOutOfOrderSegment(ssr, boundaryId, rendered, revealBoundary) {
17251
+ const segmentId = String(boundaryId);
17252
+ const result = await rendered.container.$finalizeOutOfOrderSegment$(segmentId, rendered);
17253
+ ssr.write(`<template ${QSuspenseResolved}="${boundaryId}"${revealBoundary?.attrs ?? ''}>`);
17254
+ ssr.write(result.html);
17255
+ ssr.write('</template>');
17256
+ ssr.emitOutOfOrderSegmentScripts(result.scripts);
17257
+ ssr.emitOutOfOrderExecutorIfNeeded();
17258
+ ssr.emitInlineScript(`qO(${boundaryId})`);
17259
+ const errorSwapIds = rendered.container.$errorSwapIds$;
17260
+ if (__EXPERIMENTAL__.errorBoundary && errorSwapIds?.length) {
17261
+ ssr.emitErrorSwapExecutorIfNeeded();
17262
+ for (let i = 0; i < errorSwapIds.length; i++) {
17263
+ ssr.emitInlineScript(`qErr(${errorSwapIds[i]})`);
17264
+ }
17265
+ }
17266
+ // qO() is the browser-visible handoff for this segment, so flush it immediately.
17267
+ await ssr.streamHandler.flush();
17268
+ }
17269
+
17270
+ /** @internal */
17271
+ const errorBoundaryReset = () => {
17272
+ const [host] = _captures;
17273
+ // this is executed only on client
17274
+ const container = tryGetInvokeContext()?.$container$;
17275
+ const isBrowserEnv = qTest ? !isServerPlatform() : isBrowser$1;
17276
+ if (isBrowserEnv && host && container) {
17277
+ resetErrorBoundary(container, host);
17278
+ }
17279
+ };
17280
+ const RESET_KEY_SUFFIX = '\0';
17281
+ /** @internal */
17282
+ function resetErrorBoundary(container, host) {
17283
+ const boundaryHost = container.resolveContextHost(host, ERROR_CONTEXT);
17284
+ if (!boundaryHost) {
17285
+ return;
17286
+ }
17287
+ const store = getOwnErrorBoundaryStore(container, boundaryHost);
17288
+ if (!store) {
17289
+ return;
17290
+ }
17291
+ scheduleBoundaryContentReset(container, boundaryHost, store.projectedContentOwner ?? null);
17292
+ store.error = undefined;
17293
+ }
17294
+ function scheduleBoundaryContentReset(container, boundaryHost, recordedContentOwner) {
17295
+ let resetHost = boundaryHost;
17296
+ let contentOwner = null;
17297
+ let vNode = vnode_getProjectionParentOrParent(boundaryHost);
17298
+ let crossedProjection = false;
17299
+ while (vNode) {
17300
+ if (vnode_isVirtualVNode(vNode)) {
17301
+ if (vnode_getProp(vNode, QSlot, null) !== null) {
17302
+ crossedProjection = true;
17303
+ }
17304
+ else if (vnode_getProp(vNode, OnRenderProp, null) !== null) {
17305
+ if (!crossedProjection) {
17306
+ const ownerBoundary = getOwnErrorBoundaryStore(container, vNode);
17307
+ if (!ownerBoundary || ownerBoundary.error !== undefined) {
17308
+ if (contentOwner) {
17309
+ resetHost = contentOwner;
17310
+ }
17311
+ contentOwner = vNode;
17312
+ if (ownerBoundary?.error !== undefined || !hasSlotProps(vNode.props)) {
17313
+ break;
17314
+ }
17315
+ }
17316
+ }
17317
+ crossedProjection = false;
17318
+ }
17319
+ }
17320
+ vNode = vnode_getProjectionParentOrParent(vNode);
17321
+ }
17322
+ if (recordedContentOwner && recordedContentOwner !== contentOwner) {
17323
+ resetHost = contentOwner ?? resetHost;
17324
+ contentOwner = recordedContentOwner;
17325
+ }
17326
+ if (contentOwner) {
17327
+ // Force keyed diff to recreate emptied projected content.
17328
+ resetHost.key = `${resetHost.key ?? ''}${RESET_KEY_SUFFIX}`;
17329
+ markVNodeDirty(container, contentOwner, 4 /* ChoreBits.COMPONENT */);
17330
+ }
17331
+ else {
17332
+ markVNodeDirty(container, boundaryHost, 4 /* ChoreBits.COMPONENT */);
17333
+ }
17334
+ }
17335
+ const renderFallbackOrLastResort = (fallbackQrl, error, reset) => {
17336
+ return fallbackQrl(error, reset).catch((err) => {
17337
+ if (fallbackQrl.resolved !== undefined) {
17338
+ throw err;
17339
+ }
17340
+ return /*#__PURE__*/ _jsxSorted('div', { role: 'alert' }, null, 'Something went wrong.', 0, null);
17341
+ });
17342
+ };
17343
+ const buildSSRErrorBoundaryHosts = (store, container) => {
17344
+ const boundaryId = container?.nextOutOfOrderId(false) ?? 0;
17345
+ return [
17346
+ /*#__PURE__*/ _jsxSorted('div', {
17347
+ [QErrorContentHost]: String(boundaryId),
17348
+ style: 'display:contents',
17349
+ }, null,
17350
+ /*#__PURE__*/ _jsxSorted(Slot, null, null, null, 0, null), 1, null),
17351
+ /*#__PURE__*/ _jsxSorted(SSRErrorFallbackHost, {
17352
+ boundaryId,
17353
+ store,
17354
+ }, null, null, 1, null),
17355
+ ];
17356
+ };
17357
+ /** @internal */
17358
+ const errorBoundaryCmp = (props) => {
17359
+ if (!__EXPERIMENTAL__.errorBoundary) {
17360
+ throw new Error('<ErrorBoundary> requires the `errorBoundary` experimental feature. Enable it in your Qwik Vite config: qwikVite({ experimental: ["errorBoundary"] }).');
17361
+ }
17362
+ const store = useErrorBoundaryStore();
17363
+ const invokeCtx = tryGetInvokeContext();
17364
+ const container = invokeCtx?.$container$;
17365
+ if (container && (getStoreTarget(store) ?? store).boundaryId === undefined) {
17366
+ store.boundaryId = getNextUniqueIndex(container);
17367
+ }
17368
+ const isServerEnv = qTest ? isServerPlatform() : !isBrowser$1;
17369
+ if (!isServerEnv && store.error === undefined) {
17370
+ return /*#__PURE__*/ _jsxSorted(Slot, null, null, null, 0, null);
17371
+ }
17372
+ const reset = /*#__PURE__*/ inlinedQrl(errorBoundaryReset, '_ebR', [invokeCtx?.$hostElement$]);
17373
+ if (isServerEnv) {
17374
+ const fallbackQrl = props.fallback$;
17375
+ store.$fallback$ = noSerialize((error) => fallbackQrl(error, reset));
17376
+ const onErrorQrl = props.onError$;
17377
+ if (onErrorQrl) {
17378
+ store.$onError$ = noSerialize((error, info) => onErrorQrl(error, info));
17379
+ }
17380
+ return buildSSRErrorBoundaryHosts(store, container);
17381
+ }
17382
+ const displayError = redactBoundaryErrorForDisplay(store.error, true);
17383
+ return /*#__PURE__*/ _jsxSorted(Fragment, null, null, renderFallbackOrLastResort(props.fallback$, displayError, reset), 0, null);
17384
+ };
17385
+ /** Renders `fallback$` instead of its children when a descendant throws. @public @experimental */
17386
+ const ErrorBoundary =
17387
+ /*#__PURE__*/ componentQrl(
17388
+ /*#__PURE__*/ inlinedQrl(errorBoundaryCmp, ERROR_BOUNDARY_QRL_SYMBOL));
17389
+ const consumeSSRErrorFallback = (ssr, store) => {
17390
+ const fallback = store.$fallback$;
17391
+ delete store.$fallback$;
17392
+ const projected = redactBoundaryErrorForDisplay(store.error, isDev, ssr.$transformError$);
17393
+ return fallback(projected);
17394
+ };
17395
+ const SSRErrorFallbackRenderer = __EXPERIMENTAL__.errorBoundary
17396
+ ? /*#__PURE__*/ createInternalServerComponent((ssr, jsx, options, enqueue) => {
17397
+ const boundaryId = jsx.varProps.boundaryId;
17398
+ const store = jsx.varProps.store;
17399
+ if (jsx.varProps.deliverLate) {
17400
+ const streamFallback = async (error) => {
17401
+ if (!store.$fallback$) {
17402
+ return;
17403
+ }
17404
+ // The catch site already recorded it; marking again refires onError$.
17405
+ if (store.error !== error) {
17406
+ markBoundaryErrored(store, error, "render" /* ErrorBoundaryPhase.Render */);
17407
+ }
17408
+ const segment = await ssr.segment(`${boundaryId}`, consumeSSRErrorFallback(ssr, store), options);
17409
+ // qErr hides the errored content host and strips its broadcast handlers.
17410
+ segment.container.$registerErrorSwap$(boundaryId);
17411
+ await ssr.$runQueuedRender$(() => finalizeAndSwapOutOfOrderSegment(ssr, boundaryId, segment, null));
17412
+ };
17413
+ store.$emitFallback$ = noSerialize(streamFallback);
17414
+ ssr.write(`<template ${QSuspenseResolved}="${boundaryId}"></template>`);
17415
+ if (store.error !== undefined) {
17416
+ return streamFallback(store.error);
17417
+ }
17418
+ return;
17419
+ }
17420
+ if (store.error === undefined || !store.$fallback$) {
17421
+ return;
17422
+ }
17423
+ if (isOutOfOrderSegmentContainer(ssr)) {
17424
+ ssr.$registerErrorSwap$(boundaryId);
17425
+ }
17426
+ else {
17427
+ enqueue(() => {
17428
+ ssr.emitErrorSwapExecutorIfNeeded();
17429
+ ssr.emitInlineScript(`qErr(${boundaryId})`);
17430
+ });
17431
+ }
17432
+ enqueue(consumeSSRErrorFallback(ssr, store));
17433
+ })
17434
+ : null;
17435
+ const SSRErrorFallbackHost = __EXPERIMENTAL__.errorBoundary
17436
+ ? /*#__PURE__*/ createInternalServerComponent((ssr, jsx, _options, enqueue) => {
17437
+ const boundaryId = jsx.varProps.boundaryId;
17438
+ const store = jsx.varProps.store;
17439
+ const deliverLate = __EXPERIMENTAL__.suspense &&
17440
+ ssr.outOfOrderStreaming &&
17441
+ !isOutOfOrderSegmentContainer(ssr) &&
17442
+ (store.error === undefined || isErrorFromDeferredSegment(store));
17443
+ enqueue(
17444
+ /*#__PURE__*/ _jsxSorted('div', {
17445
+ [deliverLate ? QSuspenseResultParent : QErrorFallbackHost]: String(boundaryId),
17446
+ style: 'display:none',
17447
+ }, null,
17448
+ /*#__PURE__*/ _jsxSorted(SSRErrorFallbackRenderer, { boundaryId, store, deliverLate }, null, null, 1, null), 1, null));
17449
+ })
17450
+ : null;
17451
+
16877
17452
  /** @internal */
16878
17453
  const eachCmpTask = async ({ track }) => {
16879
17454
  const props = _captures[0];
@@ -17164,10 +17739,23 @@ const SSRDeferredSlot = __EXPERIMENTAL__.suspense
17164
17739
  const boundaryState = jsx.varProps.boundary;
17165
17740
  const contentStyle = jsx.varProps.contentStyle;
17166
17741
  const revealBoundary = jsx.varProps.reveal;
17742
+ const errorBoundaryStore = __EXPERIMENTAL__.errorBoundary && options.parentComponentFrame
17743
+ ? ssr.resolveContext(options.parentComponentFrame.componentNode, ERROR_CONTEXT)
17744
+ : undefined;
17167
17745
  const content = ssr.segment(contentSegment, createClaimedDeferredSlot(ssr, jsx, options), options);
17168
17746
  ssr.write(`<template ${QSuspenseResolved}="${boundaryId}"></template>`);
17169
17747
  ssr.emitOutOfOrderExecutorIfNeeded();
17170
- ssr.queueOutOfOrderSegment(content.then((rendered) => emitRenderedOutOfOrderSegment(ssr, boundaryId, contentSegment, rendered, contentStyle, revealBoundary, boundaryState)));
17748
+ ssr.queueOutOfOrderSegment(content
17749
+ .then((rendered) => emitRenderedOutOfOrderSegment(ssr, boundaryId, rendered, contentStyle, revealBoundary, boundaryState))
17750
+ .catch((error) => {
17751
+ if (errorBoundaryStore?.$emitFallback$) {
17752
+ return errorBoundaryStore.$emitFallback$(error);
17753
+ }
17754
+ if (errorBoundaryStore?.error !== undefined) {
17755
+ return;
17756
+ }
17757
+ throw error;
17758
+ }));
17171
17759
  })
17172
17760
  : null;
17173
17761
  function createClaimedDeferredSlot(ssr, jsx, options) {
@@ -17183,7 +17771,7 @@ function createClaimedDeferredSlot(ssr, jsx, options) {
17183
17771
  }
17184
17772
  return /*#__PURE__*/ _jsxSorted(Slot, jsx.varProps, jsx.constProps, slotChildren, jsx.flags, jsx.key);
17185
17773
  }
17186
- async function emitRenderedOutOfOrderSegment(ssr, boundaryId, segmentId, rendered, contentStyle, revealBoundary, boundaryState) {
17774
+ async function emitRenderedOutOfOrderSegment(ssr, boundaryId, rendered, contentStyle, revealBoundary, boundaryState) {
17187
17775
  if (boundaryState) {
17188
17776
  boundaryState.contentResolved = true;
17189
17777
  if (boundaryState.delayTimer) {
@@ -17194,14 +17782,7 @@ async function emitRenderedOutOfOrderSegment(ssr, boundaryId, segmentId, rendere
17194
17782
  revealBoundary?.resolve();
17195
17783
  await ssr.$runQueuedRender$(async () => {
17196
17784
  ssr.addRoot(contentStyle);
17197
- const result = await rendered.container.$finalizeOutOfOrderSegment$(segmentId, rendered);
17198
- ssr.write(`<template ${QSuspenseResolved}="${boundaryId}"${revealBoundary?.attrs ?? ''}>`);
17199
- ssr.write(result.html);
17200
- ssr.write('</template>');
17201
- ssr.emitOutOfOrderSegmentScripts(result.scripts);
17202
- ssr.emitInlineScript(`qO(${boundaryId})`);
17203
- // qO() is the browser-visible handoff for this segment, so flush it immediately.
17204
- await ssr.streamHandler.flush();
17785
+ await finalizeAndSwapOutOfOrderSegment(ssr, boundaryId, rendered, revealBoundary);
17205
17786
  });
17206
17787
  }
17207
17788
  function scheduleOutOfOrderFallbackDelay(ssr, boundaryState, state) {
@@ -17316,5 +17897,5 @@ if (import.meta.hot) {
17316
17897
  });
17317
17898
  }
17318
17899
 
17319
- export { $, Each, Fragment, NoSerializeSymbol, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, Reveal, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SerializerSymbol, Show, SkipRender, Slot, Suspense, _CONST_PROPS, DomContainer as _DomContainer, _EFFECT_BACK_REF, ELEMENT_SEQ as _ELEMENT_SEQ, EMPTY_ARRAY as _EMPTY_ARRAY, EMPTY_OBJ as _EMPTY_OBJ, _IMMUTABLE, _SharedContainer, SubscriptionData as _SubscriptionData, SubscriptionPatch as _SubscriptionPatch, Task as _Task, _UNINITIALIZED, _VAR_PROPS, _addProjection, _captures, _chk, _createDeserializeContainer, createQRL as _createQRL, createStore as _createStore, delay as _delay, _deserialize, _dumpState, eachCmp as _eaC, eachCmpTask as _eaT, _executeSsrChores, _fnSignal, getAsyncLocalStorage as _getAsyncLocalStorage, _getConstProps, _getContextContainer, _getContextEvent, _getContextHostElement, getDomContainer as _getDomContainer, _getQContainerElement, getSubscriber as _getSubscriber, _getVarProps, _hasStoreEffects, _hmr, _injectAsyncSignalValue, invoke as _invoke, 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, _markSignalAsExternallyOwned, newInvokeContext as _newInvokeContext, _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, retryOnPromise as _retryOnPromise, _rsc, _run, _serialize, setEvent as _setEvent, _setProjectionTarget, showCmp as _shC, showCmpTask as _shT, 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, 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, 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 };
17900
+ export { $, Each, ErrorBoundary, Fragment, NoSerializeSymbol, PrefetchGraph, PrefetchServiceWorker, RenderOnce, Resource, Reveal, SSRComment, SSRRaw, SSRStream, SSRStreamBlock, SerializerSymbol, Show, SkipRender, Slot, Suspense, _CONST_PROPS, DomContainer as _DomContainer, _EFFECT_BACK_REF, ELEMENT_SEQ as _ELEMENT_SEQ, EMPTY_ARRAY as _EMPTY_ARRAY, EMPTY_OBJ as _EMPTY_OBJ, _IMMUTABLE, _SharedContainer, SubscriptionData as _SubscriptionData, SubscriptionPatch as _SubscriptionPatch, Task as _Task, _UNINITIALIZED, _VAR_PROPS, _addProjection, _captures, _chk, _createDeserializeContainer, createQRL as _createQRL, createStore as _createStore, delay as _delay, _deserialize, _dumpState, eachCmp as _eaC, eachCmpTask as _eaT, errorBoundaryCmp as _ebC, errorBoundaryReset as _ebR, _executeSsrChores, _fnSignal, getAsyncLocalStorage as _getAsyncLocalStorage, _getConstProps, _getContextContainer, _getContextEvent, _getContextHostElement, getDomContainer as _getDomContainer, _getQContainerElement, getSubscriber as _getSubscriber, _getVarProps, handleSSRError as _handleSSRError, _hasStoreEffects, _hmr, _injectAsyncSignalValue, invoke as _invoke, 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, _markSignalAsExternallyOwned, newInvokeContext as _newInvokeContext, _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, retryOnPromise as _retryOnPromise, _rsc, _run, _serialize, setEvent as _setEvent, _setProjectionTarget, showCmp as _shC, showCmpTask as _shT, 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, 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, useComputed$, useComputedQrl, useConstant, useContext, useContextProvider, useId, useLexicalScope, useOn, useOnDocument, useOnWindow, useResource$, useResourceQrl, useSerializer$, useSerializerQrl, useServerData, useSignal, useStore, useStyles$, useStylesQrl, useStylesScoped$, useStylesScopedQrl, useTask$, useTaskQrl, useVisibleTask$, useVisibleTaskQrl, version, withLocale };
17320
17901
  //# sourceMappingURL=core.mjs.map