@sveltejs/kit 3.0.0-next.10 → 3.0.0-next.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/package.json +1 -1
  2. package/src/constants.js +2 -0
  3. package/src/core/adapt/builder.js +3 -6
  4. package/src/core/config/index.js +6 -1
  5. package/src/core/config/options.js +11 -69
  6. package/src/core/postbuild/fallback.js +2 -1
  7. package/src/core/postbuild/prerender.js +75 -28
  8. package/src/core/postbuild/queue.js +2 -1
  9. package/src/core/sync/write_server.js +2 -2
  10. package/src/core/utils.js +28 -3
  11. package/src/exports/env/index.js +68 -3
  12. package/src/exports/internal/env.js +6 -3
  13. package/src/exports/params.js +9 -4
  14. package/src/exports/public.d.ts +41 -20
  15. package/src/exports/vite/dev/index.js +50 -6
  16. package/src/exports/vite/index.js +130 -125
  17. package/src/exports/vite/module_ids.js +0 -2
  18. package/src/exports/vite/preview/index.js +3 -2
  19. package/src/exports/vite/utils.js +20 -0
  20. package/src/runtime/app/paths/server.js +1 -1
  21. package/src/runtime/app/server/index.js +1 -1
  22. package/src/runtime/app/server/remote/query.js +1 -1
  23. package/src/runtime/client/ndjson.js +1 -1
  24. package/src/runtime/client/stream.js +3 -2
  25. package/src/runtime/server/data/index.js +1 -1
  26. package/src/runtime/server/errors.js +135 -0
  27. package/src/runtime/server/fetch.js +1 -1
  28. package/src/runtime/server/index.js +20 -10
  29. package/src/runtime/server/internal.js +25 -0
  30. package/src/runtime/server/page/actions.js +2 -1
  31. package/src/runtime/server/page/data_serializer.js +10 -10
  32. package/src/runtime/server/page/index.js +3 -2
  33. package/src/runtime/server/page/load_data.js +1 -1
  34. package/src/runtime/server/page/render.js +3 -7
  35. package/src/runtime/server/page/respond_with_error.js +6 -5
  36. package/src/runtime/server/{remote.js → remote-functions.js} +1 -1
  37. package/src/runtime/server/respond.js +3 -7
  38. package/src/runtime/server/sourcemaps.js +183 -0
  39. package/src/runtime/server/utils.js +2 -157
  40. package/src/types/ambient-private.d.ts +2 -0
  41. package/src/types/global-private.d.ts +0 -5
  42. package/src/types/internal.d.ts +6 -6
  43. package/src/types/private.d.ts +8 -0
  44. package/src/utils/escape.js +9 -25
  45. package/src/utils/features.js +1 -1
  46. package/src/utils/fork.js +7 -2
  47. package/src/utils/page_nodes.js +3 -5
  48. package/src/version.js +1 -1
  49. package/types/index.d.ts +72 -22
  50. package/types/index.d.ts.map +3 -1
  51. package/src/runtime/shared-server.js +0 -7
@@ -34,6 +34,9 @@ import {
34
34
  error_for_missing_config,
35
35
  get_config_aliases,
36
36
  normalize_id,
37
+ remote_module_pattern,
38
+ server_only_directory_pattern,
39
+ server_only_module_pattern,
37
40
  strip_virtual_prefix
38
41
  } from './utils.js';
39
42
  import { stackless } from '../../utils/error.js';
@@ -51,7 +54,6 @@ import {
51
54
  sveltekit_env,
52
55
  sveltekit_env_private,
53
56
  sveltekit_env_service_worker,
54
- sveltekit_server,
55
57
  sveltekit_env_public_client,
56
58
  sveltekit_env_public_server
57
59
  } from './module_ids.js';
