@sveltejs/kit 3.0.0-next.13 → 3.0.0-next.14
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 +2 -2
- package/src/cli.js +2 -2
- package/src/core/adapt/builder.js +8 -5
- package/src/core/generate_manifest/index.js +6 -0
- package/src/core/postbuild/entities.js +8 -2
- package/src/core/postbuild/fallback.js +2 -1
- package/src/core/postbuild/prerender.js +12 -3
- package/src/core/sync/create_manifest_data/conflict.js +1 -1
- package/src/core/sync/create_manifest_data/index.js +33 -2
- package/src/core/sync/write_app_types.js +72 -27
- package/src/core/sync/write_tsconfig/index.js +2 -1
- package/src/core/sync/write_types/index.js +5 -2
- package/src/exports/node/index.js +2 -7
- package/src/exports/public.d.ts +3 -2
- package/src/exports/vite/index.js +45 -11
- package/src/exports/vite/utils.js +1 -7
- package/src/runtime/app/server/remote/query.js +0 -6
- package/src/runtime/client/client.js +148 -38
- package/src/runtime/client/fetcher.js +2 -13
- package/src/runtime/client/remote-functions/form.svelte.js +3 -7
- package/src/runtime/client/remote-functions/prerender.svelte.js +1 -1
- package/src/runtime/client/remote-functions/query/index.js +1 -1
- package/src/runtime/client/remote-functions/query-batch.svelte.js +1 -1
- package/src/runtime/client/remote-functions/query-live/proxy.js +0 -10
- package/src/runtime/client/remote-functions/shared.svelte.js +1 -1
- package/src/runtime/client/utils.js +1 -0
- package/src/runtime/form-utils.js +4 -6
- package/src/runtime/pathname.js +37 -0
- package/src/runtime/server/fetch.js +5 -8
- package/src/runtime/server/index.js +10 -24
- package/src/runtime/server/page/crypto.js +2 -2
- package/src/runtime/server/page/csp.js +2 -1
- package/src/runtime/server/page/load_data.js +2 -29
- package/src/runtime/server/page/render.js +19 -1
- package/src/runtime/server/page/serialize_data.js +2 -13
- package/src/runtime/server/page/server_routing.js +58 -9
- package/src/runtime/server/respond.js +20 -27
- package/src/runtime/server/state.js +43 -0
- package/src/runtime/shared.js +4 -2
- package/src/runtime/utils.js +3 -0
- package/src/types/ambient-private.d.ts +1 -1
- package/src/types/ambient.d.ts +58 -7
- package/src/types/global-private.d.ts +1 -1
- package/src/types/internal.d.ts +2 -1
- package/src/utils/hash.js +21 -0
- package/src/utils/http.js +8 -7
- package/src/utils/import.js +2 -1
- package/src/utils/params.js +1 -1
- package/src/utils/regex.js +9 -0
- package/src/utils/routing.js +52 -27
- package/src/utils/url.js +1 -0
- package/src/version.js +1 -1
- package/types/index.d.ts +74 -10
- package/types/index.d.ts.map +1 -1
- package/src/exports/node/polyfills.js +0 -30
|
@@ -11,7 +11,11 @@ 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 {
|
|
14
|
+
import {
|
|
15
|
+
add_data_suffix,
|
|
16
|
+
add_resolution_suffix,
|
|
17
|
+
route_id_resolution_pathname
|
|
18
|
+
} from '../../pathname.js';
|
|
15
19
|
import { try_get_request_store, with_request_store } from '@sveltejs/kit/internal/server';
|
|
16
20
|
import { text_encoder } from '../../utils.js';
|
|
17
21
|
import { count_non_ssi_comments, create_replacer, get_global_name } from '../utils.js';
|
|
@@ -390,6 +394,20 @@ export async function render_response({
|
|
|
390
394
|
pathname,
|
|
391
395
|
create_server_routing_response(route, event.params, new URL(pathname, event.url), client)
|
|
392
396
|
);
|
|
397
|
+
|
|
398
|
+
// Prerender a route-ID-keyed `/_app/routes/<id>/__route.js` module alongside the
|
|
399
|
+
// pathname-keyed one above, so that `preloadCode(id)` can resolve a route ID without
|
|
400
|
+
// hitting the server.
|
|
401
|
+
if (route && !state.prerendering.resolved_route_ids.has(route.id)) {
|
|
402
|
+
state.prerendering.resolved_route_ids.add(route.id);
|
|
403
|
+
|
|
404
|
+
const id_pathname = paths.base + route_id_resolution_pathname(paths.app_dir, route.id);
|
|
405
|
+
|
|
406
|
+
state.prerendering.dependencies.set(
|
|
407
|
+
id_pathname,
|
|
408
|
+
create_server_routing_response(route, null, new URL(id_pathname, event.url), client)
|
|
409
|
+
);
|
|
410
|
+
}
|
|
393
411
|
}
|
|
394
412
|
|
|
395
413
|
const blocks = [];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { escape_html } from '../../../utils/escape.js';
|
|
2
|
-
import {
|
|
2
|
+
import { hash_request } from '../../../utils/hash.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Inside a script element, only `</script` and `<!--` hold special meaning to the HTML parser.
|
|
@@ -78,18 +78,7 @@ export function serialize_data(fetched, filter, prerendering = false) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
if (fetched.request_headers || fetched.request_body) {
|
|
81
|
-
|
|
82
|
-
const values = [];
|
|
83
|
-
|
|
84
|
-
if (fetched.request_headers) {
|
|
85
|
-
values.push([...new Headers(fetched.request_headers)].join(','));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (fetched.request_body) {
|
|
89
|
-
values.push(fetched.request_body);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
attrs.push(`data-hash="${hash(...values)}"`);
|
|
81
|
+
attrs.push(`data-hash="${hash_request(fetched.request_headers, fetched.request_body)}"`);
|
|
93
82
|
}
|
|
94
83
|
|
|
95
84
|
// Compute the time the response should be cached, taking into account max-age and age.
|
|
@@ -84,26 +84,75 @@ export async function resolve_route(resolved_path, url, manifest) {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Resolve a route-ID resolution request (`/_app/routes/<id>/__route.js`) to a
|
|
89
|
+
* JS module containing the route's node loaders. Params are always `{}` since
|
|
90
|
+
* this endpoint exists to support `preloadCode(routeId)`, which doesn't need them.
|
|
91
|
+
*
|
|
92
|
+
* The module has one of three shapes, which the client uses to tell three cases apart:
|
|
93
|
+
*
|
|
94
|
+
* - `export const route = {...}` — a page route, with loaders to import
|
|
95
|
+
* - `export const endpoint_only = true` — a real route with no `+page`, so there is
|
|
96
|
+
* nothing to preload, but the client can cache that fact and stop asking
|
|
97
|
+
* - an empty module — no such route
|
|
98
|
+
*
|
|
99
|
+
* @param {string} route_id
|
|
100
|
+
* @param {URL} url
|
|
101
|
+
* @param {SSRManifest} manifest
|
|
102
|
+
* @returns {Response}
|
|
103
|
+
*/
|
|
104
|
+
export function resolve_route_by_id(route_id, url, manifest) {
|
|
105
|
+
if (!manifest._.client?.routes) {
|
|
106
|
+
return text('Server-side route resolution disabled', { status: 400 });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const route = manifest._.client.routes.find((r) => r.id === route_id);
|
|
111
|
+
|
|
112
|
+
if (route) {
|
|
113
|
+
return create_server_routing_response(route, null, url, manifest._.client).response;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// `client.routes` only contains routes with a `+page`, so a miss above doesn't mean the
|
|
117
|
+
// route doesn't exist — it might be a `+server.js`-only route. `_.routes` includes those
|
|
118
|
+
// (with `page: null`), so we can distinguish "exists but has no code" from "unknown".
|
|
119
|
+
if (manifest._.routes.some((r) => r.id === route_id && !r.page)) {
|
|
120
|
+
return text('export const endpoint_only = true;', { headers: js_headers() });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return create_server_routing_response(null, null, url, manifest._.client).response;
|
|
124
|
+
} catch {
|
|
125
|
+
return text('Error resolving route', { status: 500 });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function js_headers() {
|
|
130
|
+
return new Headers({
|
|
131
|
+
'content-type': 'application/javascript; charset=utf-8'
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
87
135
|
/**
|
|
88
136
|
* @param {import('types').SSRClientRoute | null} route
|
|
89
|
-
* @param {Partial<Record<string, string>>} params
|
|
137
|
+
* @param {Partial<Record<string, string>> | null} params
|
|
90
138
|
* @param {URL} url
|
|
91
139
|
* @param {NonNullable<SSRManifest['_']['client']>} client
|
|
92
140
|
* @returns {{response: Response, body: string}}
|
|
93
141
|
*/
|
|
94
142
|
export function create_server_routing_response(route, params, url, client) {
|
|
95
|
-
const headers =
|
|
96
|
-
|
|
97
|
-
});
|
|
143
|
+
const headers = js_headers();
|
|
144
|
+
let body = '';
|
|
98
145
|
|
|
99
146
|
if (route) {
|
|
100
147
|
const csr_route = generate_route_object(route, url, client);
|
|
101
|
-
|
|
148
|
+
body = `${create_css_import(route, url, client)}export const route = ${csr_route};`;
|
|
102
149
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
150
|
+
if (params !== null) {
|
|
151
|
+
body += `\nexport const params = ${JSON.stringify(params)}`;
|
|
152
|
+
}
|
|
106
153
|
}
|
|
154
|
+
|
|
155
|
+
return { response: text(body, { headers }), body };
|
|
107
156
|
}
|
|
108
157
|
|
|
109
158
|
/**
|
|
@@ -132,5 +181,5 @@ function create_css_import(route, url, client) {
|
|
|
132
181
|
|
|
133
182
|
if (!css) return '';
|
|
134
183
|
|
|
135
|
-
return `${create_client_import(client.start, url)}.then(x => x.load_css([${css}]))
|
|
184
|
+
return `${create_client_import(client.start, url)}.then(x => x.load_css([${css}]));\n`;
|
|
136
185
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import {
|
|
1
|
+
/** @import { SSRNode } from 'types' */
|
|
2
2
|
import { DEV } from 'esm-env';
|
|
3
3
|
import { json, text } from '@sveltejs/kit';
|
|
4
4
|
import { Redirect, SvelteKitError } from '@sveltejs/kit/internal';
|
|
@@ -26,13 +26,15 @@ import { validate_server_exports } from '../../utils/exports.js';
|
|
|
26
26
|
import { action_json_redirect, is_action_json_request } from './page/actions.js';
|
|
27
27
|
import { INVALIDATED_PARAM, TRAILING_SLASH_PARAM } from '../shared.js';
|
|
28
28
|
import { get_public_env } from './env_module.js';
|
|
29
|
-
import { resolve_route } from './page/server_routing.js';
|
|
29
|
+
import { resolve_route, resolve_route_by_id } from './page/server_routing.js';
|
|
30
30
|
import { validateHeaders } from './validate-headers.js';
|
|
31
31
|
import {
|
|
32
32
|
add_data_suffix,
|
|
33
33
|
add_resolution_suffix,
|
|
34
|
+
extract_route_id,
|
|
34
35
|
has_data_suffix,
|
|
35
36
|
has_resolution_suffix,
|
|
37
|
+
is_route_id_resolution_path,
|
|
36
38
|
strip_data_suffix,
|
|
37
39
|
strip_resolution_suffix
|
|
38
40
|
} from '../pathname.js';
|
|
@@ -40,6 +42,7 @@ import { server_data_serializer } from './page/data_serializer.js';
|
|
|
40
42
|
import { get_remote_id, handle_remote_call } from './remote-functions.js';
|
|
41
43
|
import { record_span } from '../telemetry/record_span.js';
|
|
42
44
|
import { otel } from '../telemetry/otel.js';
|
|
45
|
+
import { create_request_state } from './state.js';
|
|
43
46
|
|
|
44
47
|
/** @type {import('types').RequiredResolveOptions['transformPageChunk']} */
|
|
45
48
|
const default_transform = ({ html }) => html;
|
|
@@ -138,12 +141,16 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
138
141
|
|
|
139
142
|
let skip_route_resolution = false;
|
|
140
143
|
|
|
144
|
+
/** Whether this is a `/${app_dir}/routes/<route_id>/__route.js` request, used by `preloadCode` */
|
|
145
|
+
let is_route_id_resolution_request = false;
|
|
146
|
+
|
|
141
147
|
if (is_route_resolution_request) {
|
|
142
148
|
/**
|
|
143
149
|
* If the request is for a route resolution, first modify the URL, then continue as normal
|
|
144
150
|
* for path resolution, then return the route object as a JS file.
|
|
145
151
|
*/
|
|
146
152
|
url.pathname = strip_resolution_suffix(url.pathname);
|
|
153
|
+
is_route_id_resolution_request = is_route_id_resolution_path(url.pathname, base, app_dir);
|
|
147
154
|
} else if (is_data_request) {
|
|
148
155
|
url.pathname =
|
|
149
156
|
strip_data_suffix(url.pathname) +
|
|
@@ -174,30 +181,7 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
174
181
|
url
|
|
175
182
|
);
|
|
176
183
|
|
|
177
|
-
|
|
178
|
-
const event_state = {
|
|
179
|
-
prerendering: state.prerendering,
|
|
180
|
-
transport: options.hooks.transport,
|
|
181
|
-
handleValidationError: options.hooks.handleValidationError,
|
|
182
|
-
tracing: {
|
|
183
|
-
record_span
|
|
184
|
-
},
|
|
185
|
-
remote: {
|
|
186
|
-
data: null,
|
|
187
|
-
explicit: null,
|
|
188
|
-
implicit: null,
|
|
189
|
-
forms: null,
|
|
190
|
-
requested: null,
|
|
191
|
-
batches: null,
|
|
192
|
-
live_iterators: null
|
|
193
|
-
},
|
|
194
|
-
is_in_remote_function: false,
|
|
195
|
-
is_in_remote_form_or_command: false,
|
|
196
|
-
is_in_remote_query: false,
|
|
197
|
-
is_in_remote_prerender: false,
|
|
198
|
-
is_in_render: false,
|
|
199
|
-
is_in_universal_load: false
|
|
200
|
-
};
|
|
184
|
+
const event_state = create_request_state(state, options.hooks);
|
|
201
185
|
|
|
202
186
|
/** @type {import('@sveltejs/kit').RequestEvent} */
|
|
203
187
|
const event = {
|
|
@@ -270,7 +254,8 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
270
254
|
/** @type {string | null} */
|
|
271
255
|
let resolved_path = url.pathname;
|
|
272
256
|
|
|
273
|
-
|
|
257
|
+
// `reroute` hooks receive pathnames, so they must not run for route-ID resolution requests
|
|
258
|
+
if (!remote_id && !is_route_id_resolution_request) {
|
|
274
259
|
const prerendering_reroute_state = state.prerendering?.inside_reroute;
|
|
275
260
|
try {
|
|
276
261
|
// For the duration or a reroute, disable the prerendering state as reroute could call API endpoints
|
|
@@ -354,6 +339,14 @@ export async function internal_respond(request, options, manifest, state) {
|
|
|
354
339
|
}
|
|
355
340
|
|
|
356
341
|
if (is_route_resolution_request) {
|
|
342
|
+
if (is_route_id_resolution_request) {
|
|
343
|
+
return resolve_route_by_id(
|
|
344
|
+
extract_route_id(resolved_path, app_dir),
|
|
345
|
+
new URL(request.url),
|
|
346
|
+
manifest
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
357
350
|
return resolve_route(resolved_path, new URL(request.url), manifest);
|
|
358
351
|
}
|
|
359
352
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** @import { RequestState, ServerHooks, SSRState } from 'types' */
|
|
2
|
+
import { record_span } from '../telemetry/record_span.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {SSRState} state
|
|
6
|
+
* @param {ServerHooks} hooks
|
|
7
|
+
* @returns {RequestState}
|
|
8
|
+
*/
|
|
9
|
+
export function create_request_state(state, hooks) {
|
|
10
|
+
// Request state is rebuilt fresh, resetting remote caches and context flags.
|
|
11
|
+
return {
|
|
12
|
+
prerendering: state.prerendering,
|
|
13
|
+
transport: hooks.transport,
|
|
14
|
+
handleValidationError: hooks.handleValidationError,
|
|
15
|
+
tracing: {
|
|
16
|
+
record_span
|
|
17
|
+
},
|
|
18
|
+
remote: {
|
|
19
|
+
data: null,
|
|
20
|
+
explicit: null,
|
|
21
|
+
implicit: null,
|
|
22
|
+
forms: null,
|
|
23
|
+
requested: null,
|
|
24
|
+
batches: null,
|
|
25
|
+
live_iterators: null
|
|
26
|
+
},
|
|
27
|
+
is_in_remote_function: false,
|
|
28
|
+
is_in_remote_form_or_command: false,
|
|
29
|
+
is_in_remote_query: false,
|
|
30
|
+
is_in_remote_prerender: false,
|
|
31
|
+
is_in_render: false,
|
|
32
|
+
is_in_universal_load: false
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {SSRState} state
|
|
38
|
+
* @returns {SSRState}
|
|
39
|
+
*/
|
|
40
|
+
export function fork_state_for_subrequest(state) {
|
|
41
|
+
// Sub-requests inherit all server state except for the incremented depth.
|
|
42
|
+
return { ...state, depth: state.depth + 1 };
|
|
43
|
+
}
|
package/src/runtime/shared.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @import { Transport } from '@sveltejs/kit' */
|
|
2
2
|
import * as devalue from 'devalue';
|
|
3
|
-
import { base64_decode, base64_encode, text_encoder } from './utils.js';
|
|
3
|
+
import { base64_decode, base64_encode, text_decoder, text_encoder } from './utils.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @param {string} route_id
|
|
@@ -331,6 +331,7 @@ export async function stringify_command_arg(value, transport) {
|
|
|
331
331
|
*/
|
|
332
332
|
function url_friendly_base64_encode(string) {
|
|
333
333
|
const bytes = text_encoder.encode(string);
|
|
334
|
+
// TODO replace with `bytes.toBase64({ alphabet: 'base64url', omitPadding: true })` when we require Node >= 25
|
|
334
335
|
return base64_encode(bytes).replaceAll('=', '').replaceAll('+', '-').replaceAll('/', '_');
|
|
335
336
|
}
|
|
336
337
|
|
|
@@ -342,7 +343,8 @@ function url_friendly_base64_encode(string) {
|
|
|
342
343
|
export function parse_remote_arg(string, transport) {
|
|
343
344
|
if (!string) return undefined;
|
|
344
345
|
|
|
345
|
-
const json_string =
|
|
346
|
+
const json_string = text_decoder.decode(
|
|
347
|
+
// TODO replace with `Uint8Array.fromBase64(string, { alphabet: 'base64url' })` when we require Node >= 25
|
|
346
348
|
// no need to add back `=` characters, atob can handle it
|
|
347
349
|
base64_decode(string.replaceAll('-', '+').replaceAll('_', '/'))
|
|
348
350
|
);
|
package/src/runtime/utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BROWSER } from 'esm-env';
|
|
2
2
|
|
|
3
3
|
export const text_encoder = new TextEncoder();
|
|
4
|
+
export const text_decoder = new TextDecoder();
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Like node's path.relative, but without using node
|
|
@@ -28,6 +29,7 @@ export function get_relative_path(from, to) {
|
|
|
28
29
|
* @returns {string}
|
|
29
30
|
*/
|
|
30
31
|
export function base64_encode(bytes) {
|
|
32
|
+
// TODO replace with `bytes.toBase64()` when we require Node >= 25
|
|
31
33
|
// Using `Buffer` is faster than iterating
|
|
32
34
|
if (!BROWSER && globalThis.Buffer) {
|
|
33
35
|
return globalThis.Buffer.from(bytes).toString('base64');
|
|
@@ -47,6 +49,7 @@ export function base64_encode(bytes) {
|
|
|
47
49
|
* @returns {Uint8Array}
|
|
48
50
|
*/
|
|
49
51
|
export function base64_decode(encoded) {
|
|
52
|
+
// TODO replace with `Uint8Array.fromBase64(encoded)` when we require Node >= 25
|
|
50
53
|
// Using `Buffer` is faster than iterating
|
|
51
54
|
if (!BROWSER && globalThis.Buffer) {
|
|
52
55
|
const buffer = globalThis.Buffer.from(encoded, 'base64');
|
|
@@ -51,5 +51,5 @@ declare module '__sveltekit/manifest-data' {
|
|
|
51
51
|
export const immutable: Array<{ path: string }>;
|
|
52
52
|
export const assets: Array<{ path: string }>;
|
|
53
53
|
export const prerendered: Array<{ path: string }>;
|
|
54
|
-
export const routes: Array<{ id: string }>;
|
|
54
|
+
export const routes: Array<{ id: string; page: boolean; endpoint: boolean }>;
|
|
55
55
|
}
|
package/src/types/ambient.d.ts
CHANGED
|
@@ -78,9 +78,41 @@ declare module '$app/manifest' {
|
|
|
78
78
|
*/
|
|
79
79
|
export const prerendered: Array<{ path: import('$app/types').Path }>;
|
|
80
80
|
/**
|
|
81
|
-
*
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
* A route in your app, along with its capabilities. `page` indicates the presence of a `+page`,
|
|
82
|
+
* while `endpoint` indicates the presence of a `+server`. Both are `true` when both files exist.
|
|
83
|
+
*/
|
|
84
|
+
export type ManifestRoute =
|
|
85
|
+
| {
|
|
86
|
+
id: Exclude<import('$app/types').PageRouteId, import('$app/types').EndpointRouteId>;
|
|
87
|
+
page: true;
|
|
88
|
+
endpoint: false;
|
|
89
|
+
}
|
|
90
|
+
| {
|
|
91
|
+
id: Exclude<import('$app/types').EndpointRouteId, import('$app/types').PageRouteId>;
|
|
92
|
+
page: false;
|
|
93
|
+
endpoint: true;
|
|
94
|
+
}
|
|
95
|
+
| {
|
|
96
|
+
id: Extract<import('$app/types').PageRouteId, import('$app/types').EndpointRouteId>;
|
|
97
|
+
page: true;
|
|
98
|
+
endpoint: true;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* An array of objects representing the routes in your app. Only routes that the router can match
|
|
102
|
+
* are included — directories that merely hold a `+layout` are not routes of their own.
|
|
103
|
+
*
|
|
104
|
+
* Each object has an `id`, plus `page` and `endpoint` booleans describing whether the route has a
|
|
105
|
+
* `+page` and/or a `+server`. Both are `true` for a route that has both, so the capabilities can
|
|
106
|
+
* be filtered independently:
|
|
107
|
+
*
|
|
108
|
+
* ```js
|
|
109
|
+
* import { routes } from '$app/manifest';
|
|
110
|
+
*
|
|
111
|
+
* const pages = routes.filter((route) => route.page);
|
|
112
|
+
* const endpoints = routes.filter((route) => route.endpoint);
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export const routes: ManifestRoute[];
|
|
84
116
|
}
|
|
85
117
|
|
|
86
118
|
/**
|
|
@@ -94,6 +126,8 @@ declare module '$app/types' {
|
|
|
94
126
|
// These are all functions so that we can leverage function overloads to get the correct type.
|
|
95
127
|
// Using the return types directly would error with a "not the same type" error.
|
|
96
128
|
// https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-interfaces
|
|
129
|
+
PageRouteId(): string;
|
|
130
|
+
EndpointRouteId(): string;
|
|
97
131
|
RouteId(): string;
|
|
98
132
|
RouteParams(): Record<string, Record<string, string>>;
|
|
99
133
|
LayoutParams(): Record<string, Record<string, string>>;
|
|
@@ -103,7 +137,21 @@ declare module '$app/types' {
|
|
|
103
137
|
}
|
|
104
138
|
|
|
105
139
|
/**
|
|
106
|
-
* A union of
|
|
140
|
+
* A union of the route IDs in your app that have a `+page`.
|
|
141
|
+
*
|
|
142
|
+
* A route ID can be in both `PageRouteId` and `EndpointRouteId`, if its directory contains both a `+page` and a `+server`.
|
|
143
|
+
*/
|
|
144
|
+
export type PageRouteId = ReturnType<AppTypes['PageRouteId']>;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* A union of the route IDs in your app that have a `+server`.
|
|
148
|
+
*
|
|
149
|
+
* A route ID can be in both `PageRouteId` and `EndpointRouteId`, if its directory contains both a `+page` and a `+server`.
|
|
150
|
+
*/
|
|
151
|
+
export type EndpointRouteId = ReturnType<AppTypes['EndpointRouteId']>;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* A union of all the route IDs in your app — the union of `PageRouteId` and `EndpointRouteId`. Used for `page.route.id` and `event.route.id`.
|
|
107
155
|
*/
|
|
108
156
|
export type RouteId = ReturnType<AppTypes['RouteId']>;
|
|
109
157
|
|
|
@@ -119,12 +167,15 @@ declare module '$app/types' {
|
|
|
119
167
|
? ReturnType<AppTypes['RouteParams']>[T]
|
|
120
168
|
: Record<string, never>;
|
|
121
169
|
|
|
170
|
+
/**
|
|
171
|
+
* The route IDs accepted by `LayoutParams`. Like `RouteId`, these preserve route groups and `[param]` syntax, but they identify directories containing layouts rather than matchable routes.
|
|
172
|
+
*/
|
|
173
|
+
type LayoutParamsId = keyof ReturnType<AppTypes['LayoutParams']>;
|
|
174
|
+
|
|
122
175
|
/**
|
|
123
176
|
* A utility for getting the parameters associated with a given layout, which is similar to `RouteParams` but also includes optional parameters for any child route.
|
|
124
177
|
*/
|
|
125
|
-
export type LayoutParams<T extends
|
|
126
|
-
? ReturnType<AppTypes['LayoutParams']>[T]
|
|
127
|
-
: Record<string, never>;
|
|
178
|
+
export type LayoutParams<T extends LayoutParamsId> = ReturnType<AppTypes['LayoutParams']>[T];
|
|
128
179
|
|
|
129
180
|
/**
|
|
130
181
|
* A union of all valid paths in your app, relative to the `base` path.
|
|
@@ -53,7 +53,7 @@ declare global {
|
|
|
53
53
|
const __SVELTEKIT_MANIFEST_IMMUTABLE__: string[];
|
|
54
54
|
const __SVELTEKIT_MANIFEST_ASSETS__: string[];
|
|
55
55
|
const __SVELTEKIT_MANIFEST_PRERENDERED__: string[];
|
|
56
|
-
const __SVELTEKIT_MANIFEST_ROUTES__: { id: string }
|
|
56
|
+
const __SVELTEKIT_MANIFEST_ROUTES__: Array<{ id: string }>;
|
|
57
57
|
/**
|
|
58
58
|
* This makes the use of specific features visible at both dev and build time, in such a
|
|
59
59
|
* way that we can error when they are not supported by the target platform.
|
package/src/types/internal.d.ts
CHANGED
|
@@ -239,6 +239,8 @@ export interface PrerenderOptions {
|
|
|
239
239
|
dependencies: Map<string, PrerenderDependency>;
|
|
240
240
|
/** Results of remote `prerender` functions, shared across the whole prerender run so that each only executes once */
|
|
241
241
|
remote_responses: Map<string, Promise<any>>;
|
|
242
|
+
/** Route IDs whose resolution module has been emitted, shared across the whole prerender run so that each only generates once */
|
|
243
|
+
resolved_route_ids: Set<string>;
|
|
242
244
|
/** True for the duration of a call to the `reroute` hook */
|
|
243
245
|
inside_reroute?: boolean;
|
|
244
246
|
}
|
|
@@ -536,7 +538,6 @@ export interface SSRClientRoute {
|
|
|
536
538
|
}
|
|
537
539
|
|
|
538
540
|
export interface SSRState {
|
|
539
|
-
fallback?: string;
|
|
540
541
|
getClientAddress(): string;
|
|
541
542
|
/**
|
|
542
543
|
* True if we're currently attempting to render an error page.
|
package/src/utils/hash.js
CHANGED
|
@@ -20,3 +20,24 @@ export function hash(...values) {
|
|
|
20
20
|
|
|
21
21
|
return (hash >>> 0).toString(36);
|
|
22
22
|
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Hash of the headers and body a `fetch` was called with. The server-side serializer and the
|
|
26
|
+
* client-side cache lookup must produce identical values for cached responses to be found.
|
|
27
|
+
* @param {HeadersInit | undefined} headers
|
|
28
|
+
* @param {import('types').StrictBody | null | undefined} body
|
|
29
|
+
*/
|
|
30
|
+
export function hash_request(headers, body) {
|
|
31
|
+
/** @type {import('types').StrictBody[]} */
|
|
32
|
+
const values = [];
|
|
33
|
+
|
|
34
|
+
if (headers) {
|
|
35
|
+
values.push([...new Headers(headers)].join(','));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (body) {
|
|
39
|
+
values.push(body);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return hash(...values);
|
|
43
|
+
}
|
package/src/utils/http.js
CHANGED
|
@@ -11,7 +11,7 @@ export function negotiate(accept, types) {
|
|
|
11
11
|
const parts = [];
|
|
12
12
|
|
|
13
13
|
accept.split(',').forEach((str, i) => {
|
|
14
|
-
const match =
|
|
14
|
+
const match = /^[ \t]*([^/ \t]+)\/([^; \t]+)[ \t]*(?:;[ \t]*q=([0-9.]+))?/.exec(str);
|
|
15
15
|
|
|
16
16
|
// no match equals invalid header — ignore
|
|
17
17
|
if (match) {
|
|
@@ -57,12 +57,13 @@ export function negotiate(accept, types) {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
|
-
* Returns `true` if
|
|
61
|
-
*
|
|
60
|
+
* Returns `true` if a `content-type` header value is one of the given types, ignoring
|
|
61
|
+
* parameters such as `charset` and comparing case-insensitively
|
|
62
|
+
* @param {string | null | undefined} header
|
|
62
63
|
* @param {...string} types
|
|
63
64
|
*/
|
|
64
|
-
function
|
|
65
|
-
const type =
|
|
65
|
+
export function matches_content_type(header, ...types) {
|
|
66
|
+
const type = header?.split(';', 1)[0].trim() ?? '';
|
|
66
67
|
return types.includes(type.toLowerCase());
|
|
67
68
|
}
|
|
68
69
|
|
|
@@ -72,8 +73,8 @@ function is_content_type(request, ...types) {
|
|
|
72
73
|
export function is_form_content_type(request) {
|
|
73
74
|
// These content types must be protected against CSRF
|
|
74
75
|
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/enctype
|
|
75
|
-
return
|
|
76
|
-
request,
|
|
76
|
+
return matches_content_type(
|
|
77
|
+
request.headers.get('content-type'),
|
|
77
78
|
'application/x-www-form-urlencoded',
|
|
78
79
|
'multipart/form-data',
|
|
79
80
|
'text/plain',
|
package/src/utils/import.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Resolves a peer dependency relative to the current working directory.
|
|
@@ -51,7 +52,7 @@ function resolve_peer(dependency, root) {
|
|
|
51
52
|
*/
|
|
52
53
|
export async function import_peer(dependency, root) {
|
|
53
54
|
try {
|
|
54
|
-
return await import(/* @vite-ignore */ resolve_peer(dependency, root));
|
|
55
|
+
return await import(/* @vite-ignore */ pathToFileURL(resolve_peer(dependency, root)).href);
|
|
55
56
|
} catch {
|
|
56
57
|
return await import(/* @vite-ignore */ dependency);
|
|
57
58
|
}
|
package/src/utils/params.js
CHANGED
|
@@ -25,7 +25,7 @@ export function collect_matcher_names(routes) {
|
|
|
25
25
|
*/
|
|
26
26
|
export function validate_param_matchers(params, names, file) {
|
|
27
27
|
for (const name of names) {
|
|
28
|
-
if (!(name
|
|
28
|
+
if (!Object.hasOwn(params, name)) {
|
|
29
29
|
throw new Error(`No matcher found for parameter '${name}'${file ? ` in ${file}` : ''}`);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escapes characters that have special meaning in a regular expression.
|
|
3
|
+
* @param {string} str
|
|
4
|
+
* @returns {string} escaped string
|
|
5
|
+
*/
|
|
6
|
+
export function escape_for_regexp(str) {
|
|
7
|
+
// TODO replace with `RegExp.escape(str)` when we require Node >= 24
|
|
8
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, (match) => '\\' + match);
|
|
9
|
+
}
|