@sveltejs/kit 3.0.0-next.3 → 3.0.0-next.6
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 +17 -17
- package/src/core/adapt/builder.js +18 -4
- package/src/core/adapt/index.js +1 -4
- package/src/core/config/index.js +64 -5
- package/src/core/config/options.js +73 -21
- package/src/core/env.js +35 -4
- package/src/core/postbuild/analyse.js +8 -12
- package/src/core/postbuild/crawl.js +22 -6
- package/src/core/postbuild/prerender.js +40 -23
- package/src/core/sync/create_manifest_data/index.js +23 -5
- package/src/core/sync/write_client_manifest.js +7 -0
- package/src/core/sync/write_server.js +13 -10
- package/src/core/sync/write_tsconfig.js +2 -4
- package/src/exports/index.js +32 -10
- package/src/exports/internal/env.js +1 -1
- package/src/exports/internal/index.js +6 -5
- package/src/exports/node/index.js +57 -6
- package/src/exports/public.d.ts +218 -114
- package/src/exports/vite/build/build_server.js +6 -1
- package/src/exports/vite/dev/index.js +10 -20
- package/src/exports/vite/index.js +306 -222
- package/src/exports/vite/preview/index.js +15 -7
- package/src/exports/vite/utils.js +8 -10
- package/src/runtime/app/env/types.d.ts +1 -1
- package/src/runtime/app/forms.js +22 -5
- package/src/runtime/app/paths/client.js +1 -3
- package/src/runtime/app/paths/public.d.ts +0 -28
- package/src/runtime/app/paths/server.js +7 -3
- package/src/runtime/app/server/remote/form.js +10 -3
- package/src/runtime/app/server/remote/query.js +3 -6
- package/src/runtime/app/server/remote/requested.js +8 -4
- package/src/runtime/app/server/remote/shared.js +5 -7
- package/src/runtime/client/client.js +190 -95
- package/src/runtime/client/fetcher.js +3 -2
- package/src/runtime/client/remote-functions/form.svelte.js +134 -24
- package/src/runtime/client/remote-functions/prerender.svelte.js +8 -2
- package/src/runtime/client/remote-functions/query/index.js +3 -2
- package/src/runtime/client/remote-functions/query/instance.svelte.js +26 -10
- package/src/runtime/client/remote-functions/query-batch.svelte.js +4 -3
- package/src/runtime/client/remote-functions/query-live/instance.svelte.js +26 -9
- package/src/runtime/client/remote-functions/shared.svelte.js +17 -7
- package/src/runtime/client/types.d.ts +9 -1
- package/src/runtime/client/utils.js +1 -1
- package/src/runtime/form-utils.js +84 -16
- package/src/runtime/server/cookie.js +22 -33
- package/src/runtime/server/csrf.js +65 -0
- package/src/runtime/server/data/index.js +7 -7
- package/src/runtime/server/env_module.js +0 -5
- package/src/runtime/server/index.js +1 -1
- package/src/runtime/server/page/actions.js +41 -26
- package/src/runtime/server/page/index.js +2 -1
- package/src/runtime/server/page/render.js +21 -23
- package/src/runtime/server/page/respond_with_error.js +7 -8
- package/src/runtime/server/remote.js +64 -27
- package/src/runtime/server/respond.js +81 -50
- package/src/runtime/server/utils.js +7 -7
- package/src/runtime/telemetry/otel.js +1 -1
- package/src/types/ambient.d.ts +5 -4
- package/src/types/global-private.d.ts +4 -4
- package/src/types/internal.d.ts +8 -10
- package/src/types/private.d.ts +33 -1
- package/src/types/synthetic/$lib.md +1 -1
- package/src/utils/error.js +11 -3
- package/src/utils/shared-iterator.js +5 -0
- package/src/utils/url.js +99 -2
- package/src/version.js +1 -1
- package/types/index.d.ts +340 -186
- package/types/index.d.ts.map +10 -9
|
@@ -11,7 +11,7 @@ import { uneval_action_response } from './actions.js';
|
|
|
11
11
|
import { SVELTE_KIT_ASSETS } from '../../../constants.js';
|
|
12
12
|
import { SCHEME } from '../../../utils/url.js';
|
|
13
13
|
import { create_server_routing_response, generate_route_object } from './server_routing.js';
|
|
14
|
-
import { add_resolution_suffix } from '../../pathname.js';
|
|
14
|
+
import { add_data_suffix, add_resolution_suffix } from '../../pathname.js';
|
|
15
15
|
import { try_get_request_store, with_request_store } from '@sveltejs/kit/internal/server';
|
|
16
16
|
import { text_encoder } from '../../utils.js';
|
|
17
17
|
import {
|
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
get_global_name,
|
|
21
21
|
handle_error_and_jsonify
|
|
22
22
|
} from '../utils.js';
|
|
23
|
-
import { get_status } from '../../../utils/error.js';
|
|
24
23
|
import * as env from '__sveltekit/env';
|
|
25
24
|
import { collect_remote_data } from '../remote.js';
|
|
26
25
|
|
|
@@ -68,7 +67,7 @@ export async function render_response({
|
|
|
68
67
|
}) {
|
|
69
68
|
if (state.prerendering) {
|
|
70
69
|
if (options.csp.mode === 'nonce') {
|
|
71
|
-
throw new Error('Cannot use prerendering if config.
|
|
70
|
+
throw new Error('Cannot use prerendering if config.csp.mode === "nonce"');
|
|
72
71
|
}
|
|
73
72
|
|
|
74
73
|
if (options.app_template_contains_nonce) {
|
|
@@ -119,7 +118,12 @@ export async function render_response({
|
|
|
119
118
|
// if appropriate, use relative paths for greater portability
|
|
120
119
|
if (paths.relative) {
|
|
121
120
|
if (!state.prerendering?.fallback) {
|
|
122
|
-
|
|
121
|
+
// the relative path depth must reflect the URL the browser is actually at, which
|
|
122
|
+
// for a data request includes the `__data.json` suffix that was stripped during routing
|
|
123
|
+
const pathname = event.isDataRequest
|
|
124
|
+
? add_data_suffix(event.url.pathname)
|
|
125
|
+
: event.url.pathname;
|
|
126
|
+
const segments = pathname.slice(paths.base.length).split('/').slice(2);
|
|
123
127
|
|
|
124
128
|
base = segments.map(() => '..').join('/') || '.';
|
|
125
129
|
|
|
@@ -200,7 +204,7 @@ export async function render_response({
|
|
|
200
204
|
|
|
201
205
|
const transformed = await handle_error_and_jsonify(event, event_state, options, e);
|
|
202
206
|
props.page.error = props.error = error = transformed;
|
|
203
|
-
props.page.status = status =
|
|
207
|
+
props.page.status = status = transformed.status;
|
|
204
208
|
return transformed;
|
|
205
209
|
}
|
|
206
210
|
: undefined
|
|
@@ -374,7 +378,13 @@ export async function render_response({
|
|
|
374
378
|
if (page_config.csr && client) {
|
|
375
379
|
const route = client.routes?.find((r) => r.id === event.route.id) ?? null;
|
|
376
380
|
|
|
377
|
-
|
|
381
|
+
// when serving a prerendered page in an app that uses runtime public env vars, we must
|
|
382
|
+
// import the env.js module so that it evaluates before any user code can evaluate.
|
|
383
|
+
// TODO revert to using top-level await once https://bugs.webkit.org/show_bug.cgi?id=242740 is fixed
|
|
384
|
+
// https://github.com/sveltejs/kit/pull/11601
|
|
385
|
+
const load_env_eagerly = client.uses_env_dynamic_public && !!state.prerendering;
|
|
386
|
+
|
|
387
|
+
if (load_env_eagerly) {
|
|
378
388
|
modulepreloads.add(`${paths.app_dir}/env.js`);
|
|
379
389
|
}
|
|
380
390
|
|
|
@@ -386,7 +396,7 @@ export async function render_response({
|
|
|
386
396
|
/** @type {(path: string) => void} */
|
|
387
397
|
let add_preload;
|
|
388
398
|
|
|
389
|
-
// see the
|
|
399
|
+
// see the output.preloadStrategy option for details on why we have multiple options here
|
|
390
400
|
if (options.link_header_preload && !state.prerendering) {
|
|
391
401
|
add_preload = (path) =>
|
|
392
402
|
link_headers.add(`<${encodeURI(path)}>; rel="modulepreload"; nopush`);
|
|
@@ -411,12 +421,6 @@ export async function render_response({
|
|
|
411
421
|
|
|
412
422
|
const blocks = [];
|
|
413
423
|
|
|
414
|
-
// when serving a prerendered page in an app that uses $app/env/public, we must
|
|
415
|
-
// import the env.js module so that it evaluates before any user code can evaluate.
|
|
416
|
-
// TODO revert to using top-level await once https://bugs.webkit.org/show_bug.cgi?id=242740 is fixed
|
|
417
|
-
// https://github.com/sveltejs/kit/pull/11601
|
|
418
|
-
const load_env_eagerly = client.uses_env_dynamic_public && state.prerendering; // TODO implement uses_env_dynamic_public
|
|
419
|
-
|
|
420
424
|
const properties = [`base: ${base_expression}`];
|
|
421
425
|
|
|
422
426
|
if (paths.assets) {
|
|
@@ -438,7 +442,7 @@ export async function render_response({
|
|
|
438
442
|
|
|
439
443
|
if (Object.keys(options.hooks.transport).length > 0) {
|
|
440
444
|
if (client.inline) {
|
|
441
|
-
app_declaration = `const app =
|
|
445
|
+
app_declaration = `const app = ${global}.app.app;`;
|
|
442
446
|
} else if (client.app) {
|
|
443
447
|
app_declaration = `const app = await import(${s(prefixed(client.app))});`;
|
|
444
448
|
} else {
|
|
@@ -501,7 +505,7 @@ export async function render_response({
|
|
|
501
505
|
`error: ${serialized.error}`
|
|
502
506
|
];
|
|
503
507
|
|
|
504
|
-
if (status !== 200) {
|
|
508
|
+
if (status !== 200 && !error) {
|
|
505
509
|
hydrate.push(`status: ${status}`);
|
|
506
510
|
}
|
|
507
511
|
|
|
@@ -555,12 +559,9 @@ export async function render_response({
|
|
|
555
559
|
}
|
|
556
560
|
|
|
557
561
|
if (options.service_worker) {
|
|
558
|
-
let opts =
|
|
562
|
+
let opts = ", { type: 'module' }";
|
|
559
563
|
if (options.service_worker_options != null) {
|
|
560
|
-
const service_worker_options = { ...options.service_worker_options };
|
|
561
|
-
if (__SVELTEKIT_DEV__) {
|
|
562
|
-
service_worker_options.type = 'module';
|
|
563
|
-
}
|
|
564
|
+
const service_worker_options = { ...options.service_worker_options, type: 'module' };
|
|
564
565
|
opts = `, ${s(service_worker_options)}`;
|
|
565
566
|
}
|
|
566
567
|
|
|
@@ -579,11 +580,8 @@ export async function render_response({
|
|
|
579
580
|
}`);
|
|
580
581
|
}
|
|
581
582
|
|
|
582
|
-
// we need to eagerly import the Vite client module in development to ensure
|
|
583
|
-
// that Vite global constant replacements are initialised before our code runs
|
|
584
583
|
const init_app = `
|
|
585
584
|
{
|
|
586
|
-
${DEV ? `import('${paths.base}/@vite/client')` : ''}
|
|
587
585
|
${blocks.join('\n\n\t\t\t\t\t')}
|
|
588
586
|
}
|
|
589
587
|
`;
|
|
@@ -2,7 +2,6 @@ import { Redirect } from '@sveltejs/kit/internal';
|
|
|
2
2
|
import { render_response } from './render.js';
|
|
3
3
|
import { load_data, load_server_data } from './load_data.js';
|
|
4
4
|
import { handle_error_and_jsonify, static_error_page, redirect_response } from '../utils.js';
|
|
5
|
-
import { get_status } from '../../../utils/error.js';
|
|
6
5
|
import { PageNodes } from '../../../utils/page_nodes.js';
|
|
7
6
|
import { server_data_serializer } from './data_serializer.js';
|
|
8
7
|
|
|
@@ -90,6 +89,8 @@ export async function respond_with_error({
|
|
|
90
89
|
);
|
|
91
90
|
}
|
|
92
91
|
|
|
92
|
+
const transformed = await handle_error_and_jsonify(event, event_state, options, error);
|
|
93
|
+
|
|
93
94
|
return await render_response({
|
|
94
95
|
options,
|
|
95
96
|
manifest,
|
|
@@ -98,8 +99,8 @@ export async function respond_with_error({
|
|
|
98
99
|
ssr,
|
|
99
100
|
csr
|
|
100
101
|
},
|
|
101
|
-
status,
|
|
102
|
-
error:
|
|
102
|
+
status: transformed.status,
|
|
103
|
+
error: transformed,
|
|
103
104
|
branch,
|
|
104
105
|
error_components: [],
|
|
105
106
|
fetched,
|
|
@@ -115,10 +116,8 @@ export async function respond_with_error({
|
|
|
115
116
|
return redirect_response(e.status, e.location);
|
|
116
117
|
}
|
|
117
118
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
(await handle_error_and_jsonify(event, event_state, options, e)).message
|
|
122
|
-
);
|
|
119
|
+
const transformed = await handle_error_and_jsonify(event, event_state, options, e);
|
|
120
|
+
|
|
121
|
+
return static_error_page(options, transformed.status, transformed.message);
|
|
123
122
|
}
|
|
124
123
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/** @import { RemoteFormInternals, RemoteFunctionData, RemoteFunctionResponse, RemoteInternals, RequestState, SSROptions } from 'types' */
|
|
3
3
|
|
|
4
4
|
import { json, error } from '@sveltejs/kit';
|
|
5
|
-
import {
|
|
5
|
+
import { Redirect, SvelteKitError } from '@sveltejs/kit/internal';
|
|
6
6
|
import { with_request_store, merge_tracing } from '@sveltejs/kit/internal/server';
|
|
7
7
|
import { app_dir, base } from '$app/paths/internal/server';
|
|
8
8
|
import { is_form_content_type } from '../../utils/http.js';
|
|
@@ -14,6 +14,13 @@ import { DEV } from 'esm-env';
|
|
|
14
14
|
import { record_span } from '../telemetry/record_span.js';
|
|
15
15
|
import { deserialize_binary_form } from '../form-utils.js';
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* How long (in milliseconds) to wait after the last message was sent before
|
|
19
|
+
* sending a `: keep-alive` SSE comment, to prevent proxies/load balancers with
|
|
20
|
+
* an idle timeout from closing an otherwise-quiet `query.live` connection.
|
|
21
|
+
*/
|
|
22
|
+
const KEEP_ALIVE_INTERVAL = 30_000;
|
|
23
|
+
|
|
17
24
|
/** @type {typeof handle_remote_call_internal} */
|
|
18
25
|
export async function handle_remote_call(event, state, options, manifest, id) {
|
|
19
26
|
return record_span({
|
|
@@ -41,10 +48,10 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
41
48
|
const [hash, name, additional_args] = id.split('/');
|
|
42
49
|
const remotes = manifest._.remotes;
|
|
43
50
|
|
|
44
|
-
if (!remotes
|
|
51
|
+
if (!Object.hasOwn(remotes, hash)) error(404);
|
|
45
52
|
|
|
46
53
|
const module = await remotes[hash]();
|
|
47
|
-
const fn = module.default[name];
|
|
54
|
+
const fn = Object.hasOwn(module.default, name) ? module.default[name] : undefined;
|
|
48
55
|
|
|
49
56
|
if (!fn) error(404);
|
|
50
57
|
|
|
@@ -57,6 +64,9 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
57
64
|
'sveltekit.remote.call.name': internals.name
|
|
58
65
|
});
|
|
59
66
|
|
|
67
|
+
/** @type {HeadersInit | undefined} */
|
|
68
|
+
const headers = state.prerendering ? undefined : { 'cache-control': 'private, no-store' };
|
|
69
|
+
|
|
60
70
|
try {
|
|
61
71
|
/** @type {RemoteFunctionData} */
|
|
62
72
|
const data = {};
|
|
@@ -79,22 +89,43 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
79
89
|
|
|
80
90
|
const encoder = new TextEncoder();
|
|
81
91
|
|
|
92
|
+
let closed = false;
|
|
93
|
+
|
|
94
|
+
/** @type {ReturnType<typeof setTimeout> | undefined} */
|
|
95
|
+
let keep_alive;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* (Re)schedule the keep-alive comment. Called whenever a message is sent, so
|
|
99
|
+
* that a keep-alive is only emitted once `KEEP_ALIVE_INTERVAL` has elapsed
|
|
100
|
+
* without any other activity.
|
|
101
|
+
* @param {ReadableStreamDefaultController} controller
|
|
102
|
+
*/
|
|
103
|
+
function schedule_keep_alive(controller) {
|
|
104
|
+
clearTimeout(keep_alive);
|
|
105
|
+
keep_alive = setTimeout(() => {
|
|
106
|
+
if (closed || event.request.signal.aborted) return;
|
|
107
|
+
// SSE comments (lines starting with `:`) are ignored by the client
|
|
108
|
+
controller.enqueue(encoder.encode(': keep-alive\n\n'));
|
|
109
|
+
schedule_keep_alive(controller);
|
|
110
|
+
}, KEEP_ALIVE_INTERVAL);
|
|
111
|
+
}
|
|
112
|
+
|
|
82
113
|
/**
|
|
83
114
|
* @param {ReadableStreamDefaultController} controller
|
|
84
115
|
* @param {any} payload
|
|
85
116
|
*/
|
|
86
117
|
function send(controller, payload) {
|
|
87
118
|
controller.enqueue(encoder.encode('data: ' + JSON.stringify(payload) + '\n\n'));
|
|
119
|
+
schedule_keep_alive(controller);
|
|
88
120
|
}
|
|
89
121
|
|
|
90
|
-
let closed = false;
|
|
91
|
-
|
|
92
122
|
/** @type {string | undefined} */
|
|
93
123
|
let result = undefined;
|
|
94
124
|
|
|
95
125
|
async function cancel() {
|
|
96
126
|
if (closed) return;
|
|
97
127
|
closed = true;
|
|
128
|
+
clearTimeout(keep_alive);
|
|
98
129
|
await generator.return(undefined);
|
|
99
130
|
}
|
|
100
131
|
|
|
@@ -102,6 +133,9 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
102
133
|
|
|
103
134
|
return new Response(
|
|
104
135
|
new ReadableStream({
|
|
136
|
+
start(controller) {
|
|
137
|
+
schedule_keep_alive(controller);
|
|
138
|
+
},
|
|
105
139
|
async pull(controller) {
|
|
106
140
|
if (event.request.signal.aborted) {
|
|
107
141
|
await cancel();
|
|
@@ -137,15 +171,16 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
137
171
|
location: error.location
|
|
138
172
|
});
|
|
139
173
|
} else {
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
174
|
+
const transformed = await handle_error_and_jsonify(
|
|
175
|
+
event,
|
|
176
|
+
state,
|
|
177
|
+
options,
|
|
178
|
+
error
|
|
179
|
+
);
|
|
144
180
|
|
|
145
181
|
send(controller, {
|
|
146
182
|
type: 'error',
|
|
147
|
-
error:
|
|
148
|
-
status
|
|
183
|
+
error: transformed
|
|
149
184
|
});
|
|
150
185
|
}
|
|
151
186
|
}
|
|
@@ -226,7 +261,8 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
226
261
|
/** @type {RemoteFunctionResponse} */ ({
|
|
227
262
|
type: 'result',
|
|
228
263
|
data: stringify(data, transport)
|
|
229
|
-
})
|
|
264
|
+
}),
|
|
265
|
+
{ headers }
|
|
230
266
|
);
|
|
231
267
|
}
|
|
232
268
|
|
|
@@ -275,7 +311,8 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
275
311
|
/** @type {RemoteFunctionResponse} */ ({
|
|
276
312
|
type: 'result',
|
|
277
313
|
data: stringify(data, transport)
|
|
278
|
-
})
|
|
314
|
+
}),
|
|
315
|
+
{ headers }
|
|
279
316
|
);
|
|
280
317
|
} catch (error) {
|
|
281
318
|
if (error instanceof Redirect) {
|
|
@@ -285,23 +322,22 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
|
|
|
285
322
|
/** @type {RemoteFunctionResponse} */ ({
|
|
286
323
|
type: 'result',
|
|
287
324
|
data: stringify(data, transport)
|
|
288
|
-
})
|
|
325
|
+
}),
|
|
326
|
+
{ headers }
|
|
289
327
|
);
|
|
290
328
|
}
|
|
291
329
|
|
|
292
|
-
const
|
|
293
|
-
error instanceof HttpError || error instanceof SvelteKitError ? error.status : 500;
|
|
330
|
+
const transformed = await handle_error_and_jsonify(event, state, options, error);
|
|
294
331
|
|
|
295
332
|
return json(
|
|
296
333
|
/** @type {RemoteFunctionResponse} */ ({
|
|
297
334
|
type: 'error',
|
|
298
|
-
error:
|
|
299
|
-
status
|
|
335
|
+
error: transformed
|
|
300
336
|
}),
|
|
301
337
|
{
|
|
302
338
|
// By setting a non-200 during prerendering we fail the prerender process (unless handleHttpError handles it).
|
|
303
339
|
// Errors at runtime will be passed to the client and are handled there
|
|
304
|
-
status: state.prerendering ? status : undefined,
|
|
340
|
+
status: state.prerendering ? transformed.status : undefined,
|
|
305
341
|
headers: {
|
|
306
342
|
'cache-control': 'private, no-store'
|
|
307
343
|
}
|
|
@@ -322,13 +358,10 @@ export async function collect_remote_data(data, event, state, options) {
|
|
|
322
358
|
/**
|
|
323
359
|
*
|
|
324
360
|
* @param {unknown} error
|
|
325
|
-
* @returns {Promise<
|
|
361
|
+
* @returns {Promise<App.Error>}
|
|
326
362
|
*/
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
error instanceof HttpError || error instanceof SvelteKitError ? error.status : 500;
|
|
330
|
-
|
|
331
|
-
return [status, await handle_error_and_jsonify(event, state, options, error)];
|
|
363
|
+
function convert_error(error) {
|
|
364
|
+
return handle_error_and_jsonify(event, state, options, error);
|
|
332
365
|
}
|
|
333
366
|
|
|
334
367
|
/** @type {Promise<any>[]} */
|
|
@@ -468,9 +501,11 @@ async function handle_remote_form_post_internal(event, state, manifest, id) {
|
|
|
468
501
|
const [hash, name, ...rest] = id.split('/');
|
|
469
502
|
const action_id = rest.join('/');
|
|
470
503
|
const remotes = manifest._.remotes;
|
|
471
|
-
const module = await remotes[hash]
|
|
504
|
+
const module = Object.hasOwn(remotes, hash) ? await remotes[hash]() : undefined;
|
|
472
505
|
|
|
473
|
-
let form = /** @type {RemoteForm<any, any>} */ (
|
|
506
|
+
let form = /** @type {RemoteForm<any, any>} */ (
|
|
507
|
+
module && Object.hasOwn(module.default, name) ? module.default[name] : undefined
|
|
508
|
+
);
|
|
474
509
|
|
|
475
510
|
if (!form) {
|
|
476
511
|
event.setHeaders({
|
|
@@ -480,6 +515,7 @@ async function handle_remote_form_post_internal(event, state, manifest, id) {
|
|
|
480
515
|
});
|
|
481
516
|
return {
|
|
482
517
|
type: 'error',
|
|
518
|
+
// We're lying a bit with the types here; this will be transformed into a proper App.Error object later
|
|
483
519
|
error: new SvelteKitError(
|
|
484
520
|
405,
|
|
485
521
|
'Method Not Allowed',
|
|
@@ -526,6 +562,7 @@ async function handle_remote_form_post_internal(event, state, manifest, id) {
|
|
|
526
562
|
|
|
527
563
|
return {
|
|
528
564
|
type: 'error',
|
|
565
|
+
// @ts-expect-error We're lying a bit with the types here; this will be transformed into a proper App.Error object later
|
|
529
566
|
error: check_incorrect_fail_use(err)
|
|
530
567
|
};
|
|
531
568
|
}
|
|
@@ -8,7 +8,7 @@ import { is_endpoint_request, render_endpoint } from './endpoint.js';
|
|
|
8
8
|
import { render_page } from './page/index.js';
|
|
9
9
|
import { render_response } from './page/render.js';
|
|
10
10
|
import { respond_with_error } from './page/respond_with_error.js';
|
|
11
|
-
import {
|
|
11
|
+
import { get_self_origin, is_csrf_forbidden, is_remote_forbidden } from './csrf.js';
|
|
12
12
|
import {
|
|
13
13
|
handle_fatal_error,
|
|
14
14
|
has_prerendered_path,
|
|
@@ -72,21 +72,26 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
72
72
|
|
|
73
73
|
if (!DEV) {
|
|
74
74
|
const request_origin = request.headers.get('origin');
|
|
75
|
+
const self_origin = get_self_origin(options.paths_origin, url.origin);
|
|
75
76
|
|
|
76
77
|
if (remote_id) {
|
|
77
|
-
if (
|
|
78
|
+
if (
|
|
79
|
+
is_remote_forbidden({
|
|
80
|
+
request,
|
|
81
|
+
request_origin,
|
|
82
|
+
self_origin
|
|
83
|
+
})
|
|
84
|
+
) {
|
|
78
85
|
const message = 'Cross-site remote requests are forbidden';
|
|
79
86
|
return json({ message }, { status: 403 });
|
|
80
87
|
}
|
|
81
88
|
} else if (options.csrf_check_origin) {
|
|
82
|
-
const forbidden =
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
request_origin !== url.origin &&
|
|
89
|
-
(!request_origin || !options.csrf_trusted_origins.includes(request_origin));
|
|
89
|
+
const forbidden = is_csrf_forbidden({
|
|
90
|
+
request,
|
|
91
|
+
request_origin,
|
|
92
|
+
self_origin,
|
|
93
|
+
trusted_origins: options.csrf_trusted_origins
|
|
94
|
+
});
|
|
90
95
|
|
|
91
96
|
if (forbidden) {
|
|
92
97
|
const message = `Cross-site ${request.method} form submissions are forbidden`;
|
|
@@ -319,7 +324,7 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
319
324
|
return resolve_route(resolved_path, new URL(request.url), manifest);
|
|
320
325
|
}
|
|
321
326
|
|
|
322
|
-
if (resolved_path === `/${app_dir}/env.js`
|
|
327
|
+
if (resolved_path === `/${app_dir}/env.js`) {
|
|
323
328
|
return get_public_env(request);
|
|
324
329
|
}
|
|
325
330
|
|
|
@@ -619,54 +624,80 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
619
624
|
invalidated_data_nodes,
|
|
620
625
|
trailing_slash
|
|
621
626
|
);
|
|
622
|
-
} else
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
const node = await manifest._.nodes[route.page.leaf]();
|
|
641
|
-
if (node?.server?.actions) {
|
|
642
|
-
allowed_methods.add('POST');
|
|
627
|
+
} else {
|
|
628
|
+
let endpoint;
|
|
629
|
+
if (
|
|
630
|
+
route.endpoint &&
|
|
631
|
+
(!route.page || (!state.prerendering && is_endpoint_request(event)))
|
|
632
|
+
) {
|
|
633
|
+
endpoint = await route.endpoint();
|
|
634
|
+
|
|
635
|
+
// Prefer rendering the page if the endpoint can't handle this GET or HEAD request
|
|
636
|
+
if (route.page && (method === 'GET' || method === 'HEAD')) {
|
|
637
|
+
const endpoint_can_handle = !!(
|
|
638
|
+
endpoint.GET ||
|
|
639
|
+
endpoint.fallback ||
|
|
640
|
+
(method === 'HEAD' && endpoint.HEAD)
|
|
641
|
+
);
|
|
642
|
+
if (!endpoint_can_handle) {
|
|
643
|
+
endpoint = undefined;
|
|
644
|
+
}
|
|
643
645
|
}
|
|
646
|
+
}
|
|
644
647
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
648
|
+
if (endpoint) {
|
|
649
|
+
response = await render_endpoint(event, event_state, endpoint, state);
|
|
650
|
+
} else if (route.page) {
|
|
651
|
+
if (!page_nodes) {
|
|
652
|
+
throw new Error('page_nodes not found. This should never happen');
|
|
653
|
+
} else if (page_methods.has(method)) {
|
|
654
|
+
response = await render_page(
|
|
655
|
+
event,
|
|
656
|
+
event_state,
|
|
657
|
+
route.page,
|
|
658
|
+
options,
|
|
659
|
+
manifest,
|
|
660
|
+
state,
|
|
661
|
+
page_nodes,
|
|
662
|
+
resolve_opts
|
|
663
|
+
);
|
|
654
664
|
} else {
|
|
655
|
-
const
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
665
|
+
const allowed_methods = new Set(allowed_page_methods);
|
|
666
|
+
const node = await manifest._.nodes[route.page.leaf]();
|
|
667
|
+
if (node?.server?.actions) {
|
|
668
|
+
allowed_methods.add('POST');
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if (method === 'OPTIONS') {
|
|
672
|
+
// This will deny CORS preflight requests implicitly because we don't
|
|
673
|
+
// add the required CORS headers to the response.
|
|
674
|
+
response = new Response(null, {
|
|
675
|
+
status: 204,
|
|
676
|
+
headers: {
|
|
677
|
+
allow: Array.from(allowed_methods.values()).join(', ')
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
} else {
|
|
681
|
+
const mod = [...allowed_methods].reduce((acc, curr) => {
|
|
682
|
+
acc[curr] = true;
|
|
683
|
+
return acc;
|
|
684
|
+
}, /** @type {Record<string, any>} */ ({}));
|
|
685
|
+
response = method_not_allowed(mod, method);
|
|
686
|
+
}
|
|
660
687
|
}
|
|
688
|
+
} else {
|
|
689
|
+
// a route will always have a page or an endpoint, but TypeScript doesn't know that
|
|
690
|
+
throw new Error('Route is neither page nor endpoint. This should never happen');
|
|
661
691
|
}
|
|
662
|
-
} else {
|
|
663
|
-
// a route will always have a page or an endpoint, but TypeScript doesn't know that
|
|
664
|
-
throw new Error('Route is neither page nor endpoint. This should never happen');
|
|
665
692
|
}
|
|
666
693
|
|
|
667
694
|
// If the route contains a page and an endpoint, we need to add a
|
|
668
695
|
// `Vary: Accept` header to the response because of browser caching
|
|
669
|
-
if (
|
|
696
|
+
if (
|
|
697
|
+
(request.method === 'GET' || request.method === 'HEAD') &&
|
|
698
|
+
route.page &&
|
|
699
|
+
route.endpoint
|
|
700
|
+
) {
|
|
670
701
|
const vary = response.headers
|
|
671
702
|
.get('vary')
|
|
672
703
|
?.split(',')
|
|
@@ -75,8 +75,8 @@ export function static_error_page(options, status, message) {
|
|
|
75
75
|
*/
|
|
76
76
|
export async function handle_fatal_error(event, state, options, error) {
|
|
77
77
|
error = error instanceof HttpError ? error : coalesce_to_error(error);
|
|
78
|
-
const status = get_status(error);
|
|
79
78
|
const body = await handle_error_and_jsonify(event, state, options, error);
|
|
79
|
+
const status = body.status;
|
|
80
80
|
|
|
81
81
|
// ideally we'd use sec-fetch-dest instead, but Safari — quelle surprise — doesn't support it
|
|
82
82
|
const type = negotiate(event.request.headers.get('accept') || 'text/html', [
|
|
@@ -113,11 +113,11 @@ export async function handle_error_and_jsonify(event, state, options, error) {
|
|
|
113
113
|
const status = get_status(error);
|
|
114
114
|
const message = get_message(error);
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
);
|
|
116
|
+
const body = (await with_request_store({ event, state }, () =>
|
|
117
|
+
options.hooks.handleError({ error, event, status, message })
|
|
118
|
+
)) ?? { message };
|
|
119
|
+
|
|
120
|
+
return { ...body, status: get_status(body, error) };
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
/**
|
|
@@ -228,7 +228,7 @@ export function clean_up_stack_trace(error) {
|
|
|
228
228
|
return line.replace(/\((.+)(:\d+:\d+)\)$/, (_, file, loc) => `(${relative(file)}${loc})`);
|
|
229
229
|
});
|
|
230
230
|
|
|
231
|
-
// progressive enhancement for people who haven't configured
|
|
231
|
+
// progressive enhancement for people who haven't configured files.src to something else
|
|
232
232
|
const last_line_from_src_code = stack_trace.findLastIndex((line) => /\(src[\\/]/.test(line));
|
|
233
233
|
|
|
234
234
|
if (last_line_from_src_code === -1) {
|
|
@@ -15,7 +15,7 @@ if (__SVELTEKIT_SERVER_TRACING_ENABLED__) {
|
|
|
15
15
|
})
|
|
16
16
|
.catch(() => {
|
|
17
17
|
throw new Error(
|
|
18
|
-
'Tracing is enabled (see `
|
|
18
|
+
'Tracing is enabled (see the SvelteKit plugin `experimental.instrumentation.server` option in your vite.config.js), but `@opentelemetry/api` is not available. This error will likely resolve itself when you set up your tracing instrumentation in `instrumentation.server.js`. For more information, see https://svelte.dev/docs/kit/observability#opentelemetry-api'
|
|
19
19
|
);
|
|
20
20
|
});
|
|
21
21
|
}
|
package/src/types/ambient.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ declare namespace App {
|
|
|
25
25
|
* Defines the common shape of expected and unexpected errors. Expected errors are thrown using the `error` function. Unexpected errors are handled by the `handleError` hooks which should return this shape.
|
|
26
26
|
*/
|
|
27
27
|
export interface Error {
|
|
28
|
+
status: number;
|
|
28
29
|
message: string;
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -56,8 +57,8 @@ declare namespace App {
|
|
|
56
57
|
*/
|
|
57
58
|
declare module '$service-worker' {
|
|
58
59
|
/**
|
|
59
|
-
* The `base` path of the deployment. Typically this is equivalent to `config.
|
|
60
|
-
* Note that there is a `base` but no `assets`, since service workers cannot be used if `config.
|
|
60
|
+
* The `base` path of the deployment. Typically this is equivalent to `config.paths.base`, but it is calculated from `location.pathname` meaning that it will continue to work correctly if the site is deployed to a subdirectory.
|
|
61
|
+
* Note that there is a `base` but no `assets`, since service workers cannot be used if `config.paths.assets` is specified.
|
|
61
62
|
*/
|
|
62
63
|
export const base: string;
|
|
63
64
|
/**
|
|
@@ -66,7 +67,7 @@ declare module '$service-worker' {
|
|
|
66
67
|
*/
|
|
67
68
|
export const build: string[];
|
|
68
69
|
/**
|
|
69
|
-
* An array of URL strings representing the files in your static directory, or whatever directory is specified by `config.
|
|
70
|
+
* An array of URL strings representing the files in your static directory, or whatever directory is specified by `config.files.assets`. You can customize which files are included from `static` directory using [`config.serviceWorker.files`](https://svelte.dev/docs/kit/configuration#serviceWorker)
|
|
70
71
|
*/
|
|
71
72
|
export const files: string[];
|
|
72
73
|
/**
|
|
@@ -75,7 +76,7 @@ declare module '$service-worker' {
|
|
|
75
76
|
*/
|
|
76
77
|
export const prerendered: string[];
|
|
77
78
|
/**
|
|
78
|
-
* See [`config.
|
|
79
|
+
* See [`config.version`](https://svelte.dev/docs/kit/configuration#version). It's useful for generating unique cache names inside your service worker, so that a later deployment of your app can invalidate old caches.
|
|
79
80
|
*/
|
|
80
81
|
export const version: string;
|
|
81
82
|
}
|
|
@@ -15,13 +15,13 @@ declare global {
|
|
|
15
15
|
const __SVELTEKIT_PATHS_ASSETS__: string;
|
|
16
16
|
const __SVELTEKIT_PATHS_BASE__: string;
|
|
17
17
|
const __SVELTEKIT_PATHS_RELATIVE__: boolean;
|
|
18
|
-
/** True if `config.
|
|
18
|
+
/** True if `config.experimental.instrumentation.server` is `true` */
|
|
19
19
|
const __SVELTEKIT_SERVER_TRACING_ENABLED__: boolean;
|
|
20
|
-
/** True if `config.
|
|
20
|
+
/** True if `config.experimental.forkPreloads` is `true` */
|
|
21
21
|
const __SVELTEKIT_FORK_PRELOADS__: boolean;
|
|
22
|
-
/** True if `config.
|
|
22
|
+
/** True if `config.router.resolution === 'client'` */
|
|
23
23
|
const __SVELTEKIT_CLIENT_ROUTING__: boolean;
|
|
24
|
-
/** True if `config.
|
|
24
|
+
/** True if `config.router.type === 'hash'` */
|
|
25
25
|
const __SVELTEKIT_HASH_ROUTING__: boolean;
|
|
26
26
|
/**
|
|
27
27
|
* True if any node in the manifest has a server load function.
|