@sveltejs/kit 3.0.0-next.0 → 3.0.0-next.10

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 (127) hide show
  1. package/package.json +33 -21
  2. package/src/core/adapt/builder.js +29 -43
  3. package/src/core/adapt/index.js +1 -4
  4. package/src/core/config/index.js +132 -71
  5. package/src/core/config/options.js +294 -244
  6. package/src/core/config/types.d.ts +1 -1
  7. package/src/core/env.js +121 -5
  8. package/src/core/generate_manifest/index.js +12 -15
  9. package/src/core/postbuild/analyse.js +7 -19
  10. package/src/core/postbuild/crawl.js +22 -6
  11. package/src/core/postbuild/prerender.js +42 -23
  12. package/src/core/sync/create_manifest_data/index.js +53 -50
  13. package/src/core/sync/sync.js +0 -2
  14. package/src/core/sync/write_client_manifest.js +8 -15
  15. package/src/core/sync/write_env.js +2 -1
  16. package/src/core/sync/write_non_ambient.js +12 -9
  17. package/src/core/sync/write_server.js +13 -14
  18. package/src/core/sync/write_tsconfig.js +16 -8
  19. package/src/core/sync/write_tsconfig_test/package.json +7 -0
  20. package/src/core/sync/write_types/index.js +28 -24
  21. package/src/core/utils.js +2 -2
  22. package/src/exports/env/index.js +12 -0
  23. package/src/exports/hooks/index.js +3 -9
  24. package/src/exports/hooks/sequence.js +3 -2
  25. package/src/exports/index.js +35 -13
  26. package/src/exports/internal/client.js +5 -0
  27. package/src/exports/internal/env.js +2 -2
  28. package/src/exports/internal/index.js +1 -90
  29. package/src/exports/internal/{event.js → server/event.js} +1 -2
  30. package/src/exports/internal/server/index.js +33 -0
  31. package/src/exports/internal/shared.js +89 -0
  32. package/src/exports/node/index.js +62 -15
  33. package/src/exports/params.js +63 -0
  34. package/src/exports/public.d.ts +323 -170
  35. package/src/exports/url.js +86 -0
  36. package/src/exports/vite/build/build_server.js +53 -59
  37. package/src/exports/vite/build/remote.js +18 -11
  38. package/src/exports/vite/build/utils.js +0 -8
  39. package/src/exports/vite/dev/index.js +42 -44
  40. package/src/exports/vite/index.js +810 -593
  41. package/src/exports/vite/preview/index.js +29 -22
  42. package/src/exports/vite/static_analysis/index.js +2 -4
  43. package/src/exports/vite/static_analysis/types.d.ts +14 -0
  44. package/src/exports/vite/utils.js +21 -36
  45. package/src/runtime/app/env/internal.js +4 -4
  46. package/src/runtime/app/env/types.d.ts +1 -1
  47. package/src/runtime/app/environment/index.js +6 -0
  48. package/src/runtime/app/forms.js +24 -7
  49. package/src/runtime/app/paths/client.js +4 -10
  50. package/src/runtime/app/paths/internal/client.js +4 -2
  51. package/src/runtime/app/paths/internal/server.js +2 -23
  52. package/src/runtime/app/paths/public.d.ts +0 -28
  53. package/src/runtime/app/paths/server.js +9 -5
  54. package/src/runtime/app/server/remote/command.js +0 -3
  55. package/src/runtime/app/server/remote/form.js +27 -15
  56. package/src/runtime/app/server/remote/prerender.js +29 -36
  57. package/src/runtime/app/server/remote/query.js +101 -99
  58. package/src/runtime/app/server/remote/requested.js +22 -14
  59. package/src/runtime/app/server/remote/shared.js +30 -26
  60. package/src/runtime/app/state/client.js +1 -2
  61. package/src/runtime/app/stores.js +13 -76
  62. package/src/runtime/client/bundle.js +1 -1
  63. package/src/runtime/client/client-entry.js +3 -0
  64. package/src/runtime/client/client.js +411 -318
  65. package/src/runtime/client/entry.js +24 -3
  66. package/src/runtime/client/fetcher.js +3 -2
  67. package/src/runtime/client/ndjson.js +6 -33
  68. package/src/runtime/client/payload.js +17 -0
  69. package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
  70. package/src/runtime/client/remote-functions/command.svelte.js +7 -32
  71. package/src/runtime/client/remote-functions/form.svelte.js +183 -112
  72. package/src/runtime/client/remote-functions/prerender.svelte.js +29 -8
  73. package/src/runtime/client/remote-functions/query/index.js +7 -14
  74. package/src/runtime/client/remote-functions/query/instance.svelte.js +63 -18
  75. package/src/runtime/client/remote-functions/query/proxy.js +3 -3
  76. package/src/runtime/client/remote-functions/query-batch.svelte.js +60 -68
  77. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +60 -18
  78. package/src/runtime/client/remote-functions/query-live/iterator.js +36 -55
  79. package/src/runtime/client/remote-functions/shared.svelte.js +88 -62
  80. package/src/runtime/client/sse.js +32 -0
  81. package/src/runtime/client/state.svelte.js +65 -49
  82. package/src/runtime/client/stream.js +38 -0
  83. package/src/runtime/client/types.d.ts +11 -7
  84. package/src/runtime/client/utils.js +0 -96
  85. package/src/runtime/components/root.svelte +66 -0
  86. package/src/runtime/env/dynamic/private.js +7 -0
  87. package/src/runtime/env/dynamic/public.js +7 -0
  88. package/src/runtime/env/static/private.js +6 -0
  89. package/src/runtime/env/static/public.js +6 -0
  90. package/src/runtime/form-utils.js +100 -22
  91. package/src/runtime/server/cookie.js +69 -52
  92. package/src/runtime/server/csrf.js +65 -0
  93. package/src/runtime/server/data/index.js +14 -18
  94. package/src/runtime/server/env_module.js +0 -5
  95. package/src/runtime/server/index.js +2 -2
  96. package/src/runtime/server/page/actions.js +41 -26
  97. package/src/runtime/server/page/index.js +9 -15
  98. package/src/runtime/server/page/load_data.js +1 -1
  99. package/src/runtime/server/page/render.js +112 -187
  100. package/src/runtime/server/page/respond_with_error.js +7 -8
  101. package/src/runtime/server/page/server_routing.js +27 -18
  102. package/src/runtime/server/remote.js +355 -216
  103. package/src/runtime/server/respond.js +97 -61
  104. package/src/runtime/server/utils.js +32 -9
  105. package/src/runtime/shared.js +83 -13
  106. package/src/runtime/telemetry/otel.js +1 -1
  107. package/src/runtime/types.d.ts +8 -0
  108. package/src/types/ambient.d.ts +10 -5
  109. package/src/types/global-private.d.ts +15 -22
  110. package/src/types/internal.d.ts +89 -74
  111. package/src/types/private.d.ts +33 -1
  112. package/src/utils/error.js +24 -4
  113. package/src/utils/import.js +6 -1
  114. package/src/utils/imports.js +83 -0
  115. package/src/utils/mime.js +9 -0
  116. package/src/utils/params.js +66 -0
  117. package/src/utils/routing.js +90 -44
  118. package/src/utils/shared-iterator.js +5 -0
  119. package/src/utils/streaming.js +14 -4
  120. package/src/utils/url.js +20 -2
  121. package/src/version.js +1 -1
  122. package/types/index.d.ts +467 -537
  123. package/types/index.d.ts.map +22 -39
  124. package/src/core/sync/write_root.js +0 -148
  125. package/src/exports/internal/server.js +0 -22
  126. package/src/types/synthetic/$lib.md +0 -5
  127. /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
