@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
package/src/types/internal.d.ts
CHANGED
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
RequestEvent,
|
|
18
18
|
SSRManifest,
|
|
19
19
|
Emulator,
|
|
20
|
-
Adapter,
|
|
21
20
|
ServerInit,
|
|
22
21
|
ClientInit,
|
|
23
22
|
Transport,
|
|
@@ -44,7 +43,6 @@ export interface ServerModule {
|
|
|
44
43
|
export interface ServerInternalModule {
|
|
45
44
|
set_assets(path: string): void;
|
|
46
45
|
set_building(): void;
|
|
47
|
-
set_env(environment: Record<string, string>): void;
|
|
48
46
|
set_manifest(manifest: SSRManifest): void;
|
|
49
47
|
set_prerendering(): void;
|
|
50
48
|
set_read_implementation(implementation: (path: string) => ReadableStream): void;
|
|
@@ -101,6 +99,9 @@ export interface BuildData {
|
|
|
101
99
|
routes?: SSRClientRoute[];
|
|
102
100
|
stylesheets: string[];
|
|
103
101
|
fonts: string[];
|
|
102
|
+
/**
|
|
103
|
+
* Whether the client uses public dynamic env vars — `$env/dynamic/public` or `$app/env/public`.
|
|
104
|
+
*/
|
|
104
105
|
uses_env_dynamic_public: boolean;
|
|
105
106
|
/** Only set in case of `bundleStrategy === 'inline'`. */
|
|
106
107
|
inline?: {
|
|
@@ -194,7 +195,7 @@ export class InternalServer extends Server {
|
|
|
194
195
|
}
|
|
195
196
|
|
|
196
197
|
export interface ManifestData {
|
|
197
|
-
/** Static files from `
|
|
198
|
+
/** Static files from `config.files.assets`. */
|
|
198
199
|
assets: Asset[];
|
|
199
200
|
hooks: {
|
|
200
201
|
client: string | null;
|
|
@@ -203,7 +204,7 @@ export interface ManifestData {
|
|
|
203
204
|
};
|
|
204
205
|
nodes: PageNode[];
|
|
205
206
|
routes: RouteData[];
|
|
206
|
-
|
|
207
|
+
params: string | null;
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
export interface RemoteChunk {
|
|
@@ -246,7 +247,7 @@ export type RecursiveRequired<T> = {
|
|
|
246
247
|
// Recursive implementation of TypeScript's Required utility type.
|
|
247
248
|
// Will recursively continue until it reaches a primitive or Function
|
|
248
249
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
249
|
-
[K in keyof T]-?: Extract<T[K], Function> extends never // If it does not have a Function type
|
|
250
|
+
[K in keyof T]-?: Extract<T[K], Function | (`${string}:` & {})> extends never // If it does not have a Function type
|
|
250
251
|
? RecursiveRequired<T[K]> // recursively continue through.
|
|
251
252
|
: T[K]; // Use the exact type for everything else
|
|
252
253
|
};
|
|
@@ -293,6 +294,7 @@ export interface RouteData {
|
|
|
293
294
|
|
|
294
295
|
export type ServerRedirectNode = {
|
|
295
296
|
type: 'redirect';
|
|
297
|
+
status: number;
|
|
296
298
|
location: string;
|
|
297
299
|
};
|
|
298
300
|
|
|
@@ -308,7 +310,7 @@ export type RemoteFunctionDataNode = {
|
|
|
308
310
|
/** value */
|
|
309
311
|
v?: any;
|
|
310
312
|
/** error */
|
|
311
|
-
e?:
|
|
313
|
+
e?: App.Error;
|
|
312
314
|
};
|
|
313
315
|
|
|
314
316
|
export type RemoteFunctionData = {
|
|
@@ -389,10 +391,6 @@ export interface ServerDataSkippedNode {
|
|
|
389
391
|
export interface ServerErrorNode {
|
|
390
392
|
type: 'error';
|
|
391
393
|
error: App.Error;
|
|
392
|
-
/**
|
|
393
|
-
* Only set for HttpErrors.
|
|
394
|
-
*/
|
|
395
|
-
status?: number;
|
|
396
394
|
}
|
|
397
395
|
|
|
398
396
|
export interface ServerMetadataRoute {
|
|
@@ -419,23 +417,23 @@ export interface ServerMetadata {
|
|
|
419
417
|
remotes: Map<string, Map<string, { type: RemoteInternals['type']; dynamic: boolean }>>;
|
|
420
418
|
}
|
|
421
419
|
|
|
420
|
+
// TODO get rid of this in favor us using just import('svelte').Component<any, any, any>
|
|
422
421
|
export interface SSRComponent {
|
|
423
422
|
default: {
|
|
424
423
|
render(
|
|
425
424
|
props: Record<string, any>,
|
|
426
425
|
opts: { context: Map<any, any>; csp?: { nonce?: string; hash?: boolean } }
|
|
427
|
-
): {
|
|
428
|
-
|
|
426
|
+
): Promise<{
|
|
427
|
+
body: string;
|
|
429
428
|
head: string;
|
|
430
429
|
css: {
|
|
431
430
|
code: string;
|
|
432
431
|
map: any; // TODO
|
|
433
432
|
};
|
|
434
|
-
|
|
435
|
-
hashes?: {
|
|
433
|
+
hashes: {
|
|
436
434
|
script: Array<`sha256-${string}`>;
|
|
437
435
|
};
|
|
438
|
-
}
|
|
436
|
+
}>;
|
|
439
437
|
};
|
|
440
438
|
}
|
|
441
439
|
|
|
@@ -495,7 +493,6 @@ export type SSRNodeLoader = () => Promise<SSRNode>;
|
|
|
495
493
|
|
|
496
494
|
export interface SSROptions {
|
|
497
495
|
app_template_contains_nonce: boolean;
|
|
498
|
-
async: boolean;
|
|
499
496
|
csp: ValidatedConfig['kit']['csp'];
|
|
500
497
|
csrf_check_origin: boolean;
|
|
501
498
|
csrf_trusted_origins: string[];
|
|
@@ -503,6 +500,7 @@ export interface SSROptions {
|
|
|
503
500
|
hash_routing: boolean;
|
|
504
501
|
hooks: ServerHooks;
|
|
505
502
|
link_header_preload: ValidatedConfig['kit']['output']['linkHeaderPreload'];
|
|
503
|
+
paths_origin: string | undefined;
|
|
506
504
|
root: SSRComponent['default'];
|
|
507
505
|
service_worker: boolean;
|
|
508
506
|
service_worker_options: RegistrationOptions;
|
|
@@ -599,13 +597,12 @@ export interface Uses {
|
|
|
599
597
|
search_params: Set<string>;
|
|
600
598
|
}
|
|
601
599
|
|
|
602
|
-
export type ValidatedConfig = Config & {
|
|
600
|
+
export type ValidatedConfig = Omit<Config, 'kit'> & {
|
|
603
601
|
kit: ValidatedKitConfig;
|
|
604
602
|
extensions: string[];
|
|
605
603
|
};
|
|
606
604
|
|
|
607
|
-
|
|
608
|
-
export type ValidatedKitConfig = Omit<RecursiveRequired<KitConfig>, 'adapter'>;
|
|
605
|
+
export type ValidatedKitConfig = RecursiveRequired<KitConfig>;
|
|
609
606
|
|
|
610
607
|
export type BinaryFormMeta = {
|
|
611
608
|
remote_refreshes?: string[];
|
|
@@ -729,7 +726,7 @@ export interface RequestState {
|
|
|
729
726
|
string,
|
|
730
727
|
{
|
|
731
728
|
internals: RemoteInternals;
|
|
732
|
-
|
|
729
|
+
fn: () => Promise<any>;
|
|
733
730
|
}
|
|
734
731
|
>;
|
|
735
732
|
/** Instances created via `myForm.for(...)` */
|
package/src/types/private.d.ts
CHANGED
|
@@ -61,7 +61,10 @@ export namespace Csp {
|
|
|
61
61
|
| 'unsafe-eval'
|
|
62
62
|
| 'unsafe-hashes'
|
|
63
63
|
| 'unsafe-inline'
|
|
64
|
+
| 'unsafe-allow-redirects'
|
|
65
|
+
| 'unsafe-webtransport-hashes'
|
|
64
66
|
| 'wasm-unsafe-eval'
|
|
67
|
+
| 'trusted-types-eval'
|
|
65
68
|
| 'none';
|
|
66
69
|
type CryptoSource = `${'nonce' | 'sha256' | 'sha384' | 'sha512'}-${string}`;
|
|
67
70
|
type FrameSource = HostSource | SchemeSource | 'self' | 'none';
|
|
@@ -70,7 +73,16 @@ export namespace Csp {
|
|
|
70
73
|
type HostProtocolSchemes = `${string}://` | '';
|
|
71
74
|
type HttpDelineator = '/' | '?' | '#' | '\\';
|
|
72
75
|
type PortScheme = `:${number}` | '' | ':*';
|
|
73
|
-
type SchemeSource =
|
|
76
|
+
type SchemeSource =
|
|
77
|
+
| 'http:'
|
|
78
|
+
| 'https:'
|
|
79
|
+
| 'ws:'
|
|
80
|
+
| 'wss:'
|
|
81
|
+
| 'data:'
|
|
82
|
+
| 'mediastream:'
|
|
83
|
+
| 'blob:'
|
|
84
|
+
| 'filesystem:'
|
|
85
|
+
| (`${string}:` & {});
|
|
74
86
|
type Source = HostSource | SchemeSource | CryptoSource | BaseSource;
|
|
75
87
|
type Sources = Source[];
|
|
76
88
|
}
|
|
@@ -212,6 +224,10 @@ export interface PrerenderUnseenRoutesHandler {
|
|
|
212
224
|
(details: { routes: string[]; message: string }): void;
|
|
213
225
|
}
|
|
214
226
|
|
|
227
|
+
export interface PrerenderInvalidUrlHandler {
|
|
228
|
+
(details: { href: string; referrer: string | null; message: string }): void;
|
|
229
|
+
}
|
|
230
|
+
|
|
215
231
|
export type PrerenderHttpErrorHandlerValue = 'fail' | 'warn' | 'ignore' | PrerenderHttpErrorHandler;
|
|
216
232
|
export type PrerenderMissingIdHandlerValue = 'fail' | 'warn' | 'ignore' | PrerenderMissingIdHandler;
|
|
217
233
|
export type PrerenderUnseenRoutesHandlerValue =
|
|
@@ -224,6 +240,11 @@ export type PrerenderEntryGeneratorMismatchHandlerValue =
|
|
|
224
240
|
| 'warn'
|
|
225
241
|
| 'ignore'
|
|
226
242
|
| PrerenderEntryGeneratorMismatchHandler;
|
|
243
|
+
export type PrerenderInvalidUrlHandlerValue =
|
|
244
|
+
| 'fail'
|
|
245
|
+
| 'warn'
|
|
246
|
+
| 'ignore'
|
|
247
|
+
| PrerenderInvalidUrlHandler;
|
|
227
248
|
|
|
228
249
|
export type PrerenderOption = boolean | 'auto';
|
|
229
250
|
|
|
@@ -252,3 +273,14 @@ export type DeepPartial<T> = T extends Record<PropertyKey, unknown> | unknown[]
|
|
|
252
273
|
: T | undefined;
|
|
253
274
|
|
|
254
275
|
export type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
276
|
+
|
|
277
|
+
export type HasNonOptionalBoolean<T> =
|
|
278
|
+
IsAny<T> extends true
|
|
279
|
+
? never
|
|
280
|
+
: [T] extends [boolean]
|
|
281
|
+
? true
|
|
282
|
+
: T extends Array<infer U>
|
|
283
|
+
? HasNonOptionalBoolean<U>
|
|
284
|
+
: T extends Record<string, any>
|
|
285
|
+
? { [K in keyof T]: HasNonOptionalBoolean<T[K]> }[keyof T]
|
|
286
|
+
: never;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
This is a simple alias to `src/lib`, or whatever directory is specified as [`config.
|
|
1
|
+
This is a simple alias to `src/lib`, or whatever directory is specified as [`config.files.lib`](https://svelte.dev/docs/kit/configuration#files). It allows you to access common components and utility modules without `../../../../` nonsense.
|
|
2
2
|
|
|
3
3
|
### `$lib/server`
|
|
4
4
|
|
package/src/utils/error.js
CHANGED
|
@@ -30,16 +30,24 @@ export function coalesce_to_error(err) {
|
|
|
30
30
|
* @param {unknown} error
|
|
31
31
|
*/
|
|
32
32
|
export function normalize_error(error) {
|
|
33
|
-
return /** @type {import('../exports/internal/
|
|
33
|
+
return /** @type {import('../exports/internal/shared.js').Redirect | HttpError | SvelteKitError | Error} */ (
|
|
34
34
|
error
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
* @param {
|
|
39
|
+
* @param {any} transformed
|
|
40
|
+
* @param {any} [error]
|
|
40
41
|
*/
|
|
41
|
-
export function get_status(error) {
|
|
42
|
-
|
|
42
|
+
export function get_status(transformed, error) {
|
|
43
|
+
const err = error ?? transformed;
|
|
44
|
+
const status = err instanceof HttpError || err instanceof SvelteKitError ? err.status : 500;
|
|
45
|
+
|
|
46
|
+
if (error == null || typeof transformed?.status !== 'number') {
|
|
47
|
+
return status;
|
|
48
|
+
} else {
|
|
49
|
+
return transformed.status;
|
|
50
|
+
}
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
/**
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { mimes, lookup } from 'mrmime';
|
|
2
|
+
|
|
3
|
+
// `mrmime` does not include `.ico` in its database. The IANA-registered
|
|
4
|
+
// type is `image/vnd.microsoft.icon`, but `image/x-icon` is the
|
|
5
|
+
// semi-official type that is universally supported by browsers and other
|
|
6
|
+
// tools, so we use that.
|
|
7
|
+
mimes['ico'] = 'image/x-icon';
|
|
8
|
+
|
|
9
|
+
export { lookup };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {import('types').RouteData[]} routes
|
|
6
|
+
* @returns {Set<string>}
|
|
7
|
+
*/
|
|
8
|
+
export function collect_matcher_names(routes) {
|
|
9
|
+
/** @type {Set<string>} */
|
|
10
|
+
const names = new Set();
|
|
11
|
+
|
|
12
|
+
for (const route of routes) {
|
|
13
|
+
for (const param of route.params) {
|
|
14
|
+
if (param.matcher) names.add(param.matcher);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return names;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {Record<string, unknown>} params
|
|
23
|
+
* @param {Set<string>} names
|
|
24
|
+
* @param {string} [file]
|
|
25
|
+
*/
|
|
26
|
+
export function validate_param_matchers(params, names, file) {
|
|
27
|
+
for (const name of names) {
|
|
28
|
+
if (!(name in params)) {
|
|
29
|
+
throw new Error(`No matcher found for parameter '${name}'${file ? ` in ${file}` : ''}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {{
|
|
36
|
+
* routes: import('types').RouteData[];
|
|
37
|
+
* params_path: string | null;
|
|
38
|
+
* root: string;
|
|
39
|
+
* load?: (file: string) => Promise<Record<string, unknown>>;
|
|
40
|
+
* }} opts
|
|
41
|
+
* @returns {Promise<Record<string, import('@sveltejs/kit').ParamMatcher> | null>}
|
|
42
|
+
*/
|
|
43
|
+
export async function load_and_validate_params({ routes, params_path, root, load }) {
|
|
44
|
+
const names = collect_matcher_names(routes);
|
|
45
|
+
|
|
46
|
+
if (names.size === 0) return null;
|
|
47
|
+
|
|
48
|
+
if (!params_path) {
|
|
49
|
+
throw new Error(`No matcher found for parameter '${names.values().next().value}'`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const file = path.resolve(root, params_path);
|
|
53
|
+
const module = load ? await load(file) : await import(pathToFileURL(file).href);
|
|
54
|
+
|
|
55
|
+
if (!module.params || typeof module.params !== 'object') {
|
|
56
|
+
throw new Error(`${params_path} does not export \`params\` from \`defineParams\``);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
validate_param_matchers(
|
|
60
|
+
/** @type {Record<string, unknown>} */ (module.params),
|
|
61
|
+
names,
|
|
62
|
+
params_path
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
return /** @type {Record<string, import('@sveltejs/kit').ParamMatcher>} */ (module.params);
|
|
66
|
+
}
|
package/src/utils/routing.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { BROWSER } from 'esm-env';
|
|
2
|
-
import { decode_params } from './url.js';
|
|
3
2
|
|
|
4
3
|
const param_pattern = /^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;
|
|
5
4
|
|
|
@@ -134,13 +133,43 @@ export function get_route_segments(route) {
|
|
|
134
133
|
return route.slice(1).split('/').filter(affects_path);
|
|
135
134
|
}
|
|
136
135
|
|
|
136
|
+
/**
|
|
137
|
+
* @param {import('@sveltejs/kit').ParamMatcher} matcher
|
|
138
|
+
* @param {string} value
|
|
139
|
+
* @returns {{ success: true, value: any } | { success: false }}
|
|
140
|
+
*/
|
|
141
|
+
function run_matcher(matcher, value) {
|
|
142
|
+
const result = matcher['~standard'].validate(value);
|
|
143
|
+
|
|
144
|
+
if (result instanceof Promise) {
|
|
145
|
+
throw new Error('Async param matchers are not supported');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (result.issues) {
|
|
149
|
+
return { success: false };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const parsed = result.value;
|
|
153
|
+
|
|
154
|
+
if (
|
|
155
|
+
typeof parsed !== 'string' &&
|
|
156
|
+
typeof parsed !== 'number' &&
|
|
157
|
+
typeof parsed !== 'boolean' &&
|
|
158
|
+
typeof parsed !== 'bigint'
|
|
159
|
+
) {
|
|
160
|
+
throw new Error('Param matcher must return a string, number, boolean, or bigint');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return { success: true, value: parsed };
|
|
164
|
+
}
|
|
165
|
+
|
|
137
166
|
/**
|
|
138
167
|
* @param {RegExpMatchArray} match
|
|
139
168
|
* @param {import('types').RouteParam[]} params
|
|
140
169
|
* @param {Record<string, import('@sveltejs/kit').ParamMatcher>} matchers
|
|
141
170
|
*/
|
|
142
171
|
export function exec(match, params, matchers) {
|
|
143
|
-
/** @type {Record<string,
|
|
172
|
+
/** @type {Record<string, any>} */
|
|
144
173
|
const result = {};
|
|
145
174
|
|
|
146
175
|
const values = match.slice(1);
|
|
@@ -173,37 +202,41 @@ export function exec(match, params, matchers) {
|
|
|
173
202
|
}
|
|
174
203
|
}
|
|
175
204
|
|
|
176
|
-
|
|
177
|
-
result[param.name] = value;
|
|
205
|
+
const decoded = decodeURIComponent(value);
|
|
178
206
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
207
|
+
if (param.matcher) {
|
|
208
|
+
const outcome = run_matcher(matchers[param.matcher], decoded);
|
|
209
|
+
|
|
210
|
+
if (!outcome.success) {
|
|
211
|
+
// in the `/[[a=b]]/...` case, if the value didn't satisfy the matcher,
|
|
212
|
+
// keep track of the number of skipped optional parameters and continue
|
|
213
|
+
if (param.optional && param.chained) {
|
|
214
|
+
buffered++;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
186
217
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
!next_param &&
|
|
190
|
-
!next_value &&
|
|
191
|
-
Object.keys(result).length === values_needing_match.length
|
|
192
|
-
) {
|
|
193
|
-
buffered = 0;
|
|
218
|
+
// otherwise, if the matcher returns `false`, the route did not match
|
|
219
|
+
return;
|
|
194
220
|
}
|
|
195
|
-
|
|
221
|
+
|
|
222
|
+
result[param.name] = outcome.value;
|
|
223
|
+
} else {
|
|
224
|
+
result[param.name] = decoded;
|
|
196
225
|
}
|
|
197
226
|
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
227
|
+
// Now that the params match, reset the buffer if the next param isn't the [...rest]
|
|
228
|
+
// and the next value is defined, otherwise the buffer will cause us to skip values
|
|
229
|
+
const next_param = params[i + 1];
|
|
230
|
+
const next_value = values[i + 1];
|
|
231
|
+
if (next_param && !next_param.rest && next_param.optional && next_value && param.chained) {
|
|
232
|
+
buffered = 0;
|
|
203
233
|
}
|
|
204
234
|
|
|
205
|
-
//
|
|
206
|
-
|
|
235
|
+
// There are no more params and no more values, but all non-empty values have been matched
|
|
236
|
+
if (!next_param && !next_value && Object.keys(result).length === values_needing_match.length) {
|
|
237
|
+
buffered = 0;
|
|
238
|
+
}
|
|
239
|
+
continue;
|
|
207
240
|
}
|
|
208
241
|
|
|
209
242
|
if (buffered) return;
|
|
@@ -242,7 +275,7 @@ const basic_param_pattern = /\[(\[)?(\.\.\.)?(\w+?)(?:=(\w+))?\]\]?/g;
|
|
|
242
275
|
* ); // `/blog/hello-world/something/else`
|
|
243
276
|
* ```
|
|
244
277
|
* @param {string} id
|
|
245
|
-
* @param {Record<string,
|
|
278
|
+
* @param {Record<string, import('@sveltejs/kit').ParamValue | undefined>} params
|
|
246
279
|
* @returns {string}
|
|
247
280
|
*/
|
|
248
281
|
export function resolve_route(id, params) {
|
|
@@ -254,20 +287,33 @@ export function resolve_route(id, params) {
|
|
|
254
287
|
segments
|
|
255
288
|
.map((segment) =>
|
|
256
289
|
segment.replace(basic_param_pattern, (_, optional, rest, name) => {
|
|
257
|
-
const
|
|
290
|
+
const value = params[name];
|
|
258
291
|
|
|
259
|
-
|
|
260
|
-
if (!param_value) {
|
|
292
|
+
if (value === undefined || value === '') {
|
|
261
293
|
if (optional) return '';
|
|
262
|
-
if (rest &&
|
|
294
|
+
if (rest && value !== undefined) return '';
|
|
263
295
|
throw new Error(`Missing parameter '${name}' in route ${id}`);
|
|
264
296
|
}
|
|
265
297
|
|
|
266
|
-
if (
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
298
|
+
if (typeof value === 'string') {
|
|
299
|
+
if (value.startsWith('/') || value.endsWith('/')) {
|
|
300
|
+
throw new Error(
|
|
301
|
+
`Parameter '${name}' in route ${id} cannot start or end with a slash -- this would cause an invalid route like foo//bar`
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return value;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (
|
|
309
|
+
typeof value === 'number' ||
|
|
310
|
+
typeof value === 'boolean' ||
|
|
311
|
+
typeof value === 'bigint'
|
|
312
|
+
) {
|
|
313
|
+
return String(value);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
throw new Error('Parameter values must be a string, number, boolean, or bigint');
|
|
271
317
|
})
|
|
272
318
|
)
|
|
273
319
|
.filter(Boolean)
|
|
@@ -290,7 +336,7 @@ export function has_server_load(node) {
|
|
|
290
336
|
* @param {string} path - The decoded pathname to match
|
|
291
337
|
* @param {Route[]} routes
|
|
292
338
|
* @param {Record<string, import('@sveltejs/kit').ParamMatcher>} matchers
|
|
293
|
-
* @returns {{ route: Route, params: Record<string,
|
|
339
|
+
* @returns {{ route: Route, params: Record<string, any> } | null}
|
|
294
340
|
*/
|
|
295
341
|
export function find_route(path, routes, matchers) {
|
|
296
342
|
for (const route of routes) {
|
|
@@ -301,7 +347,7 @@ export function find_route(path, routes, matchers) {
|
|
|
301
347
|
if (matched) {
|
|
302
348
|
return {
|
|
303
349
|
route,
|
|
304
|
-
params:
|
|
350
|
+
params: matched
|
|
305
351
|
};
|
|
306
352
|
}
|
|
307
353
|
}
|
|
@@ -52,6 +52,11 @@ export class SharedIterator {
|
|
|
52
52
|
/** @type {unknown} */
|
|
53
53
|
#terminal_error = undefined;
|
|
54
54
|
|
|
55
|
+
/** Whether `done()` or `fail()` has been broadcast. */
|
|
56
|
+
get closed() {
|
|
57
|
+
return this.#closed;
|
|
58
|
+
}
|
|
59
|
+
|
|
55
60
|
/**
|
|
56
61
|
* @param {(instance: SharedIterator<T>) => (() => void)} [start]
|
|
57
62
|
*/
|
package/src/utils/streaming.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { noop } from './functions.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Create an async iterator and a function to push values into it
|
|
3
5
|
* @template T
|
|
@@ -30,10 +32,18 @@ export function create_async_iterator() {
|
|
|
30
32
|
};
|
|
31
33
|
},
|
|
32
34
|
add: (promise) => {
|
|
33
|
-
|
|
34
|
-
void promise.
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
const next = Promise.withResolvers();
|
|
36
|
+
void next.promise.catch(noop); // prevent unhandled rejection potentially crashing the process
|
|
37
|
+
deferred.push(next);
|
|
38
|
+
|
|
39
|
+
void promise.then(
|
|
40
|
+
(value) => {
|
|
41
|
+
deferred[++resolved].resolve(value);
|
|
42
|
+
},
|
|
43
|
+
(error) => {
|
|
44
|
+
deferred[++resolved].reject(error);
|
|
45
|
+
}
|
|
46
|
+
);
|
|
37
47
|
}
|
|
38
48
|
};
|
|
39
49
|
}
|
package/src/utils/url.js
CHANGED
|
@@ -4,7 +4,7 @@ import { BROWSER, DEV } from 'esm-env';
|
|
|
4
4
|
* Matches a URI scheme. See https://www.rfc-editor.org/rfc/rfc3986#section-3.1
|
|
5
5
|
* @type {RegExp}
|
|
6
6
|
*/
|
|
7
|
-
export const SCHEME = /^[a-z][a-z\d+\-.]
|
|
7
|
+
export const SCHEME = /^[a-z][a-z\d+\-.]*:/i;
|
|
8
8
|
|
|
9
9
|
const internal = new URL('a://');
|
|
10
10
|
|
|
@@ -27,6 +27,24 @@ export function is_root_relative(path) {
|
|
|
27
27
|
return path[0] === '/' && path[1] !== '/';
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* @param {string} location
|
|
32
|
+
* @param {string} allowed
|
|
33
|
+
*/
|
|
34
|
+
export function matches_external_allowlist_entry(location, allowed) {
|
|
35
|
+
if (location === allowed) return true;
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const allow = new URL(allowed);
|
|
39
|
+
const loc = new URL(location, allow);
|
|
40
|
+
|
|
41
|
+
// this is stricter than `loc.origin === allow.origin`, which can fail in `blob:` cases
|
|
42
|
+
return loc.protocol === allow.protocol && loc.host === allow.host;
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
30
48
|
/**
|
|
31
49
|
* @param {string} path
|
|
32
50
|
* @param {import('types').TrailingSlash} trailing_slash
|
|
@@ -119,6 +137,7 @@ export function make_trackable(url, callback, search_params_callback, allow_hash
|
|
|
119
137
|
/**
|
|
120
138
|
* URL properties that could change during the lifetime of the page,
|
|
121
139
|
* which excludes things like `origin`
|
|
140
|
+
* @type {(keyof URL)[]}
|
|
122
141
|
*/
|
|
123
142
|
const tracked_url_properties = ['href', 'pathname', 'search', 'toString', 'toJSON'];
|
|
124
143
|
if (allow_hash) tracked_url_properties.push('hash');
|
|
@@ -127,7 +146,6 @@ export function make_trackable(url, callback, search_params_callback, allow_hash
|
|
|
127
146
|
Object.defineProperty(tracked, property, {
|
|
128
147
|
get() {
|
|
129
148
|
callback();
|
|
130
|
-
// @ts-expect-error
|
|
131
149
|
return url[property];
|
|
132
150
|
},
|
|
133
151
|
|
package/src/version.js
CHANGED