@sveltejs/kit 3.0.0-next.4 → 3.0.0-next.7
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 +23 -18
- package/src/core/adapt/builder.js +29 -43
- package/src/core/adapt/index.js +1 -4
- package/src/core/config/index.js +135 -71
- package/src/core/config/options.js +291 -244
- package/src/core/config/types.d.ts +1 -1
- package/src/core/env.js +120 -5
- package/src/core/generate_manifest/index.js +12 -15
- 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 +29 -49
- package/src/core/sync/write_client_manifest.js +14 -14
- package/src/core/sync/write_non_ambient.js +10 -7
- package/src/core/sync/write_root.js +9 -30
- package/src/core/sync/write_server.js +13 -11
- package/src/core/sync/write_tsconfig.js +2 -4
- package/src/core/sync/write_types/index.js +11 -10
- package/src/exports/index.js +34 -12
- package/src/exports/internal/client.js +5 -0
- package/src/exports/internal/env.js +1 -1
- package/src/exports/internal/index.js +1 -90
- package/src/exports/internal/{event.js → server/event.js} +1 -2
- package/src/exports/internal/server/index.js +33 -0
- package/src/exports/internal/shared.js +89 -0
- package/src/exports/node/index.js +58 -7
- package/src/exports/params.js +63 -0
- package/src/exports/public.d.ts +299 -145
- package/src/exports/url.js +84 -0
- package/src/exports/vite/build/build_server.js +6 -1
- package/src/exports/vite/dev/index.js +38 -37
- package/src/exports/vite/index.js +498 -353
- package/src/exports/vite/preview/index.js +16 -8
- package/src/exports/vite/utils.js +7 -11
- 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 +4 -10
- package/src/runtime/app/paths/public.d.ts +0 -28
- package/src/runtime/app/paths/server.js +8 -4
- package/src/runtime/app/server/remote/form.js +10 -3
- package/src/runtime/app/server/remote/query.js +9 -18
- package/src/runtime/app/server/remote/requested.js +8 -4
- package/src/runtime/app/server/remote/shared.js +5 -7
- package/src/runtime/app/state/client.js +1 -2
- package/src/runtime/app/stores.js +13 -76
- package/src/runtime/client/client.js +205 -167
- package/src/runtime/client/fetcher.js +3 -2
- package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
- package/src/runtime/client/remote-functions/form.svelte.js +122 -31
- package/src/runtime/client/remote-functions/prerender.svelte.js +17 -4
- package/src/runtime/client/remote-functions/query/index.js +3 -2
- package/src/runtime/client/remote-functions/query/instance.svelte.js +27 -11
- package/src/runtime/client/remote-functions/query-batch.svelte.js +4 -3
- package/src/runtime/client/remote-functions/query-live/instance.svelte.js +42 -15
- package/src/runtime/client/remote-functions/shared.svelte.js +18 -9
- package/src/runtime/client/state.svelte.js +66 -49
- package/src/runtime/client/types.d.ts +10 -2
- package/src/runtime/client/utils.js +0 -96
- package/src/runtime/form-utils.js +99 -18
- 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 +2 -2
- package/src/runtime/server/page/actions.js +41 -26
- package/src/runtime/server/page/index.js +2 -1
- package/src/runtime/server/page/load_data.js +1 -1
- package/src/runtime/server/page/render.js +36 -47
- package/src/runtime/server/page/respond_with_error.js +7 -8
- package/src/runtime/server/page/server_routing.js +13 -9
- package/src/runtime/server/remote.js +85 -39
- package/src/runtime/server/respond.js +92 -58
- package/src/runtime/server/utils.js +7 -7
- package/src/runtime/telemetry/otel.js +1 -1
- package/src/types/ambient.d.ts +10 -5
- package/src/types/global-private.d.ts +4 -4
- package/src/types/internal.d.ts +17 -20
- package/src/types/private.d.ts +33 -1
- package/src/types/synthetic/$lib.md +1 -1
- package/src/utils/error.js +12 -4
- package/src/utils/mime.js +9 -0
- package/src/utils/params.js +66 -0
- package/src/utils/routing.js +84 -38
- package/src/utils/shared-iterator.js +5 -0
- package/src/utils/streaming.js +14 -4
- package/src/utils/url.js +20 -2
- package/src/version.js +1 -1
- package/types/index.d.ts +434 -269
- package/types/index.d.ts.map +19 -15
- package/src/exports/internal/server.js +0 -22
- /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
|
@@ -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
|
}
|
|
@@ -69,15 +69,19 @@ export async function resolve_route(resolved_path, url, manifest) {
|
|
|
69
69
|
return text('Server-side route resolution disabled', { status: 400 });
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
72
|
+
try {
|
|
73
|
+
const matchers = await manifest._.matchers();
|
|
74
|
+
const result = find_route(resolved_path, manifest._.client.routes, matchers);
|
|
75
|
+
|
|
76
|
+
return create_server_routing_response(
|
|
77
|
+
result?.route ?? null,
|
|
78
|
+
result?.params ?? {},
|
|
79
|
+
url,
|
|
80
|
+
manifest._.client
|
|
81
|
+
).response;
|
|
82
|
+
} catch {
|
|
83
|
+
return text('Error resolving route', { status: 500 });
|
|
84
|
+
}
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
/**
|
|
@@ -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,20 +358,17 @@ 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>[]} */
|
|
335
368
|
const promises = [];
|
|
336
369
|
|
|
337
370
|
if (state.remote.explicit) {
|
|
338
|
-
for (const [remote_key, { internals,
|
|
371
|
+
for (const [remote_key, { internals, fn }] of state.remote.explicit) {
|
|
339
372
|
// there were explicit refreshes/reconnects (via `refresh()`/`set()`/`reconnect()`),
|
|
340
373
|
// so the client should apply these single-flight updates instead of calling `invalidateAll()`
|
|
341
374
|
data.r = true;
|
|
@@ -344,18 +377,27 @@ export async function collect_remote_data(data, event, state, options) {
|
|
|
344
377
|
internals.type === 'query_live' ? 'l' : internals.type[0]
|
|
345
378
|
);
|
|
346
379
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
380
|
+
// `fn` is deferred until now so the query runs after any state mutations
|
|
381
|
+
// in the command/form body. If the query was re-awaited in the meantime,
|
|
382
|
+
// `fn` returns the existing (fresh) cache entry rather than re-running.
|
|
383
|
+
// Kick off the query immediately and collect the promise so that multiple
|
|
384
|
+
// explicit refreshes run concurrently rather than serially.
|
|
385
|
+
const promise = fn();
|
|
386
|
+
|
|
387
|
+
promises.push(
|
|
388
|
+
promise.then(
|
|
389
|
+
(v) => {
|
|
390
|
+
((data[type] ??= {})[remote_key] ??= {}).v = v;
|
|
391
|
+
},
|
|
392
|
+
async (e) => {
|
|
393
|
+
if (e instanceof Redirect) {
|
|
394
|
+
// already handled elsewhere
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
356
397
|
|
|
357
|
-
|
|
358
|
-
|
|
398
|
+
((data[type] ??= {})[remote_key] ??= {}).e = await convert_error(e);
|
|
399
|
+
}
|
|
400
|
+
)
|
|
359
401
|
);
|
|
360
402
|
}
|
|
361
403
|
}
|
|
@@ -468,9 +510,11 @@ async function handle_remote_form_post_internal(event, state, manifest, id) {
|
|
|
468
510
|
const [hash, name, ...rest] = id.split('/');
|
|
469
511
|
const action_id = rest.join('/');
|
|
470
512
|
const remotes = manifest._.remotes;
|
|
471
|
-
const module = await remotes[hash]
|
|
513
|
+
const module = Object.hasOwn(remotes, hash) ? await remotes[hash]() : undefined;
|
|
472
514
|
|
|
473
|
-
let form = /** @type {RemoteForm<any, any>} */ (
|
|
515
|
+
let form = /** @type {RemoteForm<any, any>} */ (
|
|
516
|
+
module && Object.hasOwn(module.default, name) ? module.default[name] : undefined
|
|
517
|
+
);
|
|
474
518
|
|
|
475
519
|
if (!form) {
|
|
476
520
|
event.setHeaders({
|
|
@@ -480,6 +524,7 @@ async function handle_remote_form_post_internal(event, state, manifest, id) {
|
|
|
480
524
|
});
|
|
481
525
|
return {
|
|
482
526
|
type: 'error',
|
|
527
|
+
// We're lying a bit with the types here; this will be transformed into a proper App.Error object later
|
|
483
528
|
error: new SvelteKitError(
|
|
484
529
|
405,
|
|
485
530
|
'Method Not Allowed',
|
|
@@ -526,6 +571,7 @@ async function handle_remote_form_post_internal(event, state, manifest, id) {
|
|
|
526
571
|
|
|
527
572
|
return {
|
|
528
573
|
type: 'error',
|
|
574
|
+
// @ts-expect-error We're lying a bit with the types here; this will be transformed into a proper App.Error object later
|
|
529
575
|
error: check_incorrect_fail_use(err)
|
|
530
576
|
};
|
|
531
577
|
}
|
|
@@ -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
|
|
|
@@ -331,14 +336,17 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
331
336
|
}
|
|
332
337
|
|
|
333
338
|
if (!state.prerendering?.fallback) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
339
|
+
try {
|
|
340
|
+
const matchers = await manifest._.matchers();
|
|
341
|
+
const result = find_route(resolved_path, manifest._.routes, matchers);
|
|
342
|
+
|
|
343
|
+
if (result) {
|
|
344
|
+
route = result.route;
|
|
345
|
+
event.route = { id: route.id };
|
|
346
|
+
event.params = result.params;
|
|
347
|
+
}
|
|
348
|
+
} catch (e) {
|
|
349
|
+
return await handle_fatal_error(event, event_state, options, e);
|
|
342
350
|
}
|
|
343
351
|
}
|
|
344
352
|
|
|
@@ -619,54 +627,80 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
619
627
|
invalidated_data_nodes,
|
|
620
628
|
trailing_slash
|
|
621
629
|
);
|
|
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');
|
|
630
|
+
} else {
|
|
631
|
+
let endpoint;
|
|
632
|
+
if (
|
|
633
|
+
route.endpoint &&
|
|
634
|
+
(!route.page || (!state.prerendering && is_endpoint_request(event)))
|
|
635
|
+
) {
|
|
636
|
+
endpoint = await route.endpoint();
|
|
637
|
+
|
|
638
|
+
// Prefer rendering the page if the endpoint can't handle this GET or HEAD request
|
|
639
|
+
if (route.page && (method === 'GET' || method === 'HEAD')) {
|
|
640
|
+
const endpoint_can_handle = !!(
|
|
641
|
+
endpoint.GET ||
|
|
642
|
+
endpoint.fallback ||
|
|
643
|
+
(method === 'HEAD' && endpoint.HEAD)
|
|
644
|
+
);
|
|
645
|
+
if (!endpoint_can_handle) {
|
|
646
|
+
endpoint = undefined;
|
|
647
|
+
}
|
|
643
648
|
}
|
|
649
|
+
}
|
|
644
650
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
651
|
+
if (endpoint) {
|
|
652
|
+
response = await render_endpoint(event, event_state, endpoint, state);
|
|
653
|
+
} else if (route.page) {
|
|
654
|
+
if (!page_nodes) {
|
|
655
|
+
throw new Error('page_nodes not found. This should never happen');
|
|
656
|
+
} else if (page_methods.has(method)) {
|
|
657
|
+
response = await render_page(
|
|
658
|
+
event,
|
|
659
|
+
event_state,
|
|
660
|
+
route.page,
|
|
661
|
+
options,
|
|
662
|
+
manifest,
|
|
663
|
+
state,
|
|
664
|
+
page_nodes,
|
|
665
|
+
resolve_opts
|
|
666
|
+
);
|
|
654
667
|
} else {
|
|
655
|
-
const
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
668
|
+
const allowed_methods = new Set(allowed_page_methods);
|
|
669
|
+
const node = await manifest._.nodes[route.page.leaf]();
|
|
670
|
+
if (node?.server?.actions) {
|
|
671
|
+
allowed_methods.add('POST');
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (method === 'OPTIONS') {
|
|
675
|
+
// This will deny CORS preflight requests implicitly because we don't
|
|
676
|
+
// add the required CORS headers to the response.
|
|
677
|
+
response = new Response(null, {
|
|
678
|
+
status: 204,
|
|
679
|
+
headers: {
|
|
680
|
+
allow: Array.from(allowed_methods.values()).join(', ')
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
} else {
|
|
684
|
+
const mod = [...allowed_methods].reduce((acc, curr) => {
|
|
685
|
+
acc[curr] = true;
|
|
686
|
+
return acc;
|
|
687
|
+
}, /** @type {Record<string, any>} */ ({}));
|
|
688
|
+
response = method_not_allowed(mod, method);
|
|
689
|
+
}
|
|
660
690
|
}
|
|
691
|
+
} else {
|
|
692
|
+
// a route will always have a page or an endpoint, but TypeScript doesn't know that
|
|
693
|
+
throw new Error('Route is neither page nor endpoint. This should never happen');
|
|
661
694
|
}
|
|
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
695
|
}
|
|
666
696
|
|
|
667
697
|
// If the route contains a page and an endpoint, we need to add a
|
|
668
698
|
// `Vary: Accept` header to the response because of browser caching
|
|
669
|
-
if (
|
|
699
|
+
if (
|
|
700
|
+
(request.method === 'GET' || request.method === 'HEAD') &&
|
|
701
|
+
route.page &&
|
|
702
|
+
route.endpoint
|
|
703
|
+
) {
|
|
670
704
|
const vary = response.headers
|
|
671
705
|
.get('vary')
|
|
672
706
|
?.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 `tracing.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,29 +25,34 @@ 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
|
|
|
31
32
|
/**
|
|
32
33
|
* The interface that defines `event.locals`, which can be accessed in server [hooks](https://svelte.dev/docs/kit/hooks) (`handle`, and `handleError`), server-only `load` functions, and `+server.js` files.
|
|
33
34
|
*/
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
34
36
|
export interface Locals {}
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
|
-
* Defines the common shape of the [page.data state](https://svelte.dev/docs/kit/$app-state#page)
|
|
39
|
+
* Defines the common shape of the [page.data state](https://svelte.dev/docs/kit/$app-state#page) - that is, the data that is shared between all pages.
|
|
38
40
|
* The `Load` and `ServerLoad` functions in `./$types` will be narrowed accordingly.
|
|
39
41
|
* Use optional properties for data that is only present on specific pages. Do not add an index signature (`[key: string]: any`).
|
|
40
42
|
*/
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
41
44
|
export interface PageData {}
|
|
42
45
|
|
|
43
46
|
/**
|
|
44
47
|
* The shape of the `page.state` object, which can be manipulated using the [`pushState`](https://svelte.dev/docs/kit/$app-navigation#pushState) and [`replaceState`](https://svelte.dev/docs/kit/$app-navigation#replaceState) functions from `$app/navigation`.
|
|
45
48
|
*/
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
46
50
|
export interface PageState {}
|
|
47
51
|
|
|
48
52
|
/**
|
|
49
53
|
* If your adapter provides [platform-specific context](https://svelte.dev/docs/kit/adapters#Platform-specific-context) via `event.platform`, you can specify it here.
|
|
50
54
|
*/
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
51
56
|
export interface Platform {}
|
|
52
57
|
}
|
|
53
58
|
|
|
@@ -56,8 +61,8 @@ declare namespace App {
|
|
|
56
61
|
*/
|
|
57
62
|
declare module '$service-worker' {
|
|
58
63
|
/**
|
|
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.
|
|
64
|
+
* 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.
|
|
65
|
+
* Note that there is a `base` but no `assets`, since service workers cannot be used if `config.paths.assets` is specified.
|
|
61
66
|
*/
|
|
62
67
|
export const base: string;
|
|
63
68
|
/**
|
|
@@ -66,7 +71,7 @@ declare module '$service-worker' {
|
|
|
66
71
|
*/
|
|
67
72
|
export const build: string[];
|
|
68
73
|
/**
|
|
69
|
-
* An array of URL strings representing the files in your static directory, or whatever directory is specified by `config.
|
|
74
|
+
* 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
75
|
*/
|
|
71
76
|
export const files: string[];
|
|
72
77
|
/**
|
|
@@ -75,7 +80,7 @@ declare module '$service-worker' {
|
|
|
75
80
|
*/
|
|
76
81
|
export const prerendered: string[];
|
|
77
82
|
/**
|
|
78
|
-
* See [`config.
|
|
83
|
+
* 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
84
|
*/
|
|
80
85
|
export const version: string;
|
|
81
86
|
}
|
|
@@ -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.tracing.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.
|