@@ -1,6 +1,5 @@
1
1
  /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
2
-
3
- import { HttpError, Redirect, ActionFailure, ValidationError } from './internal/index.js';
2
+ import { HttpError, Redirect, ActionFailure, ValidationError } from './internal/shared.js';
4
3
  import { BROWSER, DEV } from 'esm-env';
5
4
  import {
6
5
  add_data_suffix,
@@ -11,8 +10,10 @@ import {
11
10
  strip_resolution_suffix
12
11
  } from '../runtime/pathname.js';
13
12
  import { text_encoder } from '../runtime/utils.js';
13
+ import { validate_redirect_location } from './url.js';
14
14
 
15
15
  export { VERSION } from '../version.js';
16
+ export { defineParams } from './params.js';
16
17
 
17
18
  // Keep the status codes as `number` because restricting to certain numbers makes it unnecessarily hard to use compared to the benefits
18
19
  // (we have runtime errors already to check for invalid codes). Also see https://github.com/sveltejs/kit/issues/11780
@@ -26,10 +27,10 @@ export { VERSION } from '../version.js';
26
27
  * return an error response without invoking `handleError`.
27
28
  * Make sure you're not catching the thrown error, which would prevent SvelteKit from handling it.
28
29
  * @param {number} status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses). Must be in the range 400-599.
