@sveltejs/kit 3.0.0-next.24 → 3.0.0-next.25
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 +12 -4
- package/src/constants.js +6 -0
- package/src/core/adapt/builder.js +1 -2
- package/src/core/env.js +3 -6
- package/src/core/postbuild/prerender.js +2 -2
- package/src/core/sync/write_server.js +3 -17
- package/src/core/utils.js +10 -0
- package/src/exports/adapter.js +22 -0
- package/src/exports/public.d.ts +1 -1
- package/src/exports/vite/build/service-worker.js +98 -0
- package/src/exports/vite/dev/index.js +17 -3
- package/src/exports/vite/index.js +41 -411
- package/src/exports/vite/plugins/env-vars.js +42 -0
- package/src/exports/vite/plugins/guard.js +205 -0
- package/src/exports/vite/utils.js +97 -0
- package/src/runtime/app/server/remote/query.js +2 -2
- package/src/runtime/client/client.js +4 -4
- package/src/runtime/form-utils.js +11 -19
- package/src/runtime/server/data/index.js +11 -26
- package/src/runtime/server/errors.js +7 -10
- package/src/runtime/server/fetch.js +14 -16
- package/src/runtime/server/index.js +28 -25
- package/src/runtime/server/internal.js +34 -4
- package/src/runtime/server/page/actions.js +6 -8
- package/src/runtime/server/page/data_serializer.js +6 -10
- package/src/runtime/server/page/index.js +9 -14
- package/src/runtime/server/page/render.js +16 -29
- package/src/runtime/server/page/respond_with_error.js +7 -9
- package/src/runtime/server/remote-functions.js +125 -125
- package/src/runtime/server/respond.js +53 -28
- package/src/runtime/server/state.js +1 -0
- package/src/runtime/server/utils.js +0 -7
- package/src/runtime/utils.js +41 -0
- package/src/types/global-private.d.ts +8 -0
- package/src/types/internal.d.ts +7 -11
- package/src/utils/filesystem.js +1 -1
- package/src/utils/streaming.js +5 -15
- package/src/version.js +1 -1
- package/types/index.d.ts +20 -1
- package/types/index.d.ts.map +4 -1
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/** @import { TopLevelFilterExpression } from '@rolldown/pluginutils' */
|
|
2
|
+
/** @import { ManifestData, ValidatedConfig } from 'types' */
|
|
3
|
+
/** @import { Plugin } from 'vite' */
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { and, exactRegex, importerId, include, not } from '@rolldown/pluginutils';
|
|
6
|
+
import { app_env_private, app_server } from '../module_ids.js';
|
|
7
|
+
import {
|
|
8
|
+
error_for_missing_config,
|
|
9
|
+
normalize_id,
|
|
10
|
+
remote_module_pattern,
|
|
11
|
+
server_only_directory_pattern,
|
|
12
|
+
server_only_module_pattern
|
|
13
|
+
} from '../utils.js';
|
|
14
|
+
import { stackless } from '../../../utils/error.js';
|
|
15
|
+
import { posixify } from '../../../utils/os.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Ensures that client-side code can't accidentally import server-side code,
|
|
19
|
+
* whether in `*.server.js` files, `$app/server`, any `/server/` directory, or `$app/env/private`
|
|
20
|
+
* @param {ValidatedConfig} kit
|
|
21
|
+
* @param {() => { vite: typeof import('vite'); root: string; normalized_aliases: Array<{ alias: string, path: string }>; service_worker_entry_file: string | null; }} get_config
|
|
22
|
+
* @param {() => ManifestData} get_manifest_data
|
|
23
|
+
* @returns {Plugin}
|
|
24
|
+
*/
|
|
25
|
+
export function plugin_guard(kit, get_config, get_manifest_data) {
|
|
26
|
+
/** @type {string} */
|
|
27
|
+
let root;
|
|
28
|
+
|
|
29
|
+
/** @type {string} */
|
|
30
|
+
let normalized_cwd;
|
|
31
|
+
/** @type {Array<{ alias: string, path: string }>} */
|
|
32
|
+
let normalized_aliases;
|
|
33
|
+
/** @type {string} */
|
|
34
|
+
let normalized_node_modules;
|
|
35
|
+
/** @type {string} */
|
|
36
|
+
let normalized_routes;
|
|
37
|
+
/** @type {string} */
|
|
38
|
+
let normalized_assets;
|
|
39
|
+
|
|
40
|
+
/** @type {string | null} */
|
|
41
|
+
let service_worker_entry_file;
|
|
42
|
+
|
|
43
|
+
/** @type {Map<string, Set<string>>} */
|
|
44
|
+
const import_map = new Map();
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
name: 'vite-plugin-sveltekit-guard',
|
|
48
|
+
|
|
49
|
+
// Run this plugin before built-in resolution, so that relative imports
|
|
50
|
+
// are added to the module graph
|
|
51
|
+
enforce: 'pre',
|
|
52
|
+
|
|
53
|
+
configResolved() {
|
|
54
|
+
/** @type {typeof import('vite')} */
|
|
55
|
+
let vite;
|
|
56
|
+
({ vite, root, normalized_aliases, service_worker_entry_file } = get_config());
|
|
57
|
+
|
|
58
|
+
normalized_cwd = vite.normalizePath(root);
|
|
59
|
+
normalized_node_modules = vite.normalizePath(path.resolve(root, 'node_modules'));
|
|
60
|
+
normalized_routes = vite.normalizePath(path.resolve(root, kit.files.routes));
|
|
61
|
+
normalized_assets = vite.normalizePath(path.resolve(root, kit.files.assets));
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
applyToEnvironment(environment) {
|
|
65
|
+
// the import map is only read for client-side violations in `load`, so skip other environments
|
|
66
|
+
return environment.config.consumer === 'client';
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
resolveId: {
|
|
70
|
+
// composable filters are not accepted type-wise but still work during build
|
|
71
|
+
// see https://github.com/vitejs/rolldown-vite/issues/605
|
|
72
|
+
filter: /** @type {any} */ (
|
|
73
|
+
/** @satisfies {TopLevelFilterExpression[]} */ ([
|
|
74
|
+
include(and(importerId(/.+/), not(importerId(/index\.html$/))))
|
|
75
|
+
])
|
|
76
|
+
),
|
|
77
|
+
async handler(id, importer, options) {
|
|
78
|
+
// composable filters only work during build so we still need this guard for dev
|
|
79
|
+
// see https://github.com/vitejs/rolldown-vite/issues/605
|
|
80
|
+
if (importer && !importer.endsWith('index.html')) {
|
|
81
|
+
const resolved = await this.resolve(id, importer, { ...options, skipSelf: true });
|
|
82
|
+
|
|
83
|
+
if (resolved) {
|
|
84
|
+
const normalized = normalize_id(resolved.id, normalized_aliases, normalized_cwd);
|
|
85
|
+
|
|
86
|
+
let importers = import_map.get(normalized);
|
|
87
|
+
|
|
88
|
+
if (!importers) {
|
|
89
|
+
importers = new Set();
|
|
90
|
+
import_map.set(normalized, importers);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
importers.add(normalize_id(importer, normalized_aliases, normalized_cwd));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
load: {
|
|
100
|
+
filter: {
|
|
101
|
+
id: [
|
|
102
|
+
exactRegex(app_server),
|
|
103
|
+
exactRegex(app_env_private),
|
|
104
|
+
server_only_module_pattern,
|
|
105
|
+
server_only_directory_pattern
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
handler(id) {
|
|
109
|
+
const normalized = normalize_id(id, normalized_aliases, normalized_cwd);
|
|
110
|
+
|
|
111
|
+
let is_server_only = normalized === '$app/env/private' || normalized === '$app/server';
|
|
112
|
+
|
|
113
|
+
// skip .server.js files outside the cwd or in node_modules, as the filename might not mean 'server-only module' in this context
|
|
114
|
+
if (id.startsWith(normalized_cwd) && !id.startsWith(normalized_node_modules)) {
|
|
115
|
+
// e.g. `server.ts` or `foo.server.ts`
|
|
116
|
+
is_server_only ||= server_only_module_pattern.test(id);
|
|
117
|
+
|
|
118
|
+
// e.g. `server/foo.ts`, unless in `src/routes` or `static`
|
|
119
|
+
is_server_only ||=
|
|
120
|
+
server_only_directory_pattern.test(id) &&
|
|
121
|
+
!id.startsWith(normalized_routes + '/') &&
|
|
122
|
+
!id.startsWith(normalized_assets + '/');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!is_server_only) return;
|
|
126
|
+
|
|
127
|
+
const manifest_data = get_manifest_data();
|
|
128
|
+
|
|
129
|
+
/** @type {Set<string>} */
|
|
130
|
+
const entrypoints = new Set();
|
|
131
|
+
for (const node of manifest_data.nodes) {
|
|
132
|
+
if (node.component) entrypoints.add(node.component);
|
|
133
|
+
if (node.universal) entrypoints.add(node.universal);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (manifest_data.hooks.client) entrypoints.add(manifest_data.hooks.client);
|
|
137
|
+
if (manifest_data.hooks.universal) entrypoints.add(manifest_data.hooks.universal);
|
|
138
|
+
|
|
139
|
+
if (service_worker_entry_file) {
|
|
140
|
+
entrypoints.add(posixify(path.relative(root, service_worker_entry_file)));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Walk up the import graph from the server-only module, looking for a chain
|
|
144
|
+
// that leads back to a client entrypoint. We search all candidates (not just
|
|
145
|
+
// the first) because a module can be imported by both server and client code,
|
|
146
|
+
// and a greedy first-match could follow a server-only branch that never
|
|
147
|
+
// reaches an entrypoint — see https://github.com/sveltejs/kit/issues/16232
|
|
148
|
+
/** @type {Set<string>} */
|
|
149
|
+
const visited = new Set([normalized]);
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @param {string} current
|
|
153
|
+
* @param {string[]} chain
|
|
154
|
+
* @returns {string[] | null}
|
|
155
|
+
*/
|
|
156
|
+
function find_chain(current, chain) {
|
|
157
|
+
const importers = import_map.get(current);
|
|
158
|
+
if (!importers) return null;
|
|
159
|
+
|
|
160
|
+
for (const importer of importers) {
|
|
161
|
+
if (visited.has(importer)) continue;
|
|
162
|
+
visited.add(importer);
|
|
163
|
+
|
|
164
|
+
const next_chain = [...chain, importer];
|
|
165
|
+
if (entrypoints.has(importer)) {
|
|
166
|
+
return next_chain;
|
|
167
|
+
}
|
|
168
|
+
const result = find_chain(importer, next_chain);
|
|
169
|
+
if (result) return result;
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const chain = find_chain(normalized, [normalized]);
|
|
175
|
+
|
|
176
|
+
if (chain) {
|
|
177
|
+
if (chain.some((id) => remote_module_pattern.test(id))) {
|
|
178
|
+
error_for_missing_config('remote functions', 'experimental.remoteFunctions', 'true');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const pyramid = chain
|
|
182
|
+
.reverse()
|
|
183
|
+
.map((id, i) => {
|
|
184
|
+
return `${' '.repeat(i + 1)}${id}`;
|
|
185
|
+
})
|
|
186
|
+
.join(' imports\n');
|
|
187
|
+
|
|
188
|
+
let message = `Cannot import ${normalized} into code that runs in the browser, as this could leak sensitive information.`;
|
|
189
|
+
message += `\n\n${pyramid}`;
|
|
190
|
+
message += `\n\nIf you're only using the import as a type, change it to \`import type\`.`;
|
|
191
|
+
|
|
192
|
+
throw stackless(message);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// No chain from this server-only module to a client entrypoint was found —
|
|
196
|
+
// the module is only imported from server code, which is valid.
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
// avoid watch mode rebuilds using stale import map data
|
|
201
|
+
buildEnd() {
|
|
202
|
+
import_map.clear();
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
/** @import { UserConfig } from 'vite' */
|
|
2
|
+
/** @import { EnforcedConfig } from './types.js' */
|
|
1
3
|
import fs from 'node:fs';
|
|
2
4
|
import path from 'node:path';
|
|
5
|
+
import process from 'node:process';
|
|
6
|
+
import { styleText } from 'node:util';
|
|
3
7
|
import { posixify } from '../../utils/os.js';
|
|
4
8
|
import { negotiate } from '../../utils/http.js';
|
|
5
9
|
import { escape_html } from '../../utils/escape.js';
|
|
@@ -227,3 +231,96 @@ export function error_for_missing_config(feature_name, path, value) {
|
|
|
227
231
|
`
|
|
228
232
|
);
|
|
229
233
|
}
|
|
234
|
+
|
|
235
|
+
/** @type {EnforcedConfig} */
|
|
236
|
+
export const enforced_config = {
|
|
237
|
+
appType: true,
|
|
238
|
+
base: true,
|
|
239
|
+
build: {
|
|
240
|
+
cssCodeSplit: true,
|
|
241
|
+
emptyOutDir: true,
|
|
242
|
+
lib: {
|
|
243
|
+
entry: true,
|
|
244
|
+
name: true,
|
|
245
|
+
formats: true
|
|
246
|
+
},
|
|
247
|
+
manifest: true,
|
|
248
|
+
outDir: true,
|
|
249
|
+
rolldownOptions: {
|
|
250
|
+
input: true,
|
|
251
|
+
output: {
|
|
252
|
+
format: true,
|
|
253
|
+
entryFileNames: true,
|
|
254
|
+
chunkFileNames: true,
|
|
255
|
+
assetFileNames: true
|
|
256
|
+
},
|
|
257
|
+
preserveEntrySignatures: true
|
|
258
|
+
},
|
|
259
|
+
ssr: true
|
|
260
|
+
},
|
|
261
|
+
publicDir: true,
|
|
262
|
+
resolve: {
|
|
263
|
+
alias: {
|
|
264
|
+
$app: true,
|
|
265
|
+
$env: true,
|
|
266
|
+
'<sveltekit:generated>': true
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* @param {UserConfig} config
|
|
273
|
+
* @param {UserConfig} resolved_config
|
|
274
|
+
*/
|
|
275
|
+
export function warn_overridden_config(config, resolved_config) {
|
|
276
|
+
const overridden = find_overridden_config(config, resolved_config, enforced_config, '', []);
|
|
277
|
+
|
|
278
|
+
if (overridden.length > 0) {
|
|
279
|
+
console.error(
|
|
280
|
+
styleText(
|
|
281
|
+
['bold', 'red'],
|
|
282
|
+
'The following Vite config options will be overridden by SvelteKit:'
|
|
283
|
+
) + overridden.map((key) => `\n - ${key}`).join('')
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* @param {Record<string, any>} config
|
|
290
|
+
* @param {Record<string, any>} resolved_config
|
|
291
|
+
* @param {EnforcedConfig} enforced_config
|
|
292
|
+
* @param {string} path
|
|
293
|
+
* @param {string[]} out used locally to compute the return value
|
|
294
|
+
*/
|
|
295
|
+
export function find_overridden_config(config, resolved_config, enforced_config, path, out) {
|
|
296
|
+
if (config == null || resolved_config == null) {
|
|
297
|
+
return out;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
for (const key in enforced_config) {
|
|
301
|
+
if (typeof config === 'object' && key in config && key in resolved_config) {
|
|
302
|
+
const enforced = enforced_config[key];
|
|
303
|
+
const resolved = resolved_config[key];
|
|
304
|
+
|
|
305
|
+
if (enforced === true) {
|
|
306
|
+
if (comparable(config[key]) !== comparable(resolved)) {
|
|
307
|
+
out.push(path + key);
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
find_overridden_config(config[key], resolved, enforced, path + key + '.', out);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return out;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Normalizes a config value for comparison, since Windows paths may use backslashes
|
|
319
|
+
* and differ in casing (e.g. the drive letter) depending on where they came from.
|
|
320
|
+
* @param {any} value
|
|
321
|
+
*/
|
|
322
|
+
export function comparable(value) {
|
|
323
|
+
if (typeof value !== 'string') return value;
|
|
324
|
+
const normalized = posixify(value);
|
|
325
|
+
return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
|
|
326
|
+
}
|
|
@@ -330,7 +330,7 @@ function batch(validate_or_fn, maybe_fn) {
|
|
|
330
330
|
id: '',
|
|
331
331
|
name: '',
|
|
332
332
|
validate,
|
|
333
|
-
run: async (args
|
|
333
|
+
run: async (args) => {
|
|
334
334
|
const { event, state } = get_request_store();
|
|
335
335
|
|
|
336
336
|
return run_remote_function(
|
|
@@ -347,7 +347,7 @@ function batch(validate_or_fn, maybe_fn) {
|
|
|
347
347
|
const data = get_result(arg, i);
|
|
348
348
|
return { type: 'result', data };
|
|
349
349
|
} catch (error) {
|
|
350
|
-
const transformed = await handle_error_and_jsonify(event, state,
|
|
350
|
+
const transformed = await handle_error_and_jsonify(event, state, error);
|
|
351
351
|
|
|
352
352
|
return {
|
|
353
353
|
type: 'error',
|
|
@@ -615,7 +615,7 @@ async function _invalidate(reset_page_state = true) {
|
|
|
615
615
|
|
|
616
616
|
const token = (invalidation_token = {});
|
|
617
617
|
const nav_token = navigation_token;
|
|
618
|
-
const
|
|
618
|
+
const prev_current = current;
|
|
619
619
|
const intent = await get_navigation_intent(current.url, true);
|
|
620
620
|
|
|
621
621
|
// Clear preload, it might be affected by the invalidation.
|
|
@@ -655,9 +655,9 @@ async function _invalidate(reset_page_state = true) {
|
|
|
655
655
|
);
|
|
656
656
|
}
|
|
657
657
|
|
|
658
|
-
//
|
|
659
|
-
//
|
|
660
|
-
if (
|
|
658
|
+
// a navigation applied its result while the invalidation was loading,
|
|
659
|
+
// so the invalidation contains outdated data for a page we are no longer on
|
|
660
|
+
if (current !== prev_current) {
|
|
661
661
|
return;
|
|
662
662
|
}
|
|
663
663
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import { DEV } from 'esm-env';
|
|
5
5
|
import * as devalue from 'devalue';
|
|
6
|
-
import { text_decoder, text_encoder } from './utils.js';
|
|
6
|
+
import { stream_from_iterable, text_decoder, text_encoder } from './utils.js';
|
|
7
7
|
import { noop } from '../utils/functions.js';
|
|
8
8
|
import { SvelteKitError } from '@sveltejs/kit/internal';
|
|
9
9
|
|
|
@@ -441,25 +441,17 @@ class LazyFile {
|
|
|
441
441
|
}
|
|
442
442
|
stream() {
|
|
443
443
|
const range = read_range(this.#get_chunk, this.#offset, this.size);
|
|
444
|
-
|
|
445
|
-
return
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
} else {
|
|
452
|
-
controller.close();
|
|
453
|
-
}
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
|
-
cursor += value.byteLength;
|
|
457
|
-
controller.enqueue(value);
|
|
458
|
-
if (cursor >= this.size) {
|
|
459
|
-
controller.close();
|
|
444
|
+
const size = this.size;
|
|
445
|
+
return stream_from_iterable(
|
|
446
|
+
(async function* () {
|
|
447
|
+
let cursor = 0;
|
|
448
|
+
for await (const chunk of range) {
|
|
449
|
+
cursor += chunk.byteLength;
|
|
450
|
+
yield chunk;
|
|
460
451
|
}
|
|
461
|
-
|
|
462
|
-
|
|
452
|
+
if (cursor < size) throw new Error('incomplete file data');
|
|
453
|
+
})()
|
|
454
|
+
);
|
|
463
455
|
}
|
|
464
456
|
async text() {
|
|
465
457
|
return text_decoder.decode(await this.arrayBuffer());
|
|
@@ -6,14 +6,13 @@ import { server_data_serializer_json } from '../page/data_serializer.js';
|
|
|
6
6
|
import { load_server_data } from '../page/load_data.js';
|
|
7
7
|
import { handle_error_and_jsonify } from '../errors.js';
|
|
8
8
|
import { normalize_path } from '../../../utils/url.js';
|
|
9
|
-
import {
|
|
9
|
+
import { stream_text } from '../../utils.js';
|
|
10
10
|
import { with_version_header } from '../utils.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* @param {import('@sveltejs/kit').RequestEvent} event
|
|
14
14
|
* @param {import('types').RequestState} state
|
|
15
15
|
* @param {{ page: Pick<import('types').PageNodeIndexes, 'layouts' | 'leaf'> | null }} route
|
|
16
|
-
* @param {import('types').SSROptions} options
|
|
17
16
|
* @param {import('@sveltejs/kit').SSRManifest} manifest
|
|
18
17
|
* @param {boolean[] | undefined} invalidated_data_nodes
|
|
19
18
|
* @param {import('types').TrailingSlash} trailing_slash
|
|
@@ -23,7 +22,6 @@ export async function render_data(
|
|
|
23
22
|
event,
|
|
24
23
|
state,
|
|
25
24
|
route,
|
|
26
|
-
options,
|
|
27
25
|
manifest,
|
|
28
26
|
invalidated_data_nodes,
|
|
29
27
|
trailing_slash
|
|
@@ -92,7 +90,7 @@ export async function render_data(
|
|
|
92
90
|
return fn();
|
|
93
91
|
});
|
|
94
92
|
|
|
95
|
-
const data_serializer = server_data_serializer_json(event, state
|
|
93
|
+
const data_serializer = server_data_serializer_json(event, state);
|
|
96
94
|
await Promise.all(
|
|
97
95
|
promises.map(async (p, i) => {
|
|
98
96
|
const node = await p.catch(async (error) => {
|
|
@@ -100,7 +98,7 @@ export async function render_data(
|
|
|
100
98
|
throw error;
|
|
101
99
|
}
|
|
102
100
|
|
|
103
|
-
const transformed = await handle_error_and_jsonify(event, state,
|
|
101
|
+
const transformed = await handle_error_and_jsonify(event, state, error);
|
|
104
102
|
|
|
105
103
|
return /** @type {import('types').ServerErrorNode} */ ({
|
|
106
104
|
type: 'error',
|
|
@@ -120,27 +118,14 @@ export async function render_data(
|
|
|
120
118
|
}
|
|
121
119
|
|
|
122
120
|
return with_version_header(
|
|
123
|
-
new Response(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
controller.close();
|
|
131
|
-
},
|
|
132
|
-
|
|
133
|
-
type: 'bytes'
|
|
134
|
-
}),
|
|
135
|
-
{
|
|
136
|
-
headers: {
|
|
137
|
-
// we use a proprietary content type to prevent buffering.
|
|
138
|
-
// the `text` prefix makes it inspectable
|
|
139
|
-
'content-type': 'text/sveltekit-data',
|
|
140
|
-
'cache-control': 'private, no-store'
|
|
141
|
-
}
|
|
121
|
+
new Response(stream_text(data, chunks), {
|
|
122
|
+
headers: {
|
|
123
|
+
// we use a proprietary content type to prevent buffering.
|
|
124
|
+
// the `text` prefix makes it inspectable
|
|
125
|
+
'content-type': 'text/sveltekit-data',
|
|
126
|
+
'cache-control': 'private, no-store'
|
|
142
127
|
}
|
|
143
|
-
)
|
|
128
|
+
})
|
|
144
129
|
);
|
|
145
130
|
} catch (e) {
|
|
146
131
|
const error = normalize_error(e);
|
|
@@ -148,7 +133,7 @@ export async function render_data(
|
|
|
148
133
|
if (error instanceof Redirect) {
|
|
149
134
|
return redirect_json_response(error);
|
|
150
135
|
} else {
|
|
151
|
-
const transformed = await handle_error_and_jsonify(event, state,
|
|
136
|
+
const transformed = await handle_error_and_jsonify(event, state, error);
|
|
152
137
|
return json_response(transformed, transformed.status);
|
|
153
138
|
}
|
|
154
139
|
}
|
|
@@ -8,17 +8,16 @@ import {
|
|
|
8
8
|
import { with_request_store } from '@sveltejs/kit/internal/server';
|
|
9
9
|
import { add_deprecated_handle_error_properties, coalesce_to_error } from '../../utils/error.js';
|
|
10
10
|
import { negotiate } from '../../utils/http.js';
|
|
11
|
-
import { fix_stack_trace } from './internal.js';
|
|
11
|
+
import { fix_stack_trace, hooks, options } from './internal.js';
|
|
12
12
|
import { escape_html } from '../../utils/escape.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* @param {import('@sveltejs/kit').RequestEvent} event
|
|
16
16
|
* @param {import('types').RequestState} state
|
|
17
|
-
* @param {import('types').SSROptions} options
|
|
18
17
|
* @param {unknown} error
|
|
19
18
|
*/
|
|
20
|
-
export async function handle_fatal_error(event, state,
|
|
21
|
-
const body = await handle_error_and_jsonify(event, state,
|
|
19
|
+
export async function handle_fatal_error(event, state, error) {
|
|
20
|
+
const body = await handle_error_and_jsonify(event, state, error);
|
|
22
21
|
const status = body.status;
|
|
23
22
|
|
|
24
23
|
// sec-fetch-dest would be nicer, but non-browser clients and plain HTTP hosts don't send it
|
|
@@ -33,17 +32,16 @@ export async function handle_fatal_error(event, state, options, error) {
|
|
|
33
32
|
});
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
return static_error_page(
|
|
35
|
+
return static_error_page(status, body.message);
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
/**
|
|
40
39
|
* @param {import('@sveltejs/kit').RequestEvent} event
|
|
41
40
|
* @param {import('types').RequestState} state
|
|
42
|
-
* @param {import('types').SSROptions} options
|
|
43
41
|
* @param {any} error
|
|
44
42
|
* @returns {App.Error | Promise<App.Error>}
|
|
45
43
|
*/
|
|
46
|
-
export function handle_error_and_jsonify(event, state,
|
|
44
|
+
export function handle_error_and_jsonify(event, state, error) {
|
|
47
45
|
if (error instanceof HandledHttpError) {
|
|
48
46
|
return error.body;
|
|
49
47
|
}
|
|
@@ -90,7 +88,7 @@ export function handle_error_and_jsonify(event, state, options, error) {
|
|
|
90
88
|
const input = { ...caught, event };
|
|
91
89
|
if (__SVELTEKIT_DEV__) add_deprecated_handle_error_properties(input, fallback);
|
|
92
90
|
|
|
93
|
-
result = with_request_store({ event, state }, () =>
|
|
91
|
+
result = with_request_store({ event, state }, () => hooks.handleError(input));
|
|
94
92
|
} catch (hook_error) {
|
|
95
93
|
log_handle_error_hook_failure(error, hook_error);
|
|
96
94
|
return { status: fallback.status, message: 'Internal Error' };
|
|
@@ -141,11 +139,10 @@ function log_handle_error_hook_failure(error, hook_error) {
|
|
|
141
139
|
/**
|
|
142
140
|
* Return as a response that renders the error.html
|
|
143
141
|
*
|
|
144
|
-
* @param {import('types').SSROptions} options
|
|
145
142
|
* @param {number} status
|
|
146
143
|
* @param {string} message
|
|
147
144
|
*/
|
|
148
|
-
export function static_error_page(
|
|
145
|
+
export function static_error_page(status, message) {
|
|
149
146
|
let page = options.templates.error({ status, message: escape_html(message) });
|
|
150
147
|
|
|
151
148
|
if (__SVELTEKIT_DEV__) {
|
|
@@ -2,14 +2,13 @@ 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';
|
|
5
|
-
import { read_implementation } from './internal.js';
|
|
5
|
+
import { hooks, read_implementation } from './internal.js';
|
|
6
6
|
import { has_prerendered_path } from './utils.js';
|
|
7
7
|
import { fork_state_for_subrequest } from './state.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @param {{
|
|
11
11
|
* event: import('@sveltejs/kit').RequestEvent;
|
|
12
|
-
* options: import('types').SSROptions;
|
|
13
12
|
* manifest: import('@sveltejs/kit').SSRManifest;
|
|
14
13
|
* state: import('types').RequestState;
|
|
15
14
|
* get_cookie_header: (url: URL, header: string | null) => string;
|
|
@@ -17,7 +16,7 @@ import { fork_state_for_subrequest } from './state.js';
|
|
|
17
16
|
* }} opts
|
|
18
17
|
* @returns {typeof fetch}
|
|
19
18
|
*/
|
|
20
|
-
export function create_fetch({ event,
|
|
19
|
+
export function create_fetch({ event, manifest, state, get_cookie_header, set_internal }) {
|
|
21
20
|
/**
|
|
22
21
|
* @type {typeof fetch}
|
|
23
22
|
*/
|
|
@@ -30,7 +29,7 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade
|
|
|
30
29
|
let credentials =
|
|
31
30
|
(info instanceof Request ? info.credentials : init?.credentials) ?? 'same-origin';
|
|
32
31
|
|
|
33
|
-
return
|
|
32
|
+
return hooks.handleFetch({
|
|
34
33
|
event,
|
|
35
34
|
request: original_request,
|
|
36
35
|
fetch: async (info, init) => {
|
|
@@ -148,18 +147,19 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade
|
|
|
148
147
|
request.headers.set('accept-language', accept_language);
|
|
149
148
|
}
|
|
150
149
|
|
|
151
|
-
const response = await internal_fetch(request,
|
|
150
|
+
const response = await internal_fetch(request, manifest, state);
|
|
152
151
|
|
|
153
152
|
for (const str of response.headers.getSetCookie()) {
|
|
154
|
-
const { name, value, ...
|
|
153
|
+
const { name, value, ...cookie_options } = parseSetCookie(str, { decode: (v) => v });
|
|
155
154
|
|
|
156
|
-
const path =
|
|
155
|
+
const path =
|
|
156
|
+
cookie_options.path ?? (url.pathname.split('/').slice(0, -1).join('/') || '/');
|
|
157
157
|
|
|
158
|
-
//
|
|
158
|
+
// sameSite is string, something more specific is required - type cast is safe
|
|
159
159
|
set_internal(name, /** @type {string} */ (value), {
|
|
160
160
|
path,
|
|
161
161
|
encode: (value) => value,
|
|
162
|
-
.../** @type {import('cookie').SerializeOptions} */ (
|
|
162
|
+
.../** @type {import('cookie').SerializeOptions} */ (cookie_options)
|
|
163
163
|
});
|
|
164
164
|
}
|
|
165
165
|
|
|
@@ -193,12 +193,11 @@ function normalize_fetch_input(info, init, url) {
|
|
|
193
193
|
|
|
194
194
|
/**
|
|
195
195
|
* @param {Request} request
|
|
196
|
-
* @param {import('types').SSROptions} options
|
|
197
196
|
* @param {import('@sveltejs/kit').SSRManifest} manifest
|
|
198
197
|
* @param {import('types').RequestState} state
|
|
199
198
|
* @returns {Promise<Response>}
|
|
200
199
|
*/
|
|
201
|
-
async function internal_fetch(request,
|
|
200
|
+
async function internal_fetch(request, manifest, state) {
|
|
202
201
|
if (request.signal?.aborted) {
|
|
203
202
|
throw new DOMException('The operation was aborted.', 'AbortError');
|
|
204
203
|
}
|
|
@@ -206,7 +205,7 @@ async function internal_fetch(request, options, manifest, state) {
|
|
|
206
205
|
const subrequest_state = fork_state_for_subrequest(state);
|
|
207
206
|
|
|
208
207
|
if (!request.signal) {
|
|
209
|
-
return await respond(request,
|
|
208
|
+
return await respond(request, manifest, subrequest_state);
|
|
210
209
|
}
|
|
211
210
|
|
|
212
211
|
let remove_abort_listener = noop;
|
|
@@ -219,8 +218,7 @@ async function internal_fetch(request, options, manifest, state) {
|
|
|
219
218
|
remove_abort_listener = () => request.signal.removeEventListener('abort', on_abort);
|
|
220
219
|
});
|
|
221
220
|
|
|
222
|
-
return Promise.race([
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
]).finally(remove_abort_listener);
|
|
221
|
+
return Promise.race([respond(request, manifest, subrequest_state), abort_promise]).finally(
|
|
222
|
+
remove_abort_listener
|
|
223
|
+
);
|
|
226
224
|
}
|