@sveltejs/kit 2.53.4 → 2.55.0

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.
@@ -107,7 +107,7 @@ export function build_server_nodes(
107
107
  let fonts = [];
108
108
 
109
109
  /** @type {Set<string>} */
110
- let eager_assets = new Set();
110
+ const eager_assets = new Set();
111
111
 
112
112
  if (node.component && client_manifest) {
113
113
  exports.push(
@@ -215,7 +215,7 @@ export function build_server_nodes(
215
215
  const filename = basename(file);
216
216
  const dest = `${out}/server/stylesheets/${filename}.js`;
217
217
 
218
- let css = /** @type {string} */ (stylesheets_to_inline.get(file));
218
+ const css = /** @type {string} */ (stylesheets_to_inline.get(file));
219
219
 
220
220
  fs.writeFileSync(
221
221
  dest,
@@ -1,4 +1,5 @@
1
1
  import fs from 'node:fs';
2
+ import process from 'node:process';
2
3
  import * as vite from 'vite';
3
4
  import { dedent } from '../../../core/sync/utils.js';
4
5
  import { s } from '../../../utils/misc.js';
@@ -367,7 +367,8 @@ async function kit({ svelte_config }) {
367
367
  __SVELTEKIT_PATHS_RELATIVE__: s(kit.paths.relative),
368
368
  __SVELTEKIT_CLIENT_ROUTING__: s(kit.router.resolution === 'client'),
369
369
  __SVELTEKIT_HASH_ROUTING__: s(kit.router.type === 'hash'),
370
- __SVELTEKIT_SERVER_TRACING_ENABLED__: s(kit.experimental.tracing.server)
370
+ __SVELTEKIT_SERVER_TRACING_ENABLED__: s(kit.experimental.tracing.server),
371
+ __SVELTEKIT_EXPERIMENTAL_USE_TRANSFORM_ERROR__: s(kit.experimental.handleRenderingErrors)
371
372
  };
372
373
 
373
374
  if (is_build) {
@@ -979,7 +980,7 @@ async function kit({ svelte_config }) {
979
980
  build: {
980
981
  rollupOptions: {
981
982
  // Vite dependency crawler needs an explicit JS entry point
982
- // eventhough server otherwise works without it
983
+ // even though server otherwise works without it
983
984
  input: `${runtime_directory}/client/entry.js`
984
985
  }
985
986
  },
@@ -1,4 +1,4 @@
1
- /** @import { Asset, RouteId, Pathname, ResolvedPathname } from '$app/types' */
1
+ /** @import { Asset, RouteId, RouteIdWithSearchOrHash, Pathname, PathnameWithSearchOrHash, ResolvedPathname } from '$app/types' */
2
2
  /** @import { ResolveArgs } from './types.js' */
3
3
  import { base, assets, hash_routing } from './internal/client.js';
4
4
  import { resolve_route } from '../../../utils/routing.js';
@@ -47,7 +47,7 @@ const pathname_prefix = hash_routing ? '#' : '';
47
47
  * ```
48
48
  * @since 2.26
49
49
  *
50
- * @template {RouteId | Pathname} T
50
+ * @template {RouteIdWithSearchOrHash | PathnameWithSearchOrHash} T
51
51
  * @param {ResolveArgs<T>} args
52
52
  * @returns {ResolvedPathname}
53
53
  */
@@ -1,4 +1,4 @@
1
- import { RouteId, Pathname, ResolvedPathname } from '$app/types';
1
+ import { RouteIdWithSearchOrHash, PathnameWithSearchOrHash, ResolvedPathname } from '$app/types';
2
2
  import { ResolveArgs } from './types.js';
3
3
 
4
4
  export { resolve, asset, match } from './client.js';
@@ -24,6 +24,6 @@ export let assets: '' | `https://${string}` | `http://${string}` | '/_svelte_kit
24
24
  /**
25
25
  * @deprecated Use [`resolve(...)`](https://svelte.dev/docs/kit/$app-paths#resolve) instead
26
26
  */
27
- export function resolveRoute<T extends RouteId | Pathname>(
27
+ export function resolveRoute<T extends RouteIdWithSearchOrHash | PathnameWithSearchOrHash>(
28
28
  ...args: ResolveArgs<T>
29
29
  ): ResolvedPathname;
@@ -35,7 +35,7 @@ export async function match(url) {
35
35
  const store = try_get_request_store();
36
36
 
37
37
  if (typeof url === 'string') {
38
- const origin = store?.event.url.origin ?? 'http://internal';
38
+ const origin = store?.event.url.origin ?? 'a://a';
39
39
  url = new URL(url, origin);
40
40
  }
41
41
 
@@ -1,7 +1,23 @@
1
- import { Pathname, RouteId, RouteParams } from '$app/types';
1
+ import {
2
+ PathnameWithSearchOrHash,
3
+ RouteId,
4
+ RouteIdWithSearchOrHash,
5
+ RouteParams
6
+ } from '$app/types';
2
7
 
3
- export type ResolveArgs<T extends RouteId | Pathname> = T extends RouteId
4
- ? RouteParams<T> extends Record<string, never>
5
- ? [route: T]
6
- : [route: T, params: RouteParams<T>]
7
- : [route: T];
8
+ type StripSearchOrHash<T extends string> = T extends `${infer Pathname}?${string}`
9
+ ? Pathname
10
+ : T extends `${infer Pathname}#${string}`
11
+ ? Pathname
12
+ : T;
13
+
14
+ export type ResolveArgs<T extends RouteIdWithSearchOrHash | PathnameWithSearchOrHash> =
15
+ T extends RouteId
16
+ ? RouteParams<T> extends Record<string, never>
17
+ ? [route: T]
18
+ : [route: T, params: RouteParams<T>]
19
+ : StripSearchOrHash<T> extends infer U extends RouteId
20
+ ? RouteParams<U> extends Record<string, never>
21
+ ? [route: T]
22
+ : [route: T, params: RouteParams<U>]
23
+ : [route: T];
@@ -11,7 +11,7 @@ function context_dev(name) {
11
11
  return context();
12
12
  } catch {
13
13
  throw new Error(
14
- `Can only read '${name}' on the server during rendering (not in e.g. \`load\` functions), as it is bound to the current request via component context. This prevents state from leaking between users.` +
14
+ `Can only read '${name}' on the server during rendering (not in e.g. \`load\` functions), as it is bound to the current request via component context. This prevents state from leaking between users. ` +
15
15
  'For more information, see https://svelte.dev/docs/kit/state-management#avoid-shared-state-on-the-server'
16
16
  );
17
17
  }
@@ -56,6 +56,14 @@ export { load_css };
56
56
  const ICON_REL_ATTRIBUTES = new Set(['icon', 'shortcut icon', 'apple-touch-icon']);
57
57
 
58
58
  let errored = false;
59
+ /**
60
+ * Set via transformError, reset and read at the end of navigate.
61
+ * Necessary because a navigation might succeed loading but during rendering
62
+ * an error occurs, at which point the navigation result needs to be overridden with the error result.
63
+ * TODO this is all very hacky, rethink for SvelteKit 3 where we can assume Svelte 5 and do an overhaul of client.js
64
+ * @type {{ error: App.Error, status: number } | null}
65
+ */
66
+ let rendering_error = null;
59
67
 
60
68
  // We track the scroll position associated with each history entry in sessionStorage,
61
69
  // rather than on history.state itself, because when navigation is driven by
@@ -238,12 +246,14 @@ const on_navigate_callbacks = new Set();
238
246
  /** @type {Set<(navigation: import('@sveltejs/kit').AfterNavigate) => void>} */
239
247
  const after_navigate_callbacks = new Set();
240
248
 
241
- /** @type {import('./types.js').NavigationState} */
249
+ /** @type {import('./types.js').NavigationState & { nav: import('@sveltejs/kit').NavigationEvent }} */
242
250
  let current = {
243
251
  branch: [],
244
252
  error: null,
245
253
  // @ts-ignore - we need the initial value to be null
246
- url: null
254
+ url: null,
255
+ // @ts-ignore - we need the initial value to be null
256
+ nav: null
247
257
  };
248
258
 
249
259
  /** this being true means we SSR'd */
@@ -415,7 +425,7 @@ async function _invalidate(include_load_functions = true, reset_page_state = tru
415
425
  navigation_result.props.page.state = prev_state;
416
426
  }
417
427
  update(navigation_result.props.page);
418
- current = navigation_result.state;
428
+ current = { ...navigation_result.state, nav: current.nav };
419
429
  reset_invalidation();
420
430
  root.$set(navigation_result.props);
421
431
  } else {
@@ -581,7 +591,17 @@ async function _preload_code(url) {
581
591
  async function initialize(result, target, hydrate) {
582
592
  if (DEV && result.state.error && document.querySelector('vite-error-overlay')) return;
583
593
 
584
- current = result.state;
594
+ /** @type {import('@sveltejs/kit').NavigationEvent} */
595
+ const nav = {
596
+ params: current.params,
597
+ route: { id: current.route?.id ?? null },
598
+ url: new URL(location.href)
599
+ };
600
+
601
+ current = {
602
+ ...result.state,
603
+ nav
604
+ };
585
605
 
586
606
  const style = document.querySelector('style[data-sveltekit]');
587
607
  if (style) style.remove();
@@ -593,7 +613,17 @@ async function initialize(result, target, hydrate) {
593
613
  props: { ...result.props, stores, components },
594
614
  hydrate,
595
615
  // @ts-ignore Svelte 5 specific: asynchronously instantiate the component, i.e. don't call flushSync
596
- sync: false
616
+ sync: false,
617
+ // @ts-ignore Svelte 5 specific: transformError allows to transform errors before they are passed to boundaries
618
+ transformError: __SVELTEKIT_EXPERIMENTAL_USE_TRANSFORM_ERROR__
619
+ ? /** @param {unknown} e */ async (e) => {
620
+ const error = await handle_error(e, current.nav);
621
+ rendering_error = { error, status: get_status(e) };
622
+ page.error = error;
623
+ page.status = rendering_error.status;
624
+ return error;
625
+ }
626
+ : undefined
597
627
  });
598
628
 
599
629
  // Wait for a microtask in case svelte experimental async is enabled,
@@ -607,9 +637,7 @@ async function initialize(result, target, hydrate) {
607
637
  const navigation = {
608
638
  from: null,
609
639
  to: {
610
- params: current.params,
611
- route: { id: current.route?.id ?? null },
612
- url: new URL(location.href),
640
+ ...nav,
613
641
  scroll: scroll_positions[current_history_index] ?? scroll_state()
614
642
  },
615
643
  willUnload: false,
@@ -629,13 +657,23 @@ async function initialize(result, target, hydrate) {
629
657
  * url: URL;
630
658
  * params: Record<string, string>;
631
659
  * branch: Array<import('./types.js').BranchNode | undefined>;
660
+ * errors?: Array<import('types').CSRPageNodeLoader | undefined>;
632
661
  * status: number;
633
662
  * error: App.Error | null;
634
663
  * route: import('types').CSRRoute | null;
635
664
  * form?: Record<string, any> | null;
636
665
  * }} opts
637
666
  */
638
- function get_navigation_result_from_branch({ url, params, branch, status, error, route, form }) {
667
+ async function get_navigation_result_from_branch({
668
+ url,
669
+ params,
670
+ branch,
671
+ errors,
672
+ status,
673
+ error,
674
+ route,
675
+ form
676
+ }) {
639
677
  /** @type {import('types').TrailingSlash} */
640
678
  let slash = 'never';
641
679
 
@@ -670,6 +708,32 @@ function get_navigation_result_from_branch({ url, params, branch, status, error,
670
708
  }
671
709
  };
672
710
 
711
+ if (errors && __SVELTEKIT_EXPERIMENTAL_USE_TRANSFORM_ERROR__) {
712
+ let last_idx = -1;
713
+ result.props.errors = await Promise.all(
714
+ // eslint-disable-next-line @typescript-eslint/await-thenable
715
+ branch
716
+ .map((b, i) => {
717
+ if (i === 0) return undefined; // root layout wraps root error component, not the other way around
718
+ if (!b) return null;
719
+
720
+ i--;
721
+ // Find the closest error component up to the previous branch
722
+ while (i > last_idx + 1 && !errors[i]) i -= 1;
723
+ last_idx = i;
724
+ return errors[i]?.()
725
+ .then((e) => e.component)
726
+ .catch(() => undefined);
727
+ })
728
+ // filter out indexes where there was no branch, but keep indexes where there was a branch but no error component
729
+ .filter((e) => e !== null)
730
+ );
731
+ }
732
+
733
+ if (error && __SVELTEKIT_EXPERIMENTAL_USE_TRANSFORM_ERROR__) {
734
+ result.props.error = error;
735
+ }
736
+
673
737
  if (form !== undefined) {
674
738
  result.props.form = form;
675
739
  }
@@ -1201,6 +1265,7 @@ async function load_route({ id, invalidating, url, params, route, preload }) {
1201
1265
  url,
1202
1266
  params,
1203
1267
  branch: branch.slice(0, error_load.idx).concat(error_load.node),
1268
+ errors,
1204
1269
  status,
1205
1270
  error,
1206
1271
  route
@@ -1220,6 +1285,7 @@ async function load_route({ id, invalidating, url, params, route, preload }) {
1220
1285
  url,
1221
1286
  params,
1222
1287
  branch,
1288
+ errors,
1223
1289
  status: 200,
1224
1290
  error: null,
1225
1291
  route,
@@ -1325,6 +1391,7 @@ async function load_root_error_page({ status, error, url, route }) {
1325
1391
  branch: [root_layout, root_error],
1326
1392
  status,
1327
1393
  error,
1394
+ errors: [],
1328
1395
  route: null
1329
1396
  });
1330
1397
  } catch (error) {
@@ -1732,7 +1799,16 @@ async function navigate({
1732
1799
  });
1733
1800
  }
1734
1801
 
1735
- current = navigation_result.state;
1802
+ // Type-casts are save because we know this resolved a proper SvelteKit route
1803
+ const target = /** @type {import('@sveltejs/kit').NavigationTarget} */ (nav.navigation.to);
1804
+ current = {
1805
+ ...navigation_result.state,
1806
+ nav: {
1807
+ params: /** @type {Record<string, any>} */ (target.params),
1808
+ route: target.route,
1809
+ url: target.url
1810
+ }
1811
+ };
1736
1812
 
1737
1813
  // reset url before updating page store
1738
1814
  if (navigation_result.props.page) {
@@ -1744,7 +1820,12 @@ async function navigate({
1744
1820
  if (fork) {
1745
1821
  commit_promise = fork.commit();
1746
1822
  } else {
1823
+ rendering_error = null; // TODO this can break with forks, rethink for SvelteKit 3 where we can assume Svelte 5
1747
1824
  root.$set(navigation_result.props);
1825
+ // Check for sync rendering error
1826
+ if (rendering_error) {
1827
+ Object.assign(navigation_result.props.page, rendering_error);
1828
+ }
1748
1829
  update(navigation_result.props.page);
1749
1830
 
1750
1831
  commit_promise = svelte.settled?.();
@@ -1800,6 +1881,10 @@ async function navigate({
1800
1881
  autoscroll = true;
1801
1882
 
1802
1883
  if (navigation_result.props.page) {
1884
+ // Check for async rendering error
1885
+ if (rendering_error) {
1886
+ Object.assign(navigation_result.props.page, rendering_error);
1887
+ }
1803
1888
  Object.assign(page, navigation_result.props.page);
1804
1889
  }
1805
1890
 
@@ -2401,16 +2486,17 @@ export async function set_nearest_error_page(error, status = 500) {
2401
2486
 
2402
2487
  const error_load = await load_nearest_error_page(current.branch.length, branch, route.errors);
2403
2488
  if (error_load) {
2404
- const navigation_result = get_navigation_result_from_branch({
2489
+ const navigation_result = await get_navigation_result_from_branch({
2405
2490
  url,
2406
2491
  params: current.params,
2407
2492
  branch: branch.slice(0, error_load.idx).concat(error_load.node),
2408
2493
  status,
2409
2494
  error,
2495
+ // do not set errors, we haven't changed the page so the previous ones are still current
2410
2496
  route
2411
2497
  });
2412
2498
 
2413
- current = navigation_result.state;
2499
+ current = { ...navigation_result.state, nav: current.nav };
2414
2500
 
2415
2501
  root.$set(navigation_result.props);
2416
2502
  update(navigation_result.props.page);
@@ -2829,12 +2915,13 @@ async function _hydrate(
2829
2915
  }
2830
2916
  }
2831
2917
 
2832
- result = get_navigation_result_from_branch({
2918
+ result = await get_navigation_result_from_branch({
2833
2919
  url,
2834
2920
  params,
2835
2921
  branch,
2836
2922
  status,
2837
2923
  error,
2924
+ errors: parsed_route?.errors, // TODO load earlier?
2838
2925
  form,
2839
2926
  route: parsed_route ?? null
2840
2927
  });
@@ -298,6 +298,14 @@ export function form(id) {
298
298
  return;
299
299
  }
300
300
 
301
+ const target = event.submitter?.hasAttribute('formtarget')
302
+ ? /** @type {HTMLButtonElement | HTMLInputElement} */ (event.submitter).formTarget
303
+ : clone(form).target;
304
+
305
+ if (target === '_blank') {
306
+ return;
307
+ }
308
+
301
309
  event.preventDefault();
302
310
 
303
311
  const form_data = new FormData(form, event.submitter);
@@ -14,6 +14,8 @@ export let updated;
14
14
  const is_legacy =
15
15
  onMount.toString().includes('$$') || /function \w+\(\) \{\}/.test(onMount.toString());
16
16
 
17
+ const placeholder_url = 'a:';
18
+
17
19
  if (is_legacy) {
18
20
  page = {
19
21
  data: {},
@@ -23,7 +25,7 @@ if (is_legacy) {
23
25
  route: { id: null },
24
26
  state: {},
25
27
  status: -1,
26
- url: new URL('https://example.com')
28
+ url: new URL(placeholder_url)
27
29
  };
28
30
  navigating = { current: null };
29
31
  updated = { current: false };
@@ -36,7 +38,7 @@ if (is_legacy) {
36
38
  route = $state.raw({ id: null });
37
39
  state = $state.raw({});
38
40
  status = $state.raw(-1);
39
- url = $state.raw(new URL('https://example.com'));
41
+ url = $state.raw(new URL(placeholder_url));
40
42
  })();
41
43
 
42
44
  navigating = new (class Navigating {
@@ -85,9 +85,11 @@ export type NavigationFinished = {
85
85
  state: NavigationState;
86
86
  props: {
87
87
  constructors: Array<typeof SvelteComponent>;
88
+ errors?: Array<typeof SvelteComponent | undefined>;
88
89
  components?: SvelteComponent[];
89
90
  page: Page;
90
91
  form?: Record<string, any> | null;
92
+ error?: App.Error;
91
93
  [key: `data_${number}`]: Record<string, any>;
92
94
  };
93
95
  };
@@ -285,6 +285,11 @@ export async function render_page(
285
285
 
286
286
  const layouts = compact(branch.slice(0, j + 1));
287
287
  const nodes = new PageNodes(layouts.map((layout) => layout.node));
288
+ const error_branch = layouts.concat({
289
+ node,
290
+ data: null,
291
+ server_data: null
292
+ });
288
293
 
289
294
  return await render_response({
290
295
  event,
@@ -299,11 +304,14 @@ export async function render_page(
299
304
  },
300
305
  status,
301
306
  error,
302
- branch: layouts.concat({
303
- node,
304
- data: null,
305
- server_data: null
306
- }),
307
+ error_components: await load_error_components(
308
+ options,
309
+ ssr,
310
+ error_branch,
311
+ page,
312
+ manifest
313
+ ),
314
+ branch: error_branch,
307
315
  fetched,
308
316
  data_serializer
309
317
  });
@@ -350,11 +358,11 @@ export async function render_page(
350
358
  },
351
359
  status,
352
360
  error: null,
353
- branch: ssr === false ? [] : compact(branch),
361
+ branch: !ssr ? [] : compact(branch),
354
362
  action_result,
355
363
  fetched,
356
- data_serializer:
357
- ssr === false ? server_data_serializer(event, event_state, options) : data_serializer
364
+ data_serializer: !ssr ? server_data_serializer(event, event_state, options) : data_serializer,
365
+ error_components: await load_error_components(options, ssr, branch, page, manifest)
358
366
  });
359
367
  } catch (e) {
360
368
  // a remote function could have thrown a redirect during render
@@ -376,3 +384,44 @@ export async function render_page(
376
384
  });
377
385
  }
378
386
  }
387
+
388
+ /**
389
+ *
390
+ * @param {import('types').SSROptions} options
391
+ * @param {boolean} ssr
392
+ * @param {Array<import('./types.js').Loaded | null>} branch
393
+ * @param {import('types').PageNodeIndexes} page
394
+ * @param {import('@sveltejs/kit').SSRManifest} manifest
395
+ */
396
+ async function load_error_components(options, ssr, branch, page, manifest) {
397
+ /** @type {Array<import('types').SSRComponent | undefined> | undefined} */
398
+ let error_components;
399
+
400
+ if (options.server_error_boundaries && ssr) {
401
+ let last_idx = -1;
402
+ error_components = await Promise.all(
403
+ // eslint-disable-next-line @typescript-eslint/await-thenable
404
+ branch
405
+ .map((b, i) => {
406
+ if (i === 0) return undefined; // root layout wraps root error component, not the other way around
407
+ if (!b) return null;
408
+
409
+ i--;
410
+ // Find the closest error component up to the previous branch
411
+ while (i > last_idx + 1 && page.errors[i] === undefined) i -= 1;
412
+ last_idx = i;
413
+
414
+ const idx = page.errors[i];
415
+ if (idx == null) return undefined;
416
+
417
+ return manifest._.nodes[idx]?.()
418
+ .then((e) => e.component?.())
419
+ .catch(() => undefined);
420
+ })
421
+ // filter out indexes where there was no branch, but keep indexes where there was a branch but no error component
422
+ .filter((e) => e !== null)
423
+ );
424
+ }
425
+
426
+ return error_components;
427
+ }
@@ -15,8 +15,9 @@ import { create_server_routing_response, generate_route_object } from './server_
15
15
  import { add_resolution_suffix } from '../../pathname.js';
16
16
  import { try_get_request_store, with_request_store } from '@sveltejs/kit/internal/server';
17
17
  import { text_encoder } from '../../utils.js';
18
- import { get_global_name } from '../utils.js';
18
+ import { get_global_name, handle_error_and_jsonify } from '../utils.js';
19
19
  import { create_remote_key } from '../../shared.js';
20
+ import { get_status } from '../../../utils/error.js';
20
21
 
21
22
  // TODO rename this function/module
22
23
 
@@ -40,7 +41,8 @@ const updated = {
40
41
  * event_state: import('types').RequestState;
41
42
  * resolve_opts: import('types').RequiredResolveOptions;
42
43
  * action_result?: import('@sveltejs/kit').ActionResult;
43
- * data_serializer: import('./types.js').ServerDataSerializer
44
+ * data_serializer: import('./types.js').ServerDataSerializer;
45
+ * error_components?: Array<import('types').SSRComponent | undefined>
44
46
  * }} opts
45
47
  */
46
48
  export async function render_response({
@@ -56,7 +58,8 @@ export async function render_response({
56
58
  event_state,
57
59
  resolve_opts,
58
60
  action_result,
59
- data_serializer
61
+ data_serializer,
62
+ error_components
60
63
  }) {
61
64
  if (state.prerendering) {
62
65
  if (options.csp.mode === 'nonce') {
@@ -147,6 +150,13 @@ export async function render_response({
147
150
  form: form_value
148
151
  };
149
152
 
153
+ if (error_components) {
154
+ if (error) {
155
+ props.error = error;
156
+ }
157
+ props.errors = error_components;
158
+ }
159
+
150
160
  let data = {};
151
161
 
152
162
  // props_n (instead of props[n]) makes it easy to avoid
@@ -176,7 +186,15 @@ export async function render_response({
176
186
  }
177
187
  ]
178
188
  ]),
179
- csp: csp.script_needs_nonce ? { nonce: csp.nonce } : { hash: csp.script_needs_hash }
189
+ csp: csp.script_needs_nonce ? { nonce: csp.nonce } : { hash: csp.script_needs_hash },
190
+ transformError: error_components
191
+ ? /** @param {unknown} e */ async (e) => {
192
+ const transformed = await handle_error_and_jsonify(event, event_state, options, e);
193
+ props.page.error = props.error = error = transformed;
194
+ props.page.status = status = get_status(e);
195
+ return transformed;
196
+ }
197
+ : undefined
180
198
  };
181
199
 
182
200
  const fetch = globalThis.fetch;
@@ -494,7 +512,30 @@ export async function render_response({
494
512
  if (!info.id) continue;
495
513
 
496
514
  for (const key in cache) {
497
- remote[create_remote_key(info.id, key)] = await cache[key];
515
+ const remote_key = create_remote_key(info.id, key);
516
+
517
+ if (event_state.refreshes?.[remote_key] !== undefined) {
518
+ // This entry was refreshed/set by a command or form action.
519
+ // Always await it so the mutation result is serialized.
520
+ remote[remote_key] = await cache[key];
521
+ } else {
522
+ // Don't block the response on pending remote data - if a query
523
+ // hasn't settled yet, it wasn't awaited in the template (or is behind a pending boundary).
524
+ const result = await Promise.race([
525
+ Promise.resolve(cache[key]).then(
526
+ (v) => /** @type {const} */ ({ settled: true, value: v }),
527
+ (e) => /** @type {const} */ ({ settled: true, error: e })
528
+ ),
529
+ new Promise((resolve) => {
530
+ queueMicrotask(() => resolve(/** @type {const} */ ({ settled: false })));
531
+ })
532
+ ]);
533
+
534
+ if (result.settled) {
535
+ if ('error' in result) throw result.error;
536
+ remote[remote_key] = result.value;
537
+ }
538
+ }
498
539
  }
499
540
  }
500
541
 
@@ -101,6 +101,7 @@ export async function respond_with_error({
101
101
  status,
102
102
  error: await handle_error_and_jsonify(event, event_state, options, error),
103
103
  branch,
104
+ error_components: [],
104
105
  fetched,
105
106
  event,
106
107
  event_state,
@@ -6,7 +6,7 @@ import { hash } from '../../../utils/hash.js';
6
6
  *
7
7
  * The first closes the script element, so everything after is treated as raw HTML.
8
8
  * The second disables further parsing until `-->`, so the script element might be unexpectedly
9
- * kept open until until an unrelated HTML comment in the page.
9
+ * kept open up until an unrelated HTML comment in the page.
10
10
  *
11
11
  * U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR are escaped for the sake of pre-2018
12
12
  * browsers.
@@ -104,6 +104,11 @@ declare module '$app/types' {
104
104
  */
105
105
  export type RouteId = ReturnType<AppTypes['RouteId']>;
106
106
 
107
+ /**
108
+ * `RouteId`, but possibly suffixed with a search string and/or hash.
109
+ */
110
+ export type RouteIdWithSearchOrHash = RouteId | `${RouteId}?${string}` | `${RouteId}#${string}`;
111
+
107
112
  /**
108
113
  * A utility for getting the parameters associated with a given route.
109
114
  */
@@ -123,6 +128,14 @@ declare module '$app/types' {
123
128
  */
124
129
  export type Pathname = ReturnType<AppTypes['Pathname']>;
125
130
 
131
+ /**
132
+ * `Pathname`, but possibly suffixed with a search string and/or hash.
133
+ */
134
+ export type PathnameWithSearchOrHash =
135
+ | Pathname
136
+ | `${Pathname}?${string}`
137
+ | `${Pathname}#${string}`;
138
+
126
139
  /**
127
140
  * `Pathname`, but possibly prefixed with a base path. Used for `page.url.pathname`.
128
141
  */
@@ -57,6 +57,7 @@ declare global {
57
57
  * to throw an error if the feature would fail in production.
58
58
  */
59
59
  var __SVELTEKIT_TRACK__: (label: string) => void;
60
+ var __SVELTEKIT_EXPERIMENTAL_USE_TRANSFORM_ERROR__: boolean;
60
61
  var Bun: object;
61
62
  var Deno: object;
62
63
  }
@@ -468,6 +468,7 @@ export interface SSROptions {
468
468
  root: SSRComponent['default'];
469
469
  service_worker: boolean;
470
470
  service_worker_options: RegistrationOptions;
471
+ server_error_boundaries: boolean;
471
472
  templates: {
472
473
  app(values: {
473
474
  head: string;