29
- * @param {App.Error} body An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
30
+ * @param {Omit<App.Error, 'status'> & { status?: App.Error['status'] }} body An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
30
31
  * @overload
31
32
  * @param {number} status
32
- * @param {App.Error} body
33
+ * @param {Omit<App.Error, 'status'> & { status?: App.Error['status'] }} body
33
34
  * @return {never}
34
35
  * @throws {import('./public.js').HttpError} This error instructs SvelteKit to initiate HTTP error handling.
35
36
  * @throws {Error} If the provided status is invalid (not between 400 and 599).
@@ -40,10 +41,10 @@ export { VERSION } from '../version.js';
40
41
  * return an error response without invoking `handleError`.
41
42
  * Make sure you're not catching the thrown error, which would prevent SvelteKit from handling it.
42
43
  * @param {number} status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses). Must be in the range 400-599.
43
- * @param {{ message: string } extends App.Error ? App.Error | string | undefined : never} [body] An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
44
+ * @param {{ status: number; message: string } extends App.Error ? string | void | undefined : never} body The error message.
44
45
  * @overload
45
46
  * @param {number} status
46
- * @param {{ message: string } extends App.Error ? App.Error | string | undefined : never} [body]
47
+ * @param {{ status: number; message: string } extends App.Error ? string | void | undefined : never} body
47
48
  * @return {never}
48
49
  * @throws {import('./public.js').HttpError} This error instructs SvelteKit to initiate HTTP error handling.
49
50
  * @throws {Error} If the provided status is invalid (not between 400 and 599).
@@ -54,17 +55,34 @@ export { VERSION } from '../version.js';
54
55
  * return an error response without invoking `handleError`.
55
56
  * Make sure you're not catching the thrown error, which would prevent SvelteKit from handling it.
56
57
  * @param {number} status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses). Must be in the range 400-599.
57
- * @param {{ message: string } extends App.Error ? App.Error | string | undefined : never} body An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
58
+ * @param {string} body The error message.
59
+ * @param {{ status: number; message: string } extends App.Error ? never : Omit<App.Error, 'status' | 'message'>} properties Additional properties of the App.Error type.
60
+ * @overload
61
+ * @param {number} status
62
+ * @param {string} body
63
+ * @param {{ status: number; message: string } extends App.Error ? never : Omit<App.Error, 'status' | 'message'>} properties
58
64
  * @return {never}
59
65
  * @throws {import('./public.js').HttpError} This error instructs SvelteKit to initiate HTTP error handling.
60
66
  * @throws {Error} If the provided status is invalid (not between 400 and 599).
61
67
  */