@@ -402,11 +404,23 @@ function kit({ svelte_config }) {
402
404
  // dev and preview config can be shared
403
405
  /** @type {UserConfig} */
404
406
  const new_config = {
407
+ environments: {
408
+ ssr: {
409
+ build: {
410
+ sourcemap:
411
+ config.environments?.ssr?.build?.sourcemap ?? config.build?.sourcemap ?? true
412
+ }
413
+ }
414
+ },
405
415
  resolve: {
406
416
  alias: [
407
417
  { find: '__SERVER__', replacement: `${generated}/server` },
408
418
  { find: '$app', replacement: `${runtime_directory}/app` },
409
419
  { find: '$env', replacement: `${runtime_directory}/env` },
420
+ {
421
+ find: '__sveltekit/server',
422
+ replacement: `${runtime_directory}/server/internal.js`
423
+ },
410
424
  ...get_config_aliases(kit, root)
411
425
  ]
412
426
  },
@@ -458,11 +472,8 @@ function kit({ svelte_config }) {
458
472
  }
459
473
  };
460
474
 
475
+ // treat .remote.js files as empty for the purposes of prebundling
461
476
  if (kit.experimental.remoteFunctions) {
462
- // treat .remote.js files as empty for the purposes of prebundling
463
- const remote_id_filter = new RegExp(
464
- `.remote(${kit.moduleExtensions.join('|')})$`.replaceAll('.', '\\.')
465
- );
466
477
  // @ts-expect-error optimizeDeps is already set above
467
478
  new_config.optimizeDeps.rolldownOptions ??= {};
468
479
  // @ts-expect-error
@@ -471,7 +482,7 @@ function kit({ svelte_config }) {
471
482
  new_config.optimizeDeps.rolldownOptions.plugins.push({
472
483
  name: 'vite-plugin-sveltekit-setup:optimize-remote-functions',
473
484
  load: {
474
- filter: { id: remote_id_filter },
485
+ filter: { id: remote_module_pattern },
475
486
  handler() {
476
487
  return '';
477
488
  }
@@ -493,7 +504,6 @@ function kit({ svelte_config }) {
493
504
  __SVELTEKIT_SUPPORTS_ASYNC__: s(
494
505
  svelte_config.compilerOptions?.experimental?.async ?? false
495
506
  ),
496
- __SVELTEKIT_ROOT__: s(root),
497
507
  __SVELTEKIT_DEV__: s(!is_build)
498
508
  };
499
509
 
@@ -648,8 +658,7 @@ function kit({ svelte_config }) {
648
658
  exactRegex(sveltekit_env_private),
649
659
  exactRegex(sveltekit_env_public_client),
650
660
  exactRegex(sveltekit_env_public_server),
651
- exactRegex(sveltekit_env_service_worker),
652
- exactRegex(sveltekit_server)
661
+ exactRegex(sveltekit_env_service_worker)
653
662
  ]
654
663
  },
655
664
  handler(id) {
@@ -687,22 +696,6 @@ function kit({ svelte_config }) {
687
696
  kit.appDir
688
697
  )
689
698
  : create_sveltekit_env_service_worker_dev(explicit_env_config, env, kit_global);
690
-
691
- case sveltekit_server: {
692
- return dedent`
693
- export let read_implementation = null;
694
-
695
- export let manifest = null;
696
-
697
- export function set_read_implementation(fn) {
698
- read_implementation = fn;
699
- }
700
-
701
- export function set_manifest(_) {
702
- manifest = _;
703
- }
704
- `;
705
- }
706
699
  }
707
700
  }
708
701
  }
@@ -710,10 +703,6 @@ function kit({ svelte_config }) {
710
703
 
711
704
  /** @type {Map<string, Set<string>>} */
712
705
  const import_map = new Map();
713
- // Matches any ID that has .server. in its filename
714
- const server_only_module_pattern = /\.server\.[^/]+$/;
715
- // Matches any ID that has /server/ in its path
716
- const server_only_directory_pattern = /\/server\//;
717
706
 
718
707
  /**
719
708
  * Ensures that client-side code can't accidentally import server-side code,
@@ -769,24 +758,21 @@ function kit({ svelte_config }) {
769
758
  ]
770
759
  },
771
760
  handler(id) {
772
- // skip .server.js files outside the cwd or in node_modules, as the filename might not mean 'server-only module' in this context
773
- const is_internal =
774
- id.startsWith(normalized_cwd) && !id.startsWith(normalized_node_modules);
775
-
776
761
  const normalized = normalize_id(id, normalized_aliases, normalized_cwd);
777
762
 
778
- // server-only directories: any file in a `/server/` folder inside the cwd,
779
- // except those inside the routes or assets directories
780
- const is_in_routes = id.startsWith(normalized_routes + '/');
781
- const is_in_assets = id.startsWith(normalized_assets + '/');
782
- const is_server_only_directory =
783
- is_internal && !is_in_routes && !is_in_assets && server_only_directory_pattern.test(id);
763
+ let is_server_only = normalized === '$app/env/private' || normalized === '$app/server';
784
764
 
785
- const is_server_only =
786
- normalized === '$app/env/private' ||
787
- normalized === '$app/server' ||
788
- is_server_only_directory ||
789
- (is_internal && server_only_module_pattern.test(id));
765
+ // skip .server.js files outside the cwd or in node_modules, as the filename might not mean 'server-only module' in this context
766
+ if (id.startsWith(normalized_cwd) && !id.startsWith(normalized_node_modules)) {
767
+ // e.g. `server.ts` or `foo.server.ts`
768
+ is_server_only ||= server_only_module_pattern.test(id);
769
+
770
+ // e.g. `server/foo.ts`, unless in `src/routes` or `static`
771
+ is_server_only ||=
772
+ server_only_directory_pattern.test(id) &&
773
+ !id.startsWith(normalized_routes + '/') &&
774
+ !id.startsWith(normalized_assets + '/');
775
+ }
790
776
 
791
777
  if (!is_server_only) return;
792
778
 
@@ -837,6 +823,10 @@ function kit({ svelte_config }) {
837
823
  const chain = find_chain(normalized, [normalized]);
838
824
 
839
825
  if (chain) {
826
+ if (chain.some((id) => remote_module_pattern.test(id))) {
827
+ error_for_missing_config('remote functions', 'experimental.remoteFunctions', 'true');
828
+ }
829
+
840
830
  const pyramid = chain
841
831
  .reverse()
842
832
  .map((id, i) => {
@@ -907,31 +897,30 @@ function kit({ svelte_config }) {
907
897
  dev_server = _dev_server;
908
898
  },
909
899
 
910
- async transform(code, id) {
911
- const normalized = normalize_id(id, normalized_aliases, normalized_cwd);
912
- if (!svelte_config.kit.moduleExtensions.some((ext) => normalized.endsWith(`.remote${ext}`))) {
913
- return;
914
- }
915
-
916
- const file = posixify(path.relative(root, id));
917
- const remote = {
918
- hash: hash(file),
919
- file
920
- };
900
+ transform: {
901
+ filter: {
902
+ id: remote_module_pattern
903
+ },
904
+ async handler(code, id) {
905
+ const file = posixify(path.relative(root, id));
906
+ const remote = {
907
+ hash: hash(file),
908
+ file
909
+ };
921
910
 
922
- remotes.push(remote);
911
+ remotes.push(remote);
923
912
 
924
- if (this.environment.config.consumer !== 'client') {
925
- // we need to add an `await Promise.resolve()` because if the user imports this function
926
- // on the client AND in a load function when loading the client module we will trigger
927
- // an ssrLoadModule during dev. During a link preload, the module can be mistakenly
928
- // loaded and transformed twice and the first time all its exports would be undefined
929
- // triggering a dev server error. By adding a microtask we ensure that the module is fully loaded
913
+ if (this.environment.config.consumer !== 'client') {
914
+ // we need to add an `await Promise.resolve()` because if the user imports this function
915
+ // on the client AND in a load function when loading the client module we will trigger
916
+ // an ssrLoadModule during dev. During a link preload, the module can be mistakenly
917
+ // loaded and transformed twice and the first time all its exports would be undefined
918
+ // triggering a dev server error. By adding a microtask we ensure that the module is fully loaded
930
919
 
931
- // Extra newlines to prevent syntax errors around missing semicolons or comments
932
- code +=
933
- '\n\n' +
934
- dedent`
920
+ // Extra newlines to prevent syntax errors around missing semicolons or comments
921
+ code +=
922
+ '\n\n' +
923
+ dedent`
935
924
  import * as $$_self_$$ from './${path.basename(id)}';
936
925
  import { init_remote_functions as $$_init_$$ } from '@sveltejs/kit/internal/server';
937
926
 
@@ -945,71 +934,72 @@ function kit({ svelte_config }) {
945
934
  }
946
935
  `;
947
936
 
948
- // Emit a dedicated entry chunk for this remote in SSR builds (prod only)
949
- if (!dev_server) {
950
- remote_original_by_hash.set(remote.hash, id);
951
- if (!emitted_remote_hashes.has(remote.hash)) {
952
- this.emitFile({
953
- type: 'chunk',
954
- id: `\0sveltekit-remote:${remote.hash}`,
955
- name: `remote-${remote.hash}`
956
- });
957
- emitted_remote_hashes.add(remote.hash);
937
+ // Emit a dedicated entry chunk for this remote in SSR builds (prod only)
938
+ if (!dev_server) {
939
+ remote_original_by_hash.set(remote.hash, id);
940
+ if (!emitted_remote_hashes.has(remote.hash)) {
941
+ this.emitFile({
942
+ type: 'chunk',
943
+ id: `\0sveltekit-remote:${remote.hash}`,
944
+ name: `remote-${remote.hash}`
945
+ });
946
+ emitted_remote_hashes.add(remote.hash);
947
+ }
958
948
  }
959
- }
960
949
 
961
- return code;
962
- }
950
+ return code;
951
+ }
963
952
 
964
- // For the client, read the exports and create a new module that only contains fetch functions with the correct metadata
953
+ // For the client, read the exports and create a new module that only contains fetch functions with the correct metadata
965
954
 
966
- /** @type {Map<string, RemoteInternals['type']>} */
967
- const map = new Map();
955
+ /** @type {Map<string, RemoteInternals['type']>} */
956
+ const map = new Map();
968
957
 
969
- // in dev, load the server module here (which will result in this hook
970
- // being called again with `opts.ssr === true` if the module isn't
971
- // already loaded) so we can determine what it exports
972
- if (dev_server) {
973
- const module = await dev_server.ssrLoadModule(id);
958
+ // in dev, load the server module here (which will result in this hook
959
+ // being called again with `opts.ssr === true` if the module isn't
960
+ // already loaded) so we can determine what it exports
961
+ if (dev_server) {
962
+ const module = await dev_server.ssrLoadModule(id);
974
963
 
975
- for (const [name, value] of Object.entries(module)) {
976
- const type = value?.__?.type;
977
- if (type) {
978
- map.set(name, type);
964
+ for (const [name, value] of Object.entries(module)) {
965
+ const type = value?.__?.type;
966
+ if (type) {
967
+ map.set(name, type);
968
+ }
979
969
  }
980
970
  }
981
- }
982
971
 
983
- // in prod, we already built and analysed the server code before
984
- // building the client code, so `remotes` is populated
985
- else if (build_metadata?.remotes) {
986
- const exports = build_metadata?.remotes.get(remote.hash);
987
- if (!exports) throw new Error('Expected to find metadata for remote file ' + id);
972
+ // in prod, we already built and analysed the server code before
973
+ // building the client code, so `remotes` is populated
974
+ else if (build_metadata?.remotes) {
975
+ const exports = build_metadata?.remotes.get(remote.hash);
976
+ if (!exports) throw new Error('Expected to find metadata for remote file ' + id);
988
977
 
989
- for (const [name, value] of exports) {
990
- map.set(name, value.type);
978
+ for (const [name, value] of exports) {
979
+ map.set(name, value.type);
980
+ }
991
981
  }
992
- }
993
982
 
994
- const { namespace, declarations, reexports } = create_exported_declarations(
995
- map.keys(),
996
- (name, ns) => `${ns}.${map.get(name)}('${remote.hash}/${name}')`,
997
- '__remote'
998
- );
983
+ const { namespace, declarations, reexports } = create_exported_declarations(
984
+ map.keys(),
985
+ (name, ns) => `${ns}.${map.get(name)}('${remote.hash}/${name}')`,
986
+ '__remote'
987
+ );
999
988
 
1000
- let result = `import * as ${namespace} from '__sveltekit/remote';\n\n${declarations.join('\n')}`;
1001
- if (reexports.length > 0) {
1002
- result += `\nexport { ${reexports.join(', ')} };`;
1003
- }
1004
- result += '\n';
989
+ let result = `import * as ${namespace} from '__sveltekit/remote';\n\n${declarations.join('\n')}`;
990
+ if (reexports.length > 0) {
991
+ result += `\nexport { ${reexports.join(', ')} };`;
992
+ }
993
+ result += '\n';
1005
994
 
1006
- if (dev_server) {
1007
- result += `\nimport.meta.hot?.accept();\n`;
1008
- }
995
+ if (dev_server) {
996
+ result += `\nimport.meta.hot?.accept();\n`;
997
+ }
1009
998
 
1010
- return {
1011
- code: result
1012
- };
999
+ return {
1000
+ code: result
1001
+ };
1002
+ }
1013
1003
  }
1014
1004
  };
1015
1005
 
@@ -1844,15 +1834,30 @@ function kit({ svelte_config }) {
1844
1834
  }
1845
1835
 
1846
1836
  // ...and prerender
1847
- const prerender_results = await prerender({
1848
- hash: kit.router.type === 'hash',
1849
- out,
1850
- manifest_path,
1851
- metadata,
1852
- verbose,
1853
- env,
1854
- vite_config_file: vite_config.configFile
1855
- });
1837
+ let prerender_results;
1838
+ try {
1839
+ prerender_results = await prerender({
1840
+ hash: kit.router.type === 'hash',
1841
+ out,
1842
+ manifest_path,
1843
+ metadata,
1844
+ verbose,
1845
+ env,
1846
+ vite_config_file: vite_config.configFile
1847
+ });
1848
+
1849
+ // this silly hack is necessary to ensure that stderr from prerender is flushed before we continue
1850
+ await new Promise((f) => setTimeout(f, 0));
1851
+ } catch (e) {
1852
+ if (e instanceof Error && e.message === '__handled__') {
1853
+ // error details are already logged inside `prerender`, don't duplicate them
1854
+ throw stackless('Prerendering failed');
1855
+ } else {
1856
+ // Unforeseen error, rethrow as-is
1857
+ throw e;
1858
+ }
1859
+ }
1860
+
1856
1861
  prerendered = prerender_results.prerendered;
1857
1862
 
1858
1863
  await treeshake_prerendered_remotes(
@@ -9,8 +9,6 @@ export const sveltekit_env_service_worker = '\0virtual:__sveltekit/env/service-w
9
9
 
10
10
  export const service_worker = '\0virtual:service-worker';
11
11
 
12
- export const sveltekit_server = '\0virtual:__sveltekit/server';
13
-
14
12
  export const app_server = posixify(
15
13
  fileURLToPath(new URL('../../runtime/app/server/index.js', import.meta.url))
16
14
  );
@@ -10,6 +10,7 @@ import { loadEnv, normalizePath } from 'vite';
10
10
  import { createReadableStream, getRequest, setResponse } from '../../../exports/node/index.js';
11
11
  import { SVELTE_KIT_ASSETS } from '../../../constants.js';
12
12
  import { is_chrome_devtools_request, not_found } from '../utils.js';
13
+ import { stackless } from '../../../utils/error.js';
13
14
 
14
15
  /**
15
16
  * @param {PreviewServer} vite
@@ -27,8 +28,8 @@ export async function preview(vite, vite_config, svelte_config) {
27
28
 
28
29
  const dir = join(svelte_config.kit.outDir, 'output/server');
29
30
 
30
- if (!fs.existsSync(dir)) {
31
- throw new Error(`Server files not found at ${dir}, did you run \`build\` first?`);
31
+ if (!fs.existsSync(`${dir}/manifest.js`)) {
32
+ throw stackless(`Server files not found at ${dir}, did you run \`build\` first?`);
32
33
  }
33
34
 
34
35
  const instrumentation = join(dir, 'instrumentation.server.js');
@@ -10,6 +10,7 @@ import {
10
10
  service_worker,
11
11
  sveltekit_env_private
12
12
  } from './module_ids.js';
13
+ import { styleText } from 'node:util';
13
14
 
14
15
  /**
15
16
  * Transforms alias to a valid vite.resolve.alias array.
@@ -149,6 +150,10 @@ export function normalize_id(id, aliases, cwd) {
149
150
  return posixify(id);
150
151
  }
151
152
 
153
+ export const remote_module_pattern = /[/.]remote(\.[^/]+)+$/;
154
+ export const server_only_module_pattern = /[/.]server(\.[^/]+)+$/;
155
+ export const server_only_directory_pattern = /\/server\//;
156
+
152
157
  export const strip_virtual_prefix = /** @param {string} id */ (id) => id.replace('\0virtual:', '');
153
158
 
154
159
  /**
@@ -187,3 +192,18 @@ export function error_for_missing_config(feature_name, path, value) {
187
192
  `
188
193
  );
189
194
  }
195
+
196
+ /**
197
+ * @param {number} status
198
+ * @param {Request} request
199
+ */
200
+ export function log_response(status, request) {
201
+ const url = new URL(request.url);
202
+ const log = `[${status}] ${request.method} ${url.href.replace(url.origin, '')}`;
203
+
204
+ if (status < 400) {
205
+ console.log(log);
206
+ } else {
207
+ console.error(styleText(['bold', 'red'], log));
208
+ }
209
+ }
@@ -3,7 +3,7 @@ import { resolve_route, find_route } from '../../../utils/routing.js';
3
3
  import { decode_pathname } from '../../../utils/url.js';
4
4
  import { add_data_suffix } from '../../pathname.js';
5
5
  import { try_get_request_store } from '@sveltejs/kit/internal/server';
6
- import { manifest } from '__sveltekit/server';
6
+ import { manifest } from '../../server/internal.js';
7
7
  import { get_hooks } from '__SERVER__/internal.js';
8
8
 
9
9
  /** @type {import('./client.js').asset} */
@@ -1,4 +1,4 @@
1
- import { read_implementation, manifest } from '__sveltekit/server';
1
+ import { read_implementation, manifest } from '../../server/internal.js';
2
2
  import { assets } from '$app/paths/internal/server';
3
3
  import { base64_decode } from '../../utils.js';
4
4
 
@@ -13,7 +13,7 @@ import {
13
13
  } from './shared.js';
14
14
  import { noop } from '../../../../utils/functions.js';
15
15
  import { SharedIterator } from '../../../../utils/shared-iterator.js';
16
- import { handle_error_and_jsonify } from '../../../server/utils.js';
16
+ import { handle_error_and_jsonify } from '../../../server/errors.js';
17
17
 
18
18
  /**
19
19
  * Creates a remote query. When called from the browser, the function will be invoked on the server via a `fetch` call.
@@ -6,7 +6,7 @@ import { read_stream } from './stream.js';
6
6
  * @param {ReadableStreamDefaultReader<Uint8Array>} reader
7
7
  */
8
8
  export async function* read_ndjson(reader) {
9
- for await (const block of read_stream(reader, '\n')) {
9
+ for await (const block of read_stream(reader, '\n', { fatal: true })) {
10
10
  const line = block.trim();
11
11
  if (line) {
12
12
  yield JSON.parse(line);
@@ -4,11 +4,12 @@
4
4
  * stream closes.
5
5
  * @param {ReadableStreamDefaultReader<Uint8Array>} reader
6
6
  * @param {string} delimiter
7
+ * @param {TextDecoderOptions} [options]
7
8
  */
8
- export async function* read_stream(reader, delimiter) {
9
+ export async function* read_stream(reader, delimiter, options) {
9
10
  let done = false;
10
11
  let buffer = '';
11
- const decoder = new TextDecoder();
12
+ const decoder = new TextDecoder(undefined, options);
12
13
 
13
14
  while (true) {
14
15
  let split = buffer.indexOf(delimiter);
@@ -4,7 +4,7 @@ import { normalize_error } from '../../../utils/error.js';
4
4
  import { once } from '../../../utils/functions.js';
5
5
  import { server_data_serializer_json } from '../page/data_serializer.js';
6
6
  import { load_server_data } from '../page/load_data.js';
7
- import { handle_error_and_jsonify } from '../utils.js';
7
+ import { handle_error_and_jsonify } from '../errors.js';
8
8
  import { normalize_path } from '../../../utils/url.js';
9
9
  import { text_encoder } from '../../utils.js';
10
10
 
@@ -0,0 +1,135 @@
1
+ import { json, text } from '@sveltejs/kit';
2
+ import { HttpError, SvelteKitError } from '@sveltejs/kit/internal';
3
+ import { with_request_store } from '@sveltejs/kit/internal/server';
4
+ import { coalesce_to_error, get_message, get_status } from '../../utils/error.js';
5
+ import { negotiate } from '../../utils/http.js';
6
+ import { fix_stack_trace } from './internal.js';
7
+ import { escape_html } from '../../utils/escape.js';
8
+
9
+ /**
10
+ * @param {import('@sveltejs/kit').RequestEvent} event
11
+ * @param {import('types').RequestState} state
12
+ * @param {import('types').SSROptions} options
13
+ * @param {unknown} error
14
+ */
15
+ export async function handle_fatal_error(event, state, options, error) {
16
+ error = error instanceof HttpError ? error : coalesce_to_error(error);
17
+ const body = await handle_error_and_jsonify(event, state, options, error);
18
+ const status = body.status;
19
+
20
+ // ideally we'd use sec-fetch-dest instead, but Safari — quelle surprise — doesn't support it
21
+ const type = negotiate(event.request.headers.get('accept') || 'text/html', [
22
+ 'application/json',
23
+ 'text/html'
24
+ ]);
25
+
26
+ if (event.isDataRequest || type === 'application/json') {
27
+ return json(body, {
28
+ status
29
+ });
30
+ }
31
+
32
+ return static_error_page(options, status, body.message);
33
+ }
34
+
35
+ /**
36
+ * @param {import('@sveltejs/kit').RequestEvent} event
37
+ * @param {import('types').RequestState} state
38
+ * @param {import('types').SSROptions} options
39
+ * @param {any} error
40
+ * @returns {App.Error | Promise<App.Error>}
41
+ */
42
+ export function handle_error_and_jsonify(event, state, options, error) {
43
+ if (error instanceof HttpError) {
44
+ // @ts-expect-error custom user errors may not have a message field if App.Error is overwritten
45
+ return { message: 'Unknown Error', ...error.body };
46
+ }
47
+
48
+ let e = error;
49
+ while (e instanceof Error) {
50
+ fix_stack_trace(e);
51
+ e = e.cause;
52
+ }
53
+
54
+ const status = get_status(error);
55
+ const message = get_message(error);
56
+
57
+ // TODO 4.0 await this, rather than handling the non-Promise case
58
+ let result;
59
+ try {
60
+ result = with_request_store({ event, state }, () =>
61
+ options.hooks.handleError({ error, event, status, message })
62
+ ) ?? { status, message };
63
+ } catch (hook_error) {
64
+ log_handle_error_hook_failure(error, hook_error);
65
+ return { status, message: 'Internal Error' };
66
+ }
67
+
68
+ if (result instanceof Promise) {
69
+ if (!__SVELTEKIT_SUPPORTS_ASYNC__ && state.is_in_render) {
70
+ console.warn(
71
+ `To use an async \`handleError\` hook to handle errors that occur during rendering, you must enable \`compilerOptions.experimental.async\` in the SvelteKit plugin of your Vite config. The returned error has been replaced with a generic object`
72
+ );
73
+
74
+ // we're discarding the result, but we still need to prevent an unhandled
75
+ // rejection if the user's async `handleError` hook rejects
76
+ result.catch((hook_error) => log_handle_error_hook_failure(error, hook_error));
77
+
78
+ return {
79
+ status,
80
+ message: 'Internal Error'
81
+ };
82
+ }
83
+
84
+ return result.then(
85
+ (body) => {
86
+ body ??= { status, message };
87
+ return { ...body, status: get_status(body, error) };
88
+ },
89
+ (hook_error) => {
90
+ log_handle_error_hook_failure(error, hook_error);
91
+ return { status, message: 'Internal Error' };
92
+ }
93
+ );
94
+ }
95
+
96
+ return { ...result, status: get_status(result, error) };
97
+ }
98
+
99
+ /**
100
+ * @param {unknown} error
101
+ * @param {unknown} hook_error
102
+ */
103
+ function log_handle_error_hook_failure(error, hook_error) {
104
+ const failure = new Error('The `handleError` hook failed', {
105
+ cause: coalesce_to_error(hook_error)
106
+ });
107
+ failure.stack = failure.message;
108
+ console.error(failure);
109
+ if (error instanceof SvelteKitError) {
110
+ console.error(`Original error: ${error.status} ${error.text}: ${error.message}`);
111
+ } else {
112
+ console.error('Original error:', error);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Return as a response that renders the error.html
118
+ *
119
+ * @param {import('types').SSROptions} options
120
+ * @param {number} status
121
+ * @param {string} message
122
+ */
123
+ export function static_error_page(options, status, message) {
124
+ let page = options.templates.error({ status, message: escape_html(message) });
125
+
126
+ if (__SVELTEKIT_DEV__) {
127
+ // inject Vite HMR client, for easier debugging
128
+ page = page.replace('</head>', '<script type="module" src="/@vite/client"></script></head>');
129
+ }
130
+
131
+ return text(page, {
132
+ headers: { 'content-type': 'text/html; charset=utf-8' },
133
+ status
134
+ });
135
+ }
@@ -2,7 +2,7 @@ import { parseSetCookie } from 'cookie';
2
2
  import { noop } from '../../utils/functions.js';
3
3
  import { respond } from './respond.js';
4
4
  import * as paths from '$app/paths/internal/server';
5
- import { read_implementation } from '__sveltekit/server';
5
+ import { read_implementation } from './internal.js';
6
6
  import { has_prerendered_path } from './utils.js';
7
7
 
8
8
  /**