@sveltejs/kit 3.0.0-next.6 → 3.0.0-next.8
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 +8 -3
- package/src/core/adapt/builder.js +11 -39
- package/src/core/config/index.js +76 -71
- package/src/core/config/options.js +280 -285
- package/src/core/config/types.d.ts +1 -1
- package/src/core/env.js +86 -1
- package/src/core/generate_manifest/index.js +12 -15
- package/src/core/sync/create_manifest_data/index.js +7 -45
- package/src/core/sync/sync.js +0 -2
- package/src/core/sync/write_client_manifest.js +7 -21
- package/src/core/sync/write_non_ambient.js +12 -9
- package/src/core/sync/write_server.js +0 -4
- package/src/core/sync/write_types/index.js +28 -24
- package/src/core/utils.js +2 -2
- package/src/exports/index.js +3 -3
- package/src/exports/internal/client.js +5 -0
- package/src/exports/internal/index.js +1 -91
- 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 +5 -9
- package/src/exports/params.js +63 -0
- package/src/exports/public.d.ts +103 -46
- package/src/exports/url.js +84 -0
- package/src/exports/vite/dev/index.js +31 -20
- package/src/exports/vite/index.js +280 -199
- package/src/exports/vite/preview/index.js +14 -15
- package/src/exports/vite/utils.js +8 -10
- package/src/runtime/app/env/internal.js +4 -4
- package/src/runtime/app/forms.js +2 -2
- package/src/runtime/app/paths/client.js +3 -7
- package/src/runtime/app/paths/internal/client.js +4 -2
- package/src/runtime/app/paths/internal/server.js +2 -23
- package/src/runtime/app/paths/server.js +3 -3
- package/src/runtime/app/server/remote/query.js +6 -12
- package/src/runtime/app/state/client.js +1 -2
- package/src/runtime/app/stores.js +13 -76
- package/src/runtime/client/bundle.js +1 -1
- package/src/runtime/client/client-entry.js +3 -0
- package/src/runtime/client/client.js +230 -238
- package/src/runtime/client/entry.js +24 -3
- package/src/runtime/client/payload.js +17 -0
- package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
- package/src/runtime/client/remote-functions/form.svelte.js +11 -30
- package/src/runtime/client/remote-functions/prerender.svelte.js +10 -3
- package/src/runtime/client/remote-functions/query/instance.svelte.js +18 -9
- package/src/runtime/client/remote-functions/query-live/instance.svelte.js +16 -6
- package/src/runtime/client/remote-functions/shared.svelte.js +1 -2
- package/src/runtime/client/state.svelte.js +66 -49
- package/src/runtime/client/types.d.ts +3 -7
- package/src/runtime/client/utils.js +0 -96
- package/src/runtime/components/root.svelte +66 -0
- package/src/runtime/form-utils.js +16 -6
- package/src/runtime/server/cookie.js +17 -7
- package/src/runtime/server/index.js +1 -1
- package/src/runtime/server/page/index.js +7 -14
- package/src/runtime/server/page/load_data.js +1 -1
- package/src/runtime/server/page/render.js +72 -88
- package/src/runtime/server/page/server_routing.js +13 -9
- package/src/runtime/server/remote.js +23 -13
- package/src/runtime/server/respond.js +11 -8
- package/src/runtime/server/utils.js +28 -5
- package/src/runtime/telemetry/otel.js +1 -1
- package/src/runtime/types.d.ts +8 -0
- package/src/types/ambient.d.ts +5 -1
- package/src/types/global-private.d.ts +11 -18
- package/src/types/internal.d.ts +25 -30
- package/src/utils/error.js +1 -1
- package/src/utils/import.js +6 -1
- package/src/utils/mime.js +9 -0
- package/src/utils/params.js +66 -0
- package/src/utils/routing.js +90 -44
- package/src/utils/streaming.js +14 -4
- package/src/utils/url.js +0 -79
- package/src/version.js +1 -1
- package/types/index.d.ts +127 -125
- package/types/index.d.ts.map +12 -8
- package/src/core/sync/write_root.js +0 -148
- package/src/exports/internal/server.js +0 -22
- /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SvelteKitPayload } from 'types';
|
|
2
2
|
|
|
3
3
|
declare global {
|
|
4
4
|
const __SVELTEKIT_ADAPTER_NAME__: string;
|
|
@@ -15,7 +15,7 @@ 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
20
|
/** True if `config.experimental.forkPreloads` is `true` */
|
|
21
21
|
const __SVELTEKIT_FORK_PRELOADS__: boolean;
|
|
@@ -33,26 +33,20 @@ declare global {
|
|
|
33
33
|
* Used for treeshaking universal load code from client bundles when no universal loads exist.
|
|
34
34
|
*/
|
|
35
35
|
const __SVELTEKIT_HAS_UNIVERSAL_LOAD__: boolean;
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
assets?: string;
|
|
42
|
-
/** Public environment variables */
|
|
43
|
-
env?: Record<string, string>;
|
|
44
|
-
/** Serialized data from query/form/command functions */
|
|
45
|
-
data?: RemoteFunctionData;
|
|
46
|
-
/** Create a placeholder promise */
|
|
47
|
-
defer?: (id: number) => Promise<any>;
|
|
48
|
-
/** Resolve a placeholder promise */
|
|
49
|
-
resolve?: (data: { id: number; data: any; error: any }) => void;
|
|
50
|
-
};
|
|
36
|
+
/**
|
|
37
|
+
* The `__sveltekit_abc123` object in the init `<script>`.
|
|
38
|
+
* Should only be used when bundleStrategy !== 'inline' to avoid SvelteKit runtime changing on every build, preventing cacheability.
|
|
39
|
+
*/
|
|
40
|
+
const __SVELTEKIT_PAYLOAD__: SvelteKitPayload;
|
|
51
41
|
/**
|
|
52
42
|
* The Vite `root` setting used to construct paths to nodes and components
|
|
53
43
|
* for the SSR manifest during development
|
|
54
44
|
*/
|
|
55
45
|
const __SVELTEKIT_ROOT__: string;
|
|
46
|
+
/**
|
|
47
|
+
* Whether the `experimental.async` flag is applied
|
|
48
|
+
*/
|
|
49
|
+
const __SVELTEKIT_SUPPORTS_ASYNC__: boolean;
|
|
56
50
|
/**
|
|
57
51
|
* This makes the use of specific features visible at both dev and build time, in such a
|
|
58
52
|
* way that we can error when they are not supported by the target platform.
|
|
@@ -68,7 +62,6 @@ declare global {
|
|
|
68
62
|
* to throw an error if the feature would fail in production.
|
|
69
63
|
*/
|
|
70
64
|
var __SVELTEKIT_TRACK__: (label: string) => void;
|
|
71
|
-
var __SVELTEKIT_EXPERIMENTAL_USE_TRANSFORM_ERROR__: boolean;
|
|
72
65
|
var Bun: object;
|
|
73
66
|
var Deno: object;
|
|
74
67
|
}
|
package/src/types/internal.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Component } from 'svelte';
|
|
2
2
|
import {
|
|
3
3
|
Config,
|
|
4
4
|
ServerLoad,
|
|
@@ -113,7 +113,7 @@ export interface BuildData {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
export interface CSRPageNode {
|
|
116
|
-
component:
|
|
116
|
+
component: Component;
|
|
117
117
|
universal: {
|
|
118
118
|
load?: Load;
|
|
119
119
|
trailingSlash?: TrailingSlash;
|
|
@@ -204,7 +204,7 @@ export interface ManifestData {
|
|
|
204
204
|
};
|
|
205
205
|
nodes: PageNode[];
|
|
206
206
|
routes: RouteData[];
|
|
207
|
-
|
|
207
|
+
params: string | null;
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
export interface RemoteChunk {
|
|
@@ -247,7 +247,7 @@ export type RecursiveRequired<T> = {
|
|
|
247
247
|
// Recursive implementation of TypeScript's Required utility type.
|
|
248
248
|
// Will recursively continue until it reaches a primitive or Function
|
|
249
249
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
250
|
-
[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
|
|
251
251
|
? RecursiveRequired<T[K]> // recursively continue through.
|
|
252
252
|
: T[K]; // Use the exact type for everything else
|
|
253
253
|
};
|
|
@@ -417,27 +417,7 @@ export interface ServerMetadata {
|
|
|
417
417
|
remotes: Map<string, Map<string, { type: RemoteInternals['type']; dynamic: boolean }>>;
|
|
418
418
|
}
|
|
419
419
|
|
|
420
|
-
export
|
|
421
|
-
default: {
|
|
422
|
-
render(
|
|
423
|
-
props: Record<string, any>,
|
|
424
|
-
opts: { context: Map<any, any>; csp?: { nonce?: string; hash?: boolean } }
|
|
425
|
-
): {
|
|
426
|
-
html: string;
|
|
427
|
-
head: string;
|
|
428
|
-
css: {
|
|
429
|
-
code: string;
|
|
430
|
-
map: any; // TODO
|
|
431
|
-
};
|
|
432
|
-
/** Until we require all Svelte versions that support hashes, this might not be defined */
|
|
433
|
-
hashes?: {
|
|
434
|
-
script: Array<`sha256-${string}`>;
|
|
435
|
-
};
|
|
436
|
-
};
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
export type SSRComponentLoader = () => Promise<SSRComponent>;
|
|
420
|
+
export type SSRComponentLoader = () => Promise<Component>;
|
|
441
421
|
|
|
442
422
|
export interface UniversalNode {
|
|
443
423
|
/** Is `null` in case static analysis succeeds but the node is ssr=false */
|
|
@@ -493,7 +473,6 @@ export type SSRNodeLoader = () => Promise<SSRNode>;
|
|
|
493
473
|
|
|
494
474
|
export interface SSROptions {
|
|
495
475
|
app_template_contains_nonce: boolean;
|
|
496
|
-
async: boolean;
|
|
497
476
|
csp: ValidatedConfig['kit']['csp'];
|
|
498
477
|
csrf_check_origin: boolean;
|
|
499
478
|
csrf_trusted_origins: string[];
|
|
@@ -502,10 +481,8 @@ export interface SSROptions {
|
|
|
502
481
|
hooks: ServerHooks;
|
|
503
482
|
link_header_preload: ValidatedConfig['kit']['output']['linkHeaderPreload'];
|
|
504
483
|
paths_origin: string | undefined;
|
|
505
|
-
root: SSRComponent['default'];
|
|
506
484
|
service_worker: boolean;
|
|
507
485
|
service_worker_options: RegistrationOptions;
|
|
508
|
-
server_error_boundaries: boolean;
|
|
509
486
|
templates: {
|
|
510
487
|
app(values: {
|
|
511
488
|
head: string;
|
|
@@ -598,7 +575,7 @@ export interface Uses {
|
|
|
598
575
|
search_params: Set<string>;
|
|
599
576
|
}
|
|
600
577
|
|
|
601
|
-
export type ValidatedConfig = Config & {
|
|
578
|
+
export type ValidatedConfig = Omit<Config, 'kit'> & {
|
|
602
579
|
kit: ValidatedKitConfig;
|
|
603
580
|
extensions: string[];
|
|
604
581
|
};
|
|
@@ -727,7 +704,7 @@ export interface RequestState {
|
|
|
727
704
|
string,
|
|
728
705
|
{
|
|
729
706
|
internals: RemoteInternals;
|
|
730
|
-
|
|
707
|
+
fn: () => Promise<any>;
|
|
731
708
|
}
|
|
732
709
|
>;
|
|
733
710
|
/** Instances created via `myForm.for(...)` */
|
|
@@ -765,5 +742,23 @@ export interface RequestStore {
|
|
|
765
742
|
state: RequestState;
|
|
766
743
|
}
|
|
767
744
|
|
|
745
|
+
/** Type of the `__sveltekit_abc123` object in the init `<script>` */
|
|
746
|
+
export interface SvelteKitPayload {
|
|
747
|
+
/** The application version */
|
|
748
|
+
version: string;
|
|
749
|
+
/** The basepath, usually relative to the current page */
|
|
750
|
+
base: string;
|
|
751
|
+
/** Path to externally-hosted assets */
|
|
752
|
+
assets?: string;
|
|
753
|
+
/** Public environment variables */
|
|
754
|
+
env?: Record<string, string>;
|
|
755
|
+
/** Serialized data from query/form/command functions */
|
|
756
|
+
data?: RemoteFunctionData;
|
|
757
|
+
/** Create a placeholder promise */
|
|
758
|
+
defer?: (id: number) => Promise<any>;
|
|
759
|
+
/** Resolve a placeholder promise */
|
|
760
|
+
resolve?: (data: { id: number; data: any; error: any }) => void;
|
|
761
|
+
}
|
|
762
|
+
|
|
768
763
|
export * from '../exports/index.js';
|
|
769
764
|
export * from './private.js';
|
package/src/utils/error.js
CHANGED
|
@@ -30,7 +30,7 @@ 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
|
}
|
package/src/utils/import.js
CHANGED
|
@@ -2,7 +2,12 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Resolves a peer dependency relative to the current working directory.
|
|
5
|
+
* Resolves a peer dependency relative to the current working directory.
|
|
6
|
+
*
|
|
7
|
+
* Mainly used to resolve the correct Vite package when an app's SvelteKit is a
|
|
8
|
+
* linked local repository.
|
|
9
|
+
*
|
|
10
|
+
* Duplicated with `packages/adapter-auto`
|
|
6
11
|
* @param {string} dependency
|
|
7
12
|
* @param {string} root
|
|
8
13
|
*/
|
|
@@ -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,7 +1,6 @@
|
|
|
1
1
|
import { BROWSER } from 'esm-env';
|
|
2
|
-
import { decode_params } from './url.js';
|
|
3
2
|
|
|
4
|
-
const param_pattern = /^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;
|
|
3
|
+
const param_pattern = /^(\[)?(\.\.\.)?([\w-]+)(?:=([\w-]+))?(\])?$/;
|
|
5
4
|
|
|
6
5
|
const root_group_pattern = /^\/\((?:[^)]+)\)$/;
|
|
7
6
|
|
|
@@ -20,7 +19,7 @@ export function parse_route_id(id) {
|
|
|
20
19
|
`^${get_route_segments(id)
|
|
21
20
|
.map((segment) => {
|
|
22
21
|
// special case — /[...rest]/ could contain zero segments
|
|
23
|
-
const rest_match = /^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(segment);
|
|
22
|
+
const rest_match = /^\[\.\.\.([\w-]+)(?:=([\w-]+))?\]$/.exec(segment);
|
|
24
23
|
if (rest_match) {
|
|
25
24
|
params.push({
|
|
26
25
|
name: rest_match[1],
|
|
@@ -32,7 +31,7 @@ export function parse_route_id(id) {
|
|
|
32
31
|
return '(?:/([^]*))?';
|
|
33
32
|
}
|
|
34
33
|
// special case — /[[optional]]/ could contain zero segments
|
|
35
|
-
const optional_match = /^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(segment);
|
|
34
|
+
const optional_match = /^\[\[([\w-]+)(?:=([\w-]+))?\]\]$/.exec(segment);
|
|
36
35
|
if (optional_match) {
|
|
37
36
|
params.push({
|
|
38
37
|
name: optional_match[1],
|
|
@@ -73,7 +72,7 @@ export function parse_route_id(id) {
|
|
|
73
72
|
const match = /** @type {RegExpExecArray} */ (param_pattern.exec(content));
|
|
74
73
|
if (!BROWSER && !match) {
|
|
75
74
|
throw new Error(
|
|
76
|
-
`Invalid param: ${content}. Params and matcher names can only have underscores and alphanumeric characters.`
|
|
75
|
+
`Invalid param: ${content}. Params and matcher names can only have underscores, hyphens, and alphanumeric characters.`
|
|
77
76
|
);
|
|
78
77
|
}
|
|
79
78
|
|
|
@@ -104,7 +103,7 @@ export function parse_route_id(id) {
|
|
|
104
103
|
return { pattern, params };
|
|
105
104
|
}
|
|
106
105
|
|
|
107
|
-
const optional_param_regex = /\/\[\[\w+?(
|
|
106
|
+
const optional_param_regex = /\/\[\[[\w-]+?(?:=[\w-]+)?\]\]/;
|
|
108
107
|
|
|
109
108
|
/**
|
|
110
109
|
* Removes optional params from a route ID.
|
|
@@ -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;
|
|
@@ -227,7 +260,7 @@ function escape(str) {
|
|
|
227
260
|
);
|
|
228
261
|
}
|
|
229
262
|
|
|
230
|
-
const basic_param_pattern = /\[(\[)?(\.\.\.)?(\w+?)(?:=(\w+))?\]\]?/g;
|
|
263
|
+
const basic_param_pattern = /\[(\[)?(\.\.\.)?([\w-]+?)(?:=([\w-]+))?\]\]?/g;
|
|
231
264
|
|
|
232
265
|
/**
|
|
233
266
|
* Populate a route ID with params to resolve a pathname.
|
|
@@ -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
|
}
|
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
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { BROWSER, DEV } from 'esm-env';
|
|
2
|
-
import { try_get_request_store } from '../exports/internal/event.js';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Matches a URI scheme. See https://www.rfc-editor.org/rfc/rfc3986#section-3.1
|
|
6
5
|
* @type {RegExp}
|
|
7
6
|
*/
|
|
8
7
|
export const SCHEME = /^[a-z][a-z\d+\-.]*:/i;
|
|
9
|
-
// See https://datatracker.ietf.org/doc/html/rfc2606 - no domains under the .invalid TLD can be registered
|
|
10
|
-
const REDIRECT_BASE = 'https://sveltekit-redirect.invalid';
|
|
11
8
|
|
|
12
9
|
const internal = new URL('a://');
|
|
13
10
|
|
|
@@ -30,32 +27,6 @@ export function is_root_relative(path) {
|
|
|
30
27
|
return path[0] === '/' && path[1] !== '/';
|
|
31
28
|
}
|
|
32
29
|
|
|
33
|
-
/**
|
|
34
|
-
* Whether a redirect location is absolute, i.e. not a root-relative or path-relative URL,
|
|
35
|
-
* and not pointing to the same origin as we're currently on (if determineable).
|
|
36
|
-
* @param {string} location
|
|
37
|
-
*/
|
|
38
|
-
export function is_external_location(location) {
|
|
39
|
-
const origin = BROWSER ? window.location.origin : try_get_request_store()?.event.url.origin;
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
return !matches_external_allowlist_entry(location, origin ?? REDIRECT_BASE);
|
|
43
|
-
} catch {
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* @param {string} location
|
|
50
|
-
*/
|
|
51
|
-
function is_javascript_location(location) {
|
|
52
|
-
try {
|
|
53
|
-
return new URL(location, REDIRECT_BASE).protocol === 'javascript:';
|
|
54
|
-
} catch {
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
30
|
/**
|
|
60
31
|
* @param {string} location
|
|
61
32
|
* @param {string} allowed
|
|
@@ -74,56 +45,6 @@ export function matches_external_allowlist_entry(location, allowed) {
|
|
|
74
45
|
}
|
|
75
46
|
}
|
|
76
47
|
|
|
77
|
-
/**
|
|
78
|
-
* @param {string} location
|
|
79
|
-
* @param {{ external?: boolean | string[] }} [options]
|
|
80
|
-
*/
|
|
81
|
-
export function validate_redirect_location(location, options) {
|
|
82
|
-
if (!is_external_location(location)) return;
|
|
83
|
-
|
|
84
|
-
const external = options?.external;
|
|
85
|
-
|
|
86
|
-
if (!external) {
|
|
87
|
-
throw new Error(
|
|
88
|
-
DEV
|
|
89
|
-
? `Cannot redirect to external URL ${JSON.stringify(location)}. ` +
|
|
90
|
-
'To redirect to an external URL, pass `{ external: true }` or an allowlist of permitted origins as the third argument to `redirect`'
|
|
91
|
-
: 'Cannot redirect to external URL unless explicitly allowed'
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (external === true) {
|
|
96
|
-
if (is_javascript_location(location)) {
|
|
97
|
-
throw new Error(
|
|
98
|
-
DEV
|
|
99
|
-
? `Cannot redirect to ${JSON.stringify(location)} with \`{ external: true }\`. ` +
|
|
100
|
-
'The `:javascript` protocol must be explicitly listed in the `external` allowlist'
|
|
101
|
-
: 'Cannot redirect to external URL unless explicitly allowed'
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (Array.isArray(external)) {
|
|
109
|
-
if (!external.some((allowed) => matches_external_allowlist_entry(location, allowed))) {
|
|
110
|
-
throw new Error(
|
|
111
|
-
DEV
|
|
112
|
-
? `Cannot redirect to ${JSON.stringify(location)}: URL origin is not included in the \`external\` allowlist`
|
|
113
|
-
: 'Cannot redirect to external URL unless explicitly allowed'
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
throw new Error(
|
|
121
|
-
DEV
|
|
122
|
-
? '`redirect` options.external must be `true` or an array of allowed origins'
|
|
123
|
-
: 'Invalid redirect options.external value'
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
48
|
/**
|
|
128
49
|
* @param {string} path
|
|
129
50
|
* @param {import('types').TrailingSlash} trailing_slash
|
package/src/version.js
CHANGED