62
- export function error(status, body) {
68
+ /**
69
+ * Throws an error with a HTTP status code and an optional message.
70
+ * When called during request handling, this will cause SvelteKit to
71
+ * return an error response without invoking `handleError`.
72
+ * Make sure you're not catching the thrown error, which would prevent SvelteKit from handling it.
73
+ * @param {any} status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses). Must be in the range 400-599.
74
+ * @param {any} [body] An object that conforms to the App.Error type. If a string is passed, it will be used as the message property.
75
+ * @param {any} [properties] Additional properties of the App.Error type when passing a string message.
76
+ * @return {never}
77
+ * @throws {import('./public.js').HttpError} This error instructs SvelteKit to initiate HTTP error handling.
78
+ * @throws {Error} If the provided status is invalid (not between 400 and 599).
79
+ */
80
+ export function error(status, body, properties) {
63
81
  if ((!BROWSER || DEV) && (isNaN(status) || status < 400 || status > 599)) {
64
82
  throw new Error(`HTTP error status codes must be between 400 and 599 — ${status} is invalid`);
65
83
  }
66
84
 
67
- throw new HttpError(status, body);
85
+ throw new HttpError(status, body, properties);
68
86
  }
69
87
 
70
88
  /**
@@ -92,19 +110,23 @@ export function isHttpError(e, status) {
92
110
  *
93
111
  * @param {300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | ({} & number)} status The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages). Must be in the range 300-308.
94
112
  * @param {string | URL} location The location to redirect to.
113
+ * @param {{ external?: boolean | string[] }} [options] To redirect to an external URL, you must pass `{ external: true }` to allow any external URL except `javascript:` URLs, or `{ external: [...] }` with an allowlist of permitted origins.
95
114
  * @throws {import('./public.js').Redirect} This error instructs SvelteKit to redirect to the specified location.
96
- * @throws {Error} If the provided status is invalid or the location cannot be used as a header value.
115
+ * @throws {Error} If the provided status is invalid, the location cannot be used as a header value, or the location is an external URL without permission.
97
116
  * @return {never}
98
117
  */
99
- export function redirect(status, location) {
118
+ export function redirect(status, location, options) {
100
119
  if ((!BROWSER || DEV) && (isNaN(status) || status < 300 || status > 308)) {
101
120
  throw new Error('Invalid status code');
102
121
  }
103
122
 
123
+ const href = location.toString();
124
+ validate_redirect_location(href, options);
125
+
104
126
  throw new Redirect(
105
127
  // @ts-ignore
106
128
  status,
107
- location.toString()
129
+ href
108
130
  );
109
131
  }
110
132
 
@@ -212,7 +234,7 @@ export function isActionFailure(e) {
212
234
  * ```ts
213
235
  * import { invalid } from '@sveltejs/kit';
214
236
  * import { form } from '$app/server';
215
- * import { tryLogin } from '$lib/server/auth';
237
+ * import { tryLogin } from '#lib/server/auth';
216
238
  * import * as v from 'valibot';
217
239
  *
218
240
  * export const login = form(
@@ -0,0 +1,5 @@
1
+ export function get_origin() {
2
+ return window.location.origin;
3
+ }
4
+
5
+ export * from './shared.js';
@@ -1,7 +1,7 @@
1
1
  /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
2
2
  /** @import { EnvVarConfig } from '@sveltejs/kit' */
3
3
 
4
- import { stackless } from '../vite/utils.js';
4
+ import { stackless } from '../../utils/error.js';
5
5
 
6
6
  const MISSING = {
7
7
  message: `Value is missing. If it is optional, add a Standard Schema validator declaring it as such.`
@@ -16,7 +16,7 @@ const ASYNC_VALIDATOR = {
16
16
  };
17
17
 
18
18
  /**
19
- * @param {Record<string, EnvVarConfig<any>>} variables
19
+ * @param {Record<string, EnvVarConfig<any> | undefined>} variables
20
20
  * @param {string | undefined} value
21
21
  * @param {string} name
22
22
  * @param {Record<string, StandardSchemaV1.Issue[]>} issues
@@ -1,90 +1 @@
1
- /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
2
-
3
- export class HttpError {
4
- /**
5
- * @param {number} status
6
- * @param {{message: string} extends App.Error ? (App.Error | string | undefined) : App.Error} body
7
- */
8
- constructor(status, body) {
9
- this.status = status;
10
- if (typeof body === 'string') {
11
- this.body = { message: body };
12
- } else if (body) {
13
- this.body = body;
14
- } else {
15
- this.body = { message: `Error: ${status}` };
16
- }
17
- }
18
-
19
- toString() {
20
- return JSON.stringify(this.body);
21
- }
22
- }
23
-
24
- export class Redirect {
25
- /**
26
- * @param {300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308} status
27
- * @param {string} location
28
- */
29
- constructor(status, location) {
30
- try {
31
- new Headers({ location });
32
- } catch {
33
- throw new Error(
34
- `Invalid redirect location ${JSON.stringify(location)}: ` +
35
- 'this string contains characters that cannot be used in HTTP headers'
36
- );
37
- }
38
-
39
- this.status = status;
40
- this.location = location;
41
- }
42
- }
43
-
44
- /**
45
- * 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.
46
- * `SvelteKitError` goes through `handleError`.
47
- * @extends Error
48
- */
49
- export class SvelteKitError extends Error {
50
- /**
51
- * @param {number} status
52
- * @param {string} text
53
- * @param {string} message
54
- */
55
- constructor(status, text, message) {
56
- super(message);
57
- this.status = status;
58
- this.text = text;
59
- }
60
- }
61
-
62
- /**
63
- * @template [T=undefined]
64
- */
65
- export class ActionFailure {
66
- /**
67
- * @param {number} status
68
- * @param {T} data
69
- */
70
- constructor(status, data) {
71
- this.status = status;
72
- this.data = data;
73
- }
74
- }
75
-
76
- /**
77
- * Error thrown when form validation fails imperatively
78
- */
79
- export class ValidationError extends Error {
80
- /**
81
- * @param {StandardSchemaV1.Issue[]} issues
82
- */
83
- constructor(issues) {
84
- super('Validation failed');
85
- this.name = 'ValidationError';
86
- this.issues = issues;
87
- }
88
- }
89
-
90
- export { init_remote_functions } from './remote-functions.js';
1
+ export * from '#internal';
@@ -1,8 +1,7 @@
1
1
  /** @import { RequestEvent } from '@sveltejs/kit' */
2
2
  /** @import { RequestStore } from 'types' */
3
3
  /** @import { AsyncLocalStorage } from 'node:async_hooks' */
4
-
5
- import { IN_WEBCONTAINER } from '../../runtime/server/constants.js';
4
+ import { IN_WEBCONTAINER } from '../../../runtime/server/constants.js';
6
5
 
7
6
  /** @type {RequestStore | null} */
8
7
  let sync_store = null;
@@ -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,8 +1,11 @@
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
+ /** @type {WeakMap<import('http').IncomingMessage, (chunk: Buffer) => void>} */
7
+ const body_data_listeners = new WeakMap();
8
+
6
9
  /**
7
10
  * @param {import('http').IncomingMessage} req
8
11
  * @param {number} [body_size_limit]
@@ -47,17 +50,19 @@ function get_raw_body(req, body_size_limit) {
47
50
  return;
48
51
  }
49
52
 
50
- req.on('error', (error) => {
53
+ /** @param {Error} error */
54
+ const on_error = (error) => {
51
55
  cancelled = true;
52
56
  controller.error(error);
53
- });
57
+ };
54
58
 
55
- req.on('end', () => {
59
+ const on_end = () => {
56
60
  if (cancelled) return;
57
61
  controller.close();
58
- });
62
+ };
59
63
 
60
- req.on('data', (chunk) => {
64
+ /** @param {Buffer} chunk */
65
+ const on_data = (chunk) => {
61
66
  if (cancelled) return;
62
67
 
63
68
  size += chunk.length;
@@ -89,7 +94,12 @@ function get_raw_body(req, body_size_limit) {
89
94
  if (controller.desiredSize === null || controller.desiredSize <= 0) {
90
95
  req.pause();
91
96
  }
92
- });
97
+ };
98
+
99
+ req.on('error', on_error);
100
+ req.on('end', on_end);
101
+ req.on('data', on_data);
102
+ body_data_listeners.set(req, on_data);
93
103
  },
94
104
 
95
105
  pull() {
@@ -109,11 +119,9 @@ function get_raw_body(req, body_size_limit) {
109
119
  * base: string;
110
120
  * bodySizeLimit?: number;
111
121
  * }} options
112
- * @returns {Promise<Request>}
122
+ * @returns {Request}
113
123
  */
114
- // TODO 3.0 make the signature synchronous?
115
- // eslint-disable-next-line @typescript-eslint/require-await
116
- export async function getRequest({ request, base, bodySizeLimit }) {
124
+ export function getRequest({ request, base, bodySizeLimit }) {
117
125
  let headers = /** @type {Record<string, string>} */ (request.headers);
118
126
  if (request.httpVersionMajor >= 2) {
119
127
  // the Request constructor rejects headers with ':' in the name
@@ -154,14 +162,53 @@ export async function getRequest({ request, base, bodySizeLimit }) {
154
162
  });
155
163
  }
156
164
 
165
+ /**
166
+ * Drains any unconsumed request body once the response has been sent. When a
167
+ * route doesn't read the request body (for example a page route receiving a
168
+ * POST), the unread bytes remain buffered in the socket. On keep-alive
169
+ * connections Node's HTTP parser then reads those leftover bytes as the next
170
+ * request, fails to parse them, and resets the connection — losing any
171
+ * pipelined request. Resuming the request discards the bytes so the connection
172
+ * stays usable.
173
+ *
174
+ * Because `get_raw_body` attaches a `data` listener, Node marks the request as
175
+ * being consumed (`req._consuming`) and skips its own automatic drain, so we
176
+ * have to do it ourselves. The whole remaining body is read and discarded; this
177
+ * is the intended trade-off (keeping the connection reusable) over destroying it.
178
+ * @see https://github.com/sveltejs/kit/issues/14916
179
+ * @see https://github.com/sveltejs/kit/issues/15526
180
+ * @param {import('http').ServerResponse} res
181
+ */
182
+ function drain_request(res) {
183
+ const req = res.req;
184
+ if (!req || req.readableEnded || req.destroyed) return;
185
+
186
+ // When the body went unread, get_raw_body's `data` listener is still attached
187
+ // and enqueues into a ReadableStream nobody consumes; it pauses the request at
188
+ // the high water mark, so one chunk sits in memory and the rest stays buffered
189
+ // in the socket. Remove only that `data` listener so the resumed stream drops
190
+ // the remaining bytes instead of re-buffering them. The `end` and `error`
191
+ // listeners stay attached so the body's ReadableStream is still closed (or
192
+ // errored) once draining completes, and a consumer that stopped reading
193
+ // mid-body sees a clean end instead of hanging.
194
+ const on_data = body_data_listeners.get(req);
195
+ if (on_data) {
196
+ req.removeListener('data', on_data);
197
+ body_data_listeners.delete(req);
198
+ }
199
+
200
+ req.resume();
201
+ }
202
+
157
203
  /**
158
204
  * @param {import('http').ServerResponse} res
159
205
  * @param {Response} response
160
- * @returns {Promise<void>}
206
+ * @returns {void}
161
207
  */
162
- // TODO 3.0 make the signature synchronous?
163
- // eslint-disable-next-line @typescript-eslint/require-await
164
- export async function setResponse(res, response) {
208
+ export function setResponse(res, response) {
209
+ res.once('finish', () => drain_request(res));
210
+ res.once('close', () => drain_request(res));
211
+
165
212
  for (const [key, value] of response.headers) {
166
213
  try {
167
214
  res.setHeader(key, key === 'set-cookie' ? response.headers.getSetCookie() : value);
@@ -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
+ }