@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.
- package/package.json +1 -1
- package/src/constants.js +2 -0
- package/src/core/adapt/builder.js +3 -6
- package/src/core/config/index.js +6 -1
- package/src/core/config/options.js +11 -69
- package/src/core/postbuild/fallback.js +2 -1
- package/src/core/postbuild/prerender.js +75 -28
- package/src/core/postbuild/queue.js +2 -1
- package/src/core/sync/write_server.js +2 -2
- package/src/core/utils.js +28 -3
- package/src/exports/env/index.js +68 -3
- package/src/exports/internal/env.js +6 -3
- package/src/exports/params.js +9 -4
- package/src/exports/public.d.ts +41 -20
- package/src/exports/vite/dev/index.js +50 -6
- package/src/exports/vite/index.js +130 -125
- package/src/exports/vite/module_ids.js +0 -2
- package/src/exports/vite/preview/index.js +3 -2
- package/src/exports/vite/utils.js +20 -0
- package/src/runtime/app/paths/server.js +1 -1
- package/src/runtime/app/server/index.js +1 -1
- package/src/runtime/app/server/remote/query.js +1 -1
- package/src/runtime/client/ndjson.js +1 -1
- package/src/runtime/client/stream.js +3 -2
- package/src/runtime/server/data/index.js +1 -1
- package/src/runtime/server/errors.js +135 -0
- package/src/runtime/server/fetch.js +1 -1
- package/src/runtime/server/index.js +20 -10
- package/src/runtime/server/internal.js +25 -0
- package/src/runtime/server/page/actions.js +2 -1
- package/src/runtime/server/page/data_serializer.js +10 -10
- package/src/runtime/server/page/index.js +3 -2
- package/src/runtime/server/page/load_data.js +1 -1
- package/src/runtime/server/page/render.js +3 -7
- package/src/runtime/server/page/respond_with_error.js +6 -5
- package/src/runtime/server/{remote.js → remote-functions.js} +1 -1
- package/src/runtime/server/respond.js +3 -7
- package/src/runtime/server/sourcemaps.js +183 -0
- package/src/runtime/server/utils.js +2 -157
- package/src/types/ambient-private.d.ts +2 -0
- package/src/types/global-private.d.ts +0 -5
- package/src/types/internal.d.ts +6 -6
- package/src/types/private.d.ts +8 -0
- package/src/utils/escape.js +9 -25
- package/src/utils/features.js +1 -1
- package/src/utils/fork.js +7 -2
- package/src/utils/page_nodes.js +3 -5
- package/src/version.js +1 -1
- package/types/index.d.ts +72 -22
- package/types/index.d.ts.map +3 -1
- 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:
|
|
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
|
-
|
|
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
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
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
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
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
|
-
|
|
911
|
+
remotes.push(remote);
|
|
923
912
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
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
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
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
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
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
|
-
|
|
962
|
-
|
|
950
|
+
return code;
|
|
951
|
+
}
|
|
963
952
|
|
|
964
|
-
|
|
953
|
+
// For the client, read the exports and create a new module that only contains fetch functions with the correct metadata
|
|
965
954
|
|
|
966
|
-
|
|
967
|
-
|
|
955
|
+
/** @type {Map<string, RemoteInternals['type']>} */
|
|
956
|
+
const map = new Map();
|
|
968
957
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
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
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
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
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
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
|
-
|
|
990
|
-
|
|
978
|
+
for (const [name, value] of exports) {
|
|
979
|
+
map.set(name, value.type);
|
|
980
|
+
}
|
|
991
981
|
}
|
|
992
|
-
}
|
|
993
982
|
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
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
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
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
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
995
|
+
if (dev_server) {
|
|
996
|
+
result += `\nimport.meta.hot?.accept();\n`;
|
|
997
|
+
}
|
|
1009
998
|
|
|
1010
|
-
|
|
1011
|
-
|
|
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
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
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
|
|
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 '
|
|
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} */
|
|
@@ -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/
|
|
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 '../
|
|
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 '
|
|
5
|
+
import { read_implementation } from './internal.js';
|
|
6
6
|
import { has_prerendered_path } from './utils.js';
|
|
7
7
|
|
|
8
8
|
/**
|