@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.
Files changed (80) hide show
  1. package/package.json +8 -3
  2. package/src/core/adapt/builder.js +11 -39
  3. package/src/core/config/index.js +76 -71
  4. package/src/core/config/options.js +280 -285
  5. package/src/core/config/types.d.ts +1 -1
  6. package/src/core/env.js +86 -1
  7. package/src/core/generate_manifest/index.js +12 -15
  8. package/src/core/sync/create_manifest_data/index.js +7 -45
  9. package/src/core/sync/sync.js +0 -2
  10. package/src/core/sync/write_client_manifest.js +7 -21
  11. package/src/core/sync/write_non_ambient.js +12 -9
  12. package/src/core/sync/write_server.js +0 -4
  13. package/src/core/sync/write_types/index.js +28 -24
  14. package/src/core/utils.js +2 -2
  15. package/src/exports/index.js +3 -3
  16. package/src/exports/internal/client.js +5 -0
  17. package/src/exports/internal/index.js +1 -91
  18. package/src/exports/internal/{event.js → server/event.js} +1 -2
  19. package/src/exports/internal/server/index.js +33 -0
  20. package/src/exports/internal/shared.js +89 -0
  21. package/src/exports/node/index.js +5 -9
  22. package/src/exports/params.js +63 -0
  23. package/src/exports/public.d.ts +103 -46
  24. package/src/exports/url.js +84 -0
  25. package/src/exports/vite/dev/index.js +31 -20
  26. package/src/exports/vite/index.js +280 -199
  27. package/src/exports/vite/preview/index.js +14 -15
  28. package/src/exports/vite/utils.js +8 -10
  29. package/src/runtime/app/env/internal.js +4 -4
  30. package/src/runtime/app/forms.js +2 -2
  31. package/src/runtime/app/paths/client.js +3 -7
  32. package/src/runtime/app/paths/internal/client.js +4 -2
  33. package/src/runtime/app/paths/internal/server.js +2 -23
  34. package/src/runtime/app/paths/server.js +3 -3
  35. package/src/runtime/app/server/remote/query.js +6 -12
  36. package/src/runtime/app/state/client.js +1 -2
  37. package/src/runtime/app/stores.js +13 -76
  38. package/src/runtime/client/bundle.js +1 -1
  39. package/src/runtime/client/client-entry.js +3 -0
  40. package/src/runtime/client/client.js +230 -238
  41. package/src/runtime/client/entry.js +24 -3
  42. package/src/runtime/client/payload.js +17 -0
  43. package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
  44. package/src/runtime/client/remote-functions/form.svelte.js +11 -30
  45. package/src/runtime/client/remote-functions/prerender.svelte.js +10 -3
  46. package/src/runtime/client/remote-functions/query/instance.svelte.js +18 -9
  47. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +16 -6
  48. package/src/runtime/client/remote-functions/shared.svelte.js +1 -2
  49. package/src/runtime/client/state.svelte.js +66 -49
  50. package/src/runtime/client/types.d.ts +3 -7
  51. package/src/runtime/client/utils.js +0 -96
  52. package/src/runtime/components/root.svelte +66 -0
  53. package/src/runtime/form-utils.js +16 -6
  54. package/src/runtime/server/cookie.js +17 -7
  55. package/src/runtime/server/index.js +1 -1
  56. package/src/runtime/server/page/index.js +7 -14
  57. package/src/runtime/server/page/load_data.js +1 -1
  58. package/src/runtime/server/page/render.js +72 -88
  59. package/src/runtime/server/page/server_routing.js +13 -9
  60. package/src/runtime/server/remote.js +23 -13
  61. package/src/runtime/server/respond.js +11 -8
  62. package/src/runtime/server/utils.js +28 -5
  63. package/src/runtime/telemetry/otel.js +1 -1
  64. package/src/runtime/types.d.ts +8 -0
  65. package/src/types/ambient.d.ts +5 -1
  66. package/src/types/global-private.d.ts +11 -18
  67. package/src/types/internal.d.ts +25 -30
  68. package/src/utils/error.js +1 -1
  69. package/src/utils/import.js +6 -1
  70. package/src/utils/mime.js +9 -0
  71. package/src/utils/params.js +66 -0
  72. package/src/utils/routing.js +90 -44
  73. package/src/utils/streaming.js +14 -4
  74. package/src/utils/url.js +0 -79
  75. package/src/version.js +1 -1
  76. package/types/index.d.ts +127 -125
  77. package/types/index.d.ts.map +12 -8
  78. package/src/core/sync/write_root.js +0 -148
  79. package/src/exports/internal/server.js +0 -22
  80. /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
