@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
@@ -0,0 +1,33 @@
1
+ /** @import { Span } from '@opentelemetry/api' */
2
+ import { try_get_request_store } from './event.js';
3
+
4
+ export function get_origin() {
5
+ return try_get_request_store()?.event.url.origin;
6
+ }
7
+
8
+ /**
9
+ * @template {{ tracing: { enabled: boolean, root: Span, current: Span } }} T
10
+ * @param {T} event_like
11
+ * @param {Span} current
12
+ * @returns {T}
13
+ */
14
+ export function merge_tracing(event_like, current) {
15
+ return {
16
+ ...event_like,
17
+ tracing: {
18
+ ...event_like.tracing,
19
+ current
20
+ }
21
+ };
22
+ }
23
+
24
+ export {
25
+ with_request_store,
26
+ getRequestEvent,
27
+ get_request_store,
28
+ try_get_request_store
29
+ } from './event.js';
30
+
31
+ export { init_remote_functions } from './remote-functions.js';
32
+
33
+ export * from '../shared.js';
@@ -0,0 +1,89 @@
1
+ /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
2
+
3
+ export class HttpError {
4
+ /**
5
+ * @param {number} status
6
+ * @param {Omit<App.Error, 'status'> | string | undefined} body
7
+ * @param {Omit<App.Error, 'status' | 'message'>} [properties]
8
+ */
9
+ constructor(status, body, properties) {
10
+ this.status = status;
11
+ if (typeof body === 'string') {
12
+ this.body = { ...properties, message: body, status };
13
+ } else if (body) {
14
+ this.body = { ...body, status };
15
+ } else {
16
+ this.body = { message: `Error: ${status}`, status };
17
+ }
18
+ }
19
+
20
+ toString() {
21
+ return JSON.stringify(this.body);
22
+ }
23
+ }
24
+
25
+ export class Redirect {
26
+ /**
27
+ * @param {300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308} status
28
+ * @param {string} location
29
+ */
30
+ constructor(status, location) {
31
+ try {
32
+ new Headers({ location });
33
+ } catch {
34
+ throw new Error(
35
+ `Invalid redirect location ${JSON.stringify(location)}: ` +
36
+ 'this string contains characters that cannot be used in HTTP headers'
37
+ );
38
+ }
39
+
40
+ this.status = status;
41
+ this.location = location;
42
+ }
43
+ }
44
+
45
+ /**
46
+ * An error that was thrown from within the SvelteKit runtime that is not fatal and doesn't result in a 500, such as a 404.
47
+ * `SvelteKitError` goes through `handleError`.
48
+ * @extends Error
49
+ */
50
+ export class SvelteKitError extends Error {
51
+ /**
52
+ * @param {number} status
53
+ * @param {string} text
54
+ * @param {string} message
55
+ */
56
+ constructor(status, text, message) {
57
+ super(message);
58
+ this.status = status;
59
+ this.text = text;
60
+ }
61
+ }
62
+
63
+ /**
64
+ * @template [T=undefined]
65
+ */
66
+ export class ActionFailure {
67
+ /**
68
+ * @param {number} status
69
+ * @param {T} data
70
+ */
71
+ constructor(status, data) {
72
+ this.status = status;
73
+ this.data = data;
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Error thrown when form validation fails imperatively
79
+ */
80
+ export class ValidationError extends Error {
81
+ /**
82
+ * @param {StandardSchemaV1.Issue[]} issues
83
+ */
84
+ constructor(issues) {
85
+ super('Validation failed');
86
+ this.name = 'ValidationError';
87
+ this.issues = issues;
88
+ }
89
+ }
@@ -1,6 +1,6 @@
1
1
  import { createReadStream } from 'node:fs';
2
2
  import { Readable } from 'node:stream';
3
- import { SvelteKitError } from '../internal/index.js';
3
+ import { SvelteKitError } from '../internal/shared.js';
4
4
  import { noop } from '../../utils/functions.js';
5
5
 
6
6
  /** @type {WeakMap<import('http').IncomingMessage, (chunk: Buffer) => void>} */
@@ -119,11 +119,9 @@ function get_raw_body(req, body_size_limit) {
119
119
  * base: string;
120
120
  * bodySizeLimit?: number;
121
121
  * }} options
122
- * @returns {Promise<Request>}
122
+ * @returns {Request}
123
123
  */
124
- // TODO 3.0 make the signature synchronous?
125
- // eslint-disable-next-line @typescript-eslint/require-await
126
- export async function getRequest({ request, base, bodySizeLimit }) {
124
+ export function getRequest({ request, base, bodySizeLimit }) {
127
125
  let headers = /** @type {Record<string, string>} */ (request.headers);
128
126
  if (request.httpVersionMajor >= 2) {
129
127
  // the Request constructor rejects headers with ':' in the name
@@ -205,11 +203,9 @@ function drain_request(res) {
205
203
  /**
206
204
  * @param {import('http').ServerResponse} res
207
205
  * @param {Response} response
208
- * @returns {Promise<void>}
206
+ * @returns {void}
209
207
  */
210
- // TODO 3.0 make the signature synchronous?
211
- // eslint-disable-next-line @typescript-eslint/require-await
212
- export async function setResponse(res, response) {
208
+ export function setResponse(res, response) {
213
209
  res.once('finish', () => drain_request(res));
214
210
  res.once('close', () => drain_request(res));
215
211
 
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Define [parameter matchers](https://svelte.dev/docs/kit/advanced-routing#Matching) for your app.
3
+ *
4
+ * @example
5
+ * ```js
6
+ * import { defineParams } from '@sveltejs/kit';
7
+ * import * as v from 'valibot';
8
+ *
9
+ * export const params = defineParams({
10
+ * locale: (param) => {
11
+ * if (param !== 'de' && param !== 'en') return;
12
+ * return param;
13
+ * },
14
+ * number: v.pipe(v.string(), v.toNumber())
15
+ * });
16
+ * ```
17
+ *
18
+ * @template {Record<string, import('./public.js').ParamDefinition>} T
19
+ * @param {T} definitions
20
+ * @returns {import('./public.js').DefinedParams<T>}
21
+ */
22
+ export function defineParams(definitions) {
23
+ /** @type {Record<string, import('./public.js').ParamMatcher>} */
24
+ const matchers = {};
25
+
26
+ for (const [key, definition] of Object.entries(definitions)) {
27
+ matchers[key] = normalize_param_definition(definition);
28
+ }
29
+
30
+ return /** @type {import('./public.js').DefinedParams<T>} */ (matchers);
31
+ }
32
+
33
+ /**
34
+ * @param {import('@sveltejs/kit').ParamDefinition} definition
35
+ * @returns {import('@sveltejs/kit').ParamMatcher}
36
+ */
37
+ export function normalize_param_definition(definition) {
38
+ if (typeof definition === 'function') {
39
+ return /** @type {import('@sveltejs/kit').ParamMatcher} */ (
40
+ /** @type {unknown} */ ({
41
+ '~standard': {
42
+ validate(/** @type {unknown} */ value) {
43
+ const result = definition(/** @type {string} */ (value));
44
+
45
+ if (result === undefined) {
46
+ return { issues: [{ message: 'Invalid param' }] };
47
+ }
48
+
49
+ if (/** @type {any} */ (result) instanceof Promise) return result; // will be validated and rejected upstream
50
+
51
+ return { value: result };
52
+ }
53
+ }
54
+ })
55
+ );
56
+ }
57
+
58
+ if (definition && typeof definition === 'object' && '~standard' in definition) {
59
+ return definition;
60
+ }
61
+
62
+ throw new Error('Invalid param definition');
63
+ }
@@ -23,6 +23,7 @@ import {
23
23
  import { BuildData, SSRNodeLoader, SSRRoute, ValidatedConfig } from 'types';
24
24
  import { SvelteConfig } from '@sveltejs/vite-plugin-svelte';
25
25
  import { StandardSchemaV1 } from '@standard-schema/spec';
26
+ import { Plugin } from 'vite';
26
27
  import {
27
28
  RouteId as AppRouteId,
28
29
  LayoutParams as AppLayoutParams,
@@ -70,6 +71,13 @@ export interface Adapter {
70
71
  * during dev, build and prerendering.
71
72
  */
72
73
  emulate?: () => MaybePromise<Emulator>;
74
+ vite?: {
75
+ /**
76
+ * Plugins provided by the adapter are placed before any of SvelteKit's own plugins.
77
+ * @since 3.0.0
78
+ */
79
+ plugins?: Plugin[];
80
+ };
73
81
  }
74
82
 
75
83
  export type LoadProperties<input extends Record<string, any> | void> = input extends void
@@ -149,7 +157,8 @@ export interface Builder {
149
157
 
150
158
  /**
151
159
  * Generate a server-side manifest to initialise the SvelteKit [server](https://svelte.dev/docs/kit/@sveltejs-kit#Server) with.
152
- * @param opts a relative path to the base directory of the app and optionally in which format (esm or cjs) the manifest should be generated
160
+ * @param opts
161
+ * @param opts.relativePath A relative path to the base directory of the server build output
153
162
  */
154
163
  generateManifest: (opts: { relativePath: string; routes?: RouteDefinition[] }) => string;
155
164
 
@@ -490,32 +499,6 @@ export interface KitConfig {
490
499
  };
491
500
  /** Experimental features. Here be dragons. These are not subject to semantic versioning, so breaking changes or removal can happen in any release. */
492
501
  experimental?: {
493
- /**
494
- * Options for enabling server-side [OpenTelemetry](https://opentelemetry.io/) tracing for SvelteKit operations including the [`handle` hook](https://svelte.dev/docs/kit/hooks#Server-hooks-handle), [`load` functions](https://svelte.dev/docs/kit/load), [form actions](https://svelte.dev/docs/kit/form-actions), and [remote functions](https://svelte.dev/docs/kit/remote-functions).
495
- * @default { server: false, serverFile: false }
496
- * @since 2.31.0
497
- */
498
- tracing?: {
499
- /**
500
- * Enables server-side [OpenTelemetry](https://opentelemetry.io/) span emission for SvelteKit operations including the [`handle` hook](https://svelte.dev/docs/kit/hooks#Server-hooks-handle), [`load` functions](https://svelte.dev/docs/kit/load), [form actions](https://svelte.dev/docs/kit/form-actions), and [remote functions](https://svelte.dev/docs/kit/remote-functions).
501
- * @default false
502
- * @since 2.31.0
503
- */
504
- server?: boolean;
505
- };
506
-
507
- /**
508
- * @since 2.31.0
509
- */
510
- instrumentation?: {
511
- /**
512
- * Enables `instrumentation.server.js` for tracing and observability instrumentation.
513
- * @default false
514
- * @since 2.31.0
515
- */
516
- server?: boolean;
517
- };
518
-
519
502
  /**
520
503
  * Whether to enable the experimental remote functions feature. This feature is not yet stable and may be changed or removed at any time.
521
504
  * @default false
@@ -527,15 +510,6 @@ export interface KitConfig {
527
510
  * @default false
528
511
  */
529
512
  forkPreloads?: boolean;
530
-
531
- /**
532
- * Whether to enable the experimental handling of rendering errors.
533
- * When enabled, `<svelte:boundary>` is used to wrap components at each level
534
- * where there's an `+error.svelte`, rendering the error page if the component fails.
535
- * In addition, error boundaries also work on the server and the error object goes through `handleError`.
536
- * @default false
537
- */
538
- handleRenderingErrors?: boolean;
539
513
  };
540
514
  /**
541
515
  * Where to find various files within your project.
@@ -719,7 +693,7 @@ export interface KitConfig {
719
693
  * @default undefined
720
694
  * @since 3.0
721
695
  */
722
- origin?: `http://${string}` | `https://${string}`;
696
+ origin?: string;
723
697
  /**
724
698
  * Whether to use relative asset paths.
725
699
  *
@@ -904,6 +878,17 @@ export interface KitConfig {
904
878
  register?: false;
905
879
  }
906
880
  );
881
+ /**
882
+ * Options for enabling [OpenTelemetry](https://opentelemetry.io/) tracing for SvelteKit operations.
883
+ * @default { server: false }
884
+ */
885
+ tracing?: {
886
+ /**
887
+ * Enables server-side [OpenTelemetry](https://opentelemetry.io/) span emission for SvelteKit operations including the [`handle` hook](https://svelte.dev/docs/kit/hooks#Server-hooks-handle), [`load` functions](https://svelte.dev/docs/kit/load), [form actions](https://svelte.dev/docs/kit/form-actions), and [remote functions](https://svelte.dev/docs/kit/remote-functions). Tracing — and more significantly, observability instrumentation — can have a nontrivial overhead, so consider whether you really need it, or if it might be more appropriate to turn it on in development and preview environments only.
888
+ * @default false
889
+ */
890
+ server?: boolean;
891
+ };
907
892
  typescript?: {
908
893
  /**
909
894
  * A function that allows you to edit the generated `tsconfig.json`. You can mutate the config (recommended) or return a new one.
@@ -1078,7 +1063,7 @@ export type Transport = Record<string, Transporter>;
1078
1063
  */
1079
1064
  export interface Transporter<
1080
1065
  T = any,
1081
- U = Exclude<any, false | 0 | '' | null | undefined | typeof NaN>
1066
+ U = any /* minus falsy values, but we can't properly express that */
1082
1067
  > {
1083
1068
  encode: (value: T) => false | U;
1084
1069
  decode: (data: U) => T;
@@ -1437,7 +1422,7 @@ export type AfterNavigate = (Navigation | NavigationEnter) & {
1437
1422
  };
1438
1423
 
1439
1424
  /**
1440
- * The shape of the [`page`](https://svelte.dev/docs/kit/$app-state#page) reactive object and the [`$page`](https://svelte.dev/docs/kit/$app-stores) store.
1425
+ * The shape of the [`page`](https://svelte.dev/docs/kit/$app-state#page) reactive object.
1441
1426
  */
1442
1427
  export interface Page<
1443
1428
  Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>,
@@ -1446,7 +1431,7 @@ export interface Page<
1446
1431
  /**
1447
1432
  * The URL of the current page.
1448
1433
  */
1449
- url: URL & { pathname: ResolvedPathname };
1434
+ url: ReadonlyURL & { readonly pathname: ResolvedPathname | (string & {}) };
1450
1435
  /**
1451
1436
  * The parameters of the current page - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object.
1452
1437
  */
@@ -1485,7 +1470,64 @@ export interface Page<
1485
1470
  /**
1486
1471
  * The shape of a param matcher. See [matching](https://svelte.dev/docs/kit/advanced-routing#Matching) for more info.
1487
1472
  */
1488
- export type ParamMatcher = (param: string) => boolean;
1473
+ export type ParamMatcher<Output = any> = StandardSchemaV1<string, Output>;
1474
+
1475
+ /**
1476
+ * A value that can be parsed from a URL param and losslessly encoded with `String(...)`.
1477
+ */
1478
+ export type ParamValue = string | number | boolean | bigint;
1479
+
1480
+ /**
1481
+ * A param matcher definition passed to [`defineParams`](https://svelte.dev/docs/kit/@sveltejs-kit#defineParams).
1482
+ */
1483
+ export type ParamDefinition =
1484
+ | ((param: string) => ParamValue | undefined)
1485
+ | StandardSchemaV1<string, ParamValue>;
1486
+
1487
+ /**
1488
+ * The return type of [`defineParams`](https://svelte.dev/docs/kit/@sveltejs-kit#defineParams).
1489
+ */
1490
+ export type DefinedParams<T extends Record<string, ParamDefinition>> = {
1491
+ readonly [K in keyof T]: ParamEntry<T[K]>;
1492
+ };
1493
+
1494
+ /**
1495
+ * Normalizes a property of defineParams (schema or function) to standard schema.
1496
+ */
1497
+ type ParamEntry<M> =
1498
+ M extends StandardSchemaV1<any, any>
1499
+ ? StandardSchemaV1.InferOutput<M> extends ParamValue
1500
+ ? StandardSchemaV1<any, M>
1501
+ : StandardSchemaV1<any, never>
1502
+ : M extends (param: string) => infer R
1503
+ ? Exclude<R, undefined> extends ParamValue
1504
+ ? StandardSchemaV1<any, Exclude<R, undefined>>
1505
+ : StandardSchemaV1<any, never>
1506
+ : never;
1507
+
1508
+ /**
1509
+ * Extracts the param type from a matcher.
1510
+ */
1511
+ export type MatcherParam<M extends StandardSchemaV1<any, any>> =
1512
+ M extends StandardSchemaV1<any, infer Inner>
1513
+ ? Inner extends ParamValue
1514
+ ? Inner
1515
+ : Inner extends StandardSchemaV1<any, any>
1516
+ ? StandardSchemaV1.InferOutput<Inner> extends ParamValue
1517
+ ? StandardSchemaV1.InferOutput<Inner>
1518
+ : never
1519
+ : never
1520
+ : never;
1521
+
1522
+ /**
1523
+ * Define [parameter matchers](https://svelte.dev/docs/kit/advanced-routing#Matching) for your app.
1524
+ *
1525
+ * @template T
1526
+ * @param definitions
1527
+ */
1528
+ export function defineParams<T extends Record<string, ParamDefinition>>(
1529
+ definitions: T
1530
+ ): DefinedParams<T>;
1489
1531
 
1490
1532
  /**
1491
1533
  * A single entry yielded by [`requested`](https://svelte.dev/docs/kit/$app-server#requested)
@@ -1692,7 +1734,9 @@ export interface ResolveOptions {
1692
1734
  */
1693
1735
  filterSerializedResponseHeaders?: (name: string, value: string) => boolean;
1694
1736
  /**
1695
- * Determines what should be added to the `<head>` tag to preload it.
1737
+ * Determines which files should be preloaded. Files are preloaded via `<link>` tags added to the
1738
+ * `<head>` tag; if `output.linkHeaderPreload` is enabled, dynamically rendered pages use the
1739
+ * [`Link` response header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link) instead.
1696
1740
  * By default, `js` and `css` files will be preloaded.
1697
1741
  * @param input the type of the file and its path
1698
1742
  */
@@ -1727,19 +1771,24 @@ export interface ServerInitOptions {
1727
1771
  read?: (file: string) => MaybePromise<ReadableStream | null>;
1728
1772
  }
1729
1773
 
1774
+ /**
1775
+ * Information required to instantiate a new `Server` instance.
1776
+ */
1730
1777
  export interface SSRManifest {
1778
+ /** The directory where SvelteKit keeps its stuff, including static assets (such as JS and CSS) and internally-used routes. */
1731
1779
  appDir: string;
1780
+ /** The `base` and `appDir` settings combined without a leading slash. */
1732
1781
  appPath: string;
1733
1782
  /** Static files from `config.files.assets` and the service worker (if any). */
1734
1783
  assets: Set<string>;
1735
1784
  mimeTypes: Record<string, string>;
1736
1785
 
1737
- /** private fields */
1786
+ /** @internal private fields */
1738
1787
  _: {
1739
1788
  client: BuildData['client'];
1740
1789
  nodes: SSRNodeLoader[];
1741
1790
  /** hashed filename -> import to that file */
1742
- remotes: Record<string, () => Promise<any>>;
1791
+ remotes: Record<string, () => Promise<{ default: Record<string, any> }>>;
1743
1792
  routes: SSRRoute[];
1744
1793
  prerendered_routes: Set<string>;
1745
1794
  matchers: () => Promise<Record<string, ParamMatcher>>;
@@ -1930,6 +1979,14 @@ export interface Snapshot<T = any> {
1930
1979
  restore: (snapshot: T) => void;
1931
1980
  }
1932
1981
 
1982
+ export type ReadonlyURLSearchParams = Omit<URLSearchParams, 'set' | 'append' | 'delete' | 'sort'>;
1983
+
1984
+ export type ReadonlyURL = Readonly<
1985
+ Omit<URL, 'searchParams'> & {
1986
+ searchParams: ReadonlyURLSearchParams;
1987
+ }
1988
+ >;
1989
+
1933
1990
  // If T is unknown or has an index signature, the types below will recurse indefinitely and create giant unions that TS can't handle
1934
1991
  type WillRecurseIndefinitely<T> = unknown extends T ? true : string extends keyof T ? true : false;
1935
1992
 
@@ -2284,8 +2341,8 @@ export type RemoteQueryUpdate =
2284
2341
  | RemoteQueryOverride;
2285
2342
 
2286
2343
  export type RemoteResource<T> = Promise<T> & {
2287
- /** The error in case the query fails. Most often this is a [`HttpError`](https://svelte.dev/docs/kit/@sveltejs-kit#HttpError) but it isn't guaranteed to be. */
2288
- get error(): any;
2344
+ /** The error in case the query fails. */
2345
+ get error(): App.Error | undefined;
2289
2346
  /** `true` before the first result is available and during refreshes */
2290
2347
  get loading(): boolean;
2291
2348
  } & (
@@ -0,0 +1,84 @@
1
+ import { DEV } from 'esm-env';
2
+ // we use the export subpath to conditionally import the client/server `get_origin`
3
+ // so that `node:async_hooks` isn't pulled into the client build
4
+ import { get_origin } from '#internal';
5
+ import { matches_external_allowlist_entry } from '../utils/url.js';
6
+
7
+ // See https://datatracker.ietf.org/doc/html/rfc2606 - no domains under the .invalid TLD can be registered
8
+ const REDIRECT_BASE = 'https://sveltekit-redirect.invalid';
9
+
10
+ /**
11
+ * Whether a redirect location is absolute, i.e. not a root-relative or path-relative URL,
12
+ * and not pointing to the same origin as we're currently on (if determineable).
13
+ * @param {string} location
14
+ */
15
+ export function is_external_location(location) {
16
+ const origin = get_origin();
17
+
18
+ try {
19
+ return !matches_external_allowlist_entry(location, origin ?? REDIRECT_BASE);
20
+ } catch {
21
+ return true;
22
+ }
23
+ }
24
+
25
+ /**
26
+ * @param {string} location
27
+ */
28
+ function is_javascript_location(location) {
29
+ try {
30
+ return new URL(location, REDIRECT_BASE).protocol === 'javascript:';
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * @param {string} location
38
+ * @param {{ external?: boolean | string[] }} [options]
39
+ */
40
+ export function validate_redirect_location(location, options) {
41
+ if (!is_external_location(location)) return;
42
+
43
+ const external = options?.external;
44
+
45
+ if (!external) {
46
+ throw new Error(
47
+ DEV
48
+ ? `Cannot redirect to external URL ${JSON.stringify(location)}. ` +
49
+ 'To redirect to an external URL, pass `{ external: true }` or an allowlist of permitted origins as the third argument to `redirect`'
50
+ : 'Cannot redirect to external URL unless explicitly allowed'
51
+ );
52
+ }
53
+
54
+ if (external === true) {
55
+ if (is_javascript_location(location)) {
56
+ throw new Error(
57
+ DEV
58
+ ? `Cannot redirect to ${JSON.stringify(location)} with \`{ external: true }\`. ` +
59
+ 'The `:javascript` protocol must be explicitly listed in the `external` allowlist'
60
+ : 'Cannot redirect to external URL unless explicitly allowed'
61
+ );
62
+ }
63
+
64
+ return;
65
+ }
66
+
67
+ if (Array.isArray(external)) {
68
+ if (!external.some((allowed) => matches_external_allowlist_entry(location, allowed))) {
69
+ throw new Error(
70
+ DEV
71
+ ? `Cannot redirect to ${JSON.stringify(location)}: URL origin is not included in the \`external\` allowlist`
72
+ : 'Cannot redirect to external URL unless explicitly allowed'
73
+ );
74
+ }
75
+
76
+ return;
77
+ }
78
+
79
+ throw new Error(
80
+ DEV
81
+ ? '`redirect` options.external must be `true` or an array of allowed origins'
82
+ : 'Invalid redirect options.external value'
83
+ );
84
+ }
@@ -10,12 +10,14 @@ import { isCSSRequest, loadEnv, buildErrorMessage } from 'vite';
10
10
  import { createReadableStream, getRequest, setResponse } from '../../../exports/node/index.js';
11
11
  import { coalesce_to_error } from '../../../utils/error.js';
12
12
  import { resolve_entry } from '../../../utils/filesystem.js';
13
+ import { load_and_validate_params } from '../../../utils/params.js';
13
14
  import { from_fs, to_fs } from '../../../utils/vite.js';
14
15
  import { posixify } from '../../../utils/os.js';
15
16
  import { load_error_page } from '../../../core/config/index.js';
16
17
  import { SVELTE_KIT_ASSETS } from '../../../constants.js';
17
18
  import * as sync from '../../../core/sync/sync.js';
18
19
  import { get_mime_lookup, get_runtime_base } from '../../../core/utils.js';
20
+ import '../../../utils/mime.js'; // extend mrmime with additional types (affects sirv too)
19
21
  import { compact } from '../../../utils/array.js';
20
22
  import { is_chrome_devtools_request, not_found } from '../utils.js';
21
23
  import { SCHEME } from '../../../utils/url.js';
@@ -83,6 +85,10 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
83
85
  vite.config.logger.error(msg, { error: err });
84
86
  }
85
87
 
88
+ // TODO this is inadequate — it doesn't reliably show the overlay on every page load,
89
+ // and when it does appear it may immediately vanish. `vite.ws.send` broadcasts
90
+ // to all connected clients, even ones that are unaffected by the error.
91
+ // we need a more considered approach
86
92
  vite.ws.send({
87
93
  type: 'error',
88
94
  err: /** @type {import('vite').ErrorPayload['err']} */ ({
@@ -110,10 +116,17 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
110
116
  return { module, module_node, url };
111
117
  }
112
118
 
113
- function update_manifest() {
119
+ async function update_manifest() {
114
120
  try {
115
121
  ({ manifest_data } = sync.create(svelte_config, root));
116
122
 
123
+ await load_and_validate_params({
124
+ routes: manifest_data.routes,
125
+ params_path: manifest_data.params,
126
+ root,
127
+ load: (file) => loud_ssr_load_module(file)
128
+ });
129
+
117
130
  if (manifest_error) {
118
131
  manifest_error = null;
119
132
  vite.ws.send({ type: 'full-reload' });
@@ -292,22 +305,18 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
292
305
  })
293
306
  ),
294
307
  matchers: async () => {
295
- /** @type {Record<string, import('@sveltejs/kit').ParamMatcher>} */
296
- const matchers = {};
297
-
298
- for (const key in manifest_data.matchers) {
299
- const file = manifest_data.matchers[key];
300
- const url = path.resolve(root, file);
301
- const module = await vite.ssrLoadModule(url, { fixStacktrace: true });
302
-
303
- if (module.match) {
304
- matchers[key] = module.match;
305
- } else {
306
- throw new Error(`${file} does not export a \`match\` function`);
307
- }
308
+ if (!manifest_data.params) return {};
309
+
310
+ const url = path.resolve(root, manifest_data.params);
311
+ const module = await vite.ssrLoadModule(url, { fixStacktrace: true });
312
+
313
+ if (!module.params) {
314
+ throw new Error(
315
+ `${manifest_data.params} does not export \`params\` from \`defineParams\``
316
+ );
308
317
  }
309
318
 
310
- return matchers;
319
+ return module.params;
311
320
  }
312
321
  }
313
322
  };
@@ -324,7 +333,9 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
324
333
  return error.stack?.replaceAll('\0', ''); // remove null bytes from e.g. virtual module IDs, or the response will fail
325
334
  }
326
335
 
327
- update_manifest();
336
+ await update_manifest();
337
+
338
+ const params_file = resolve_entry(svelte_config.kit.files.params);
328
339
 
329
340
  /**
330
341
  * @param {string} event
@@ -334,7 +345,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
334
345
  vite.watcher.on(event, (file) => {
335
346
  if (
336
347
  file.startsWith(svelte_config.kit.files.routes + path.sep) ||
337
- file.startsWith(svelte_config.kit.files.params + path.sep) ||
348
+ (params_file && file === params_file) ||
338
349
  svelte_config.kit.moduleExtensions.some((ext) => file.endsWith(`.remote${ext}`)) ||
339
350
  // in contrast to server hooks, client hooks are written to the client manifest
340
351
  // and therefore need rebuilding when they are added/removed
@@ -517,7 +528,7 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
517
528
  read: (file) => createReadableStream(from_fs(file))
518
529
  });
519
530
 
520
- const request = await getRequest({
531
+ const request = getRequest({
521
532
  base,
522
533
  request: req
523
534
  });
@@ -568,10 +579,10 @@ export async function dev(vite, vite_config, svelte_config, get_remotes, root) {
568
579
  if (rendered.status === 404) {
569
580
  // @ts-expect-error
570
581
  serve_static_middleware.handle(req, res, () => {
571
- void setResponse(res, rendered);
582
+ setResponse(res, rendered);
572
583
  });
573
584
  } else {
574
- void setResponse(res, rendered);
585
+ setResponse(res, rendered);
575
586
  }
576
587
  } catch (e) {
577
588
  const error = coalesce_to_error(e);