@@ -1,4 +1,4 @@
1
- import { RemoteFunctionData } from 'types';
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.experimental.instrumentation.server` is `true` */
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
- /** The `__sveltekit_abc123` object in the init `<script>` */
37
- const __SVELTEKIT_PAYLOAD__: {
38
- /** The basepath, usually relative to the current page */
39
- base: string;
40
- /** Path to externally-hosted assets */
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
  }
@@ -1,4 +1,4 @@
1
- import { SvelteComponent } from 'svelte';
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: typeof SvelteComponent;
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
- matchers: Record<string, string>;
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 interface SSRComponent {
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
- promise: Promise<any>;
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';
@@ -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/index.js').Redirect | HttpError | SvelteKitError | Error} */ (
33
+ return /** @type {import('../exports/internal/shared.js').Redirect | HttpError | SvelteKitError | Error} */ (
34
34
  error
35
35
  );
36
36
  }
@@ -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. Duplicated with `packages/adapter-auto`
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
+ }
@@ -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+?(?:=\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, 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
- if (!param.matcher || matchers[param.matcher](value)) {
177
- result[param.name] = value;
205
+ const decoded = decodeURIComponent(value);
178
206
 
179
- // Now that the params match, reset the buffer if the next param isn't the [...rest]
180
- // and the next value is defined, otherwise the buffer will cause us to skip values
181
- const next_param = params[i + 1];
182
- const next_value = values[i + 1];
183
- if (next_param && !next_param.rest && next_param.optional && next_value && param.chained) {
184
- buffered = 0;
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
- // There are no more params and no more values, but all non-empty values have been matched
188
- if (
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
- continue;
221
+
222
+ result[param.name] = outcome.value;
223
+ } else {
224
+ result[param.name] = decoded;
196
225
  }
197
226
 
198
- // in the `/[[a=b]]/...` case, if the value didn't satisfy the matcher,
199
- // keep track of the number of skipped optional parameters and continue
200
- if (param.optional && param.chained) {
201
- buffered++;
202
- continue;
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
- // otherwise, if the matcher returns `false`, the route did not match
206
- return;
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, string | undefined>} params
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 param_value = params[name];
290
+ const value = params[name];
258
291
 
259
- // This is nested so TS correctly narrows the type
260
- if (!param_value) {
292
+ if (value === undefined || value === '') {
261
293
  if (optional) return '';
262
- if (rest && param_value !== undefined) return '';
294
+ if (rest && value !== undefined) return '';
263
295
  throw new Error(`Missing parameter '${name}' in route ${id}`);
264
296
  }
265
297
 
266
- if (param_value.startsWith('/') || param_value.endsWith('/'))
267
- throw new Error(
268
- `Parameter '${name}' in route ${id} cannot start or end with a slash -- this would cause an invalid route like foo//bar`
269
- );
270
- return param_value;
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, string> } | null}
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: decode_params(matched)
350
+ params: matched
305
351
  };
306
352
  }
307
353
  }
@@ -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
- deferred.push(Promise.withResolvers());
34
- void promise.then((value) => {
35
- deferred[++resolved].resolve(value);
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
@@ -1,4 +1,4 @@
1
1
  // generated during release, do not modify
2
2
 
3
3
  /** @type {string} */
4
- export const VERSION = '3.0.0-next.6';
4
+ export const VERSION = '3.0.0-next.8';