@sveltejs/kit 3.0.0-next.2 → 3.0.0-next.21
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 +62 -36
- package/src/cli.js +15 -12
- package/src/constants.js +6 -1
- package/src/core/adapt/builder.js +66 -72
- package/src/core/adapt/index.js +17 -6
- package/src/core/config/index.js +134 -80
- package/src/core/config/options.js +295 -273
- package/src/core/config/types.d.ts +1 -1
- package/src/core/env.js +146 -16
- package/src/core/generate_manifest/index.js +18 -15
- package/src/core/postbuild/analyse.js +8 -20
- package/src/core/postbuild/crawl.js +22 -6
- package/src/core/postbuild/entities.js +8 -2
- package/src/core/postbuild/fallback.js +4 -2
- package/src/core/postbuild/prerender.js +222 -70
- package/src/core/postbuild/queue.js +2 -1
- package/src/core/sync/create_manifest_data/conflict.js +1 -1
- package/src/core/sync/create_manifest_data/index.js +82 -62
- package/src/core/sync/sync.js +40 -31
- package/src/core/sync/ts.js +1 -1
- package/src/core/sync/utils.js +22 -2
- package/src/core/sync/{write_non_ambient.js → write_app_types.js} +107 -58
- package/src/core/sync/write_client_manifest.js +14 -29
- package/src/core/sync/write_env.js +7 -5
- package/src/core/sync/write_server.js +37 -45
- package/src/core/sync/write_tsconfig/index.js +274 -0
- package/src/core/sync/write_tsconfig/test-app/package.json +7 -0
- package/src/core/sync/write_tsconfig/utils.js +77 -0
- package/src/core/sync/write_tsconfig/validate.js +128 -0
- package/src/core/sync/write_types/index.js +127 -123
- package/src/core/utils.js +30 -5
- package/src/exports/env/index.js +77 -0
- package/src/exports/env/public.d.ts +55 -0
- package/src/exports/hooks/index.js +3 -9
- package/src/exports/hooks/public.d.ts +195 -0
- package/src/exports/hooks/sequence.js +9 -6
- package/src/exports/index.js +52 -29
- package/src/exports/internal/client.js +5 -0
- package/src/exports/internal/env.js +8 -5
- package/src/exports/internal/index.js +1 -90
- package/src/exports/internal/{event.js → server/event.js} +2 -3
- package/src/exports/internal/server/index.js +37 -0
- package/src/exports/internal/server/telemetry.js +95 -0
- package/src/exports/internal/shared.js +90 -0
- package/src/exports/node/index.js +64 -22
- package/src/exports/params/index.js +70 -0
- package/src/exports/params/public.d.ts +63 -0
- package/src/exports/public.d.ts +50 -1733
- package/src/exports/remote/index.js +11 -0
- package/src/exports/remote/public.d.ts +519 -0
- package/src/exports/url.js +86 -0
- package/src/exports/vite/build/build_server.js +54 -65
- package/src/exports/vite/build/remote.js +24 -19
- package/src/exports/vite/build/utils.js +0 -8
- package/src/exports/vite/dev/index.js +218 -146
- package/src/exports/vite/index.js +1590 -858
- package/src/exports/vite/module_ids.js +2 -2
- package/src/exports/vite/preview/index.js +40 -32
- package/src/exports/vite/public.d.ts +588 -0
- package/src/exports/vite/utils.js +84 -46
- package/src/pathname.js +55 -0
- package/src/runner.js +15 -0
- package/src/runtime/app/env/internal.js +4 -4
- package/src/runtime/app/env/types.d.ts +1 -1
- package/src/runtime/app/environment/index.js +3 -3
- package/src/runtime/app/{forms.js → forms/index.js} +73 -42
- package/src/runtime/app/forms/public.d.ts +2 -0
- package/src/runtime/app/forms/types.d.ts +56 -0
- package/src/runtime/app/internal/transport.js +53 -0
- package/src/runtime/app/manifest/index.js +1 -0
- package/src/runtime/app/{navigation.js → navigation/index.js} +2 -1
- package/src/runtime/app/navigation/public.d.ts +237 -0
- package/src/runtime/app/paths/client.js +37 -37
- package/src/runtime/app/paths/index.js +1 -1
- package/src/runtime/app/paths/internal/client.js +34 -2
- package/src/runtime/app/paths/internal/server.js +6 -23
- package/src/runtime/app/paths/internal.d.ts +3 -0
- package/src/runtime/app/paths/public.d.ts +1 -29
- package/src/runtime/app/paths/server.js +36 -17
- package/src/runtime/app/paths/types.d.ts +11 -19
- package/src/runtime/app/server/index.js +2 -2
- package/src/runtime/app/server/public.d.ts +201 -0
- package/src/runtime/app/server/remote/command.js +13 -11
- package/src/runtime/app/server/remote/form.js +61 -39
- package/src/runtime/app/server/remote/prerender.js +45 -45
- package/src/runtime/app/server/remote/query.js +106 -108
- package/src/runtime/app/server/remote/requested.js +27 -19
- package/src/runtime/app/server/remote/shared.js +76 -76
- package/src/runtime/app/service-worker/index.js +24 -0
- package/src/runtime/app/state/client.js +4 -2
- package/src/runtime/app/state/index.js +7 -5
- package/src/runtime/app/state/public.d.ts +72 -0
- package/src/runtime/app/state/server.js +3 -0
- package/src/runtime/app/stores.js +15 -78
- package/src/runtime/client/bundle.js +1 -1
- package/src/runtime/client/client-entry.js +3 -0
- package/src/runtime/client/client.js +1336 -648
- package/src/runtime/client/constants.js +3 -6
- package/src/runtime/client/entry.js +24 -3
- package/src/runtime/client/fetcher.js +25 -25
- package/src/runtime/client/ndjson.js +1 -1
- package/src/runtime/client/parse.js +1 -1
- 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/command.svelte.js +37 -61
- package/src/runtime/client/remote-functions/form.svelte.js +267 -178
- package/src/runtime/client/remote-functions/prerender.svelte.js +32 -11
- package/src/runtime/client/remote-functions/query/index.js +10 -17
- package/src/runtime/client/remote-functions/query/instance.svelte.js +64 -18
- package/src/runtime/client/remote-functions/query/proxy.js +5 -5
- package/src/runtime/client/remote-functions/query-batch.svelte.js +61 -70
- package/src/runtime/client/remote-functions/query-live/index.js +1 -1
- package/src/runtime/client/remote-functions/query-live/instance.svelte.js +63 -21
- package/src/runtime/client/remote-functions/query-live/iterator.js +15 -12
- package/src/runtime/client/remote-functions/query-live/proxy.js +2 -12
- package/src/runtime/client/remote-functions/shared.svelte.js +90 -65
- package/src/runtime/client/snapshots.js +147 -0
- package/src/runtime/client/state.svelte.js +94 -55
- package/src/runtime/client/stream.js +3 -2
- package/src/runtime/client/types.d.ts +13 -9
- package/src/runtime/client/utils.js +22 -110
- package/src/runtime/components/root.svelte +56 -0
- package/src/runtime/env/dynamic/private.js +7 -0
- package/src/runtime/env/dynamic/public.js +7 -0
- package/src/runtime/env/static/private.js +6 -0
- package/src/runtime/env/static/public.js +6 -0
- package/src/runtime/form-utils.js +172 -61
- package/src/runtime/pathname.js +20 -32
- package/src/runtime/props.svelte.js +72 -0
- package/src/runtime/server/constants.js +0 -3
- package/src/runtime/server/cookie.js +72 -55
- package/src/runtime/server/csrf.js +65 -0
- package/src/runtime/server/data/index.js +49 -53
- package/src/runtime/server/dev.js +22 -0
- package/src/runtime/server/endpoint.js +6 -7
- package/src/runtime/server/env_module.js +0 -5
- package/src/runtime/server/errors.js +160 -0
- package/src/runtime/server/fetch.js +32 -39
- package/src/runtime/server/index.js +90 -64
- package/src/runtime/server/internal.js +71 -0
- package/src/runtime/server/page/actions.js +85 -64
- package/src/runtime/server/page/crypto.js +2 -2
- package/src/runtime/server/page/csp.js +88 -104
- package/src/runtime/server/page/data_serializer.js +24 -25
- package/src/runtime/server/page/index.js +36 -53
- package/src/runtime/server/page/load_data.js +50 -57
- package/src/runtime/server/page/render.js +173 -239
- package/src/runtime/server/page/respond_with_error.js +17 -31
- package/src/runtime/server/page/serialize_data.js +2 -13
- package/src/runtime/server/page/server_routing.js +85 -26
- package/src/runtime/server/remote-functions.js +640 -0
- package/src/runtime/server/respond.js +190 -131
- package/src/runtime/server/sourcemaps.js +183 -0
- package/src/runtime/server/state.js +53 -0
- package/src/runtime/server/utils.js +13 -155
- package/src/runtime/shared.js +20 -40
- package/src/runtime/utils.js +3 -0
- package/src/types/ambient-private.d.ts +11 -1
- package/src/types/ambient.d.ts +87 -36
- package/src/types/global-private.d.ts +25 -25
- package/src/types/internal.d.ts +163 -157
- package/src/types/private.d.ts +41 -1
- package/src/utils/error.js +28 -4
- package/src/utils/escape.js +9 -25
- package/src/utils/features.js +1 -1
- package/src/utils/filesystem.js +1 -23
- package/src/utils/fork.js +7 -2
- package/src/utils/hash.js +21 -0
- package/src/utils/http.js +8 -7
- package/src/utils/import.js +9 -2
- package/src/utils/imports.js +83 -0
- package/src/utils/mime.js +9 -0
- package/src/utils/page_nodes.js +6 -7
- package/src/utils/params.js +67 -0
- package/src/utils/regex.js +9 -0
- package/src/utils/routing.js +145 -73
- package/src/utils/shared-iterator.js +5 -0
- package/src/utils/streaming.js +14 -4
- package/src/utils/url.js +33 -2
- package/src/version.js +1 -1
- package/types/index.d.ts +2616 -2433
- package/types/index.d.ts.map +120 -106
- package/src/core/sync/write_ambient.js +0 -18
- package/src/core/sync/write_root.js +0 -148
- package/src/core/sync/write_tsconfig.js +0 -250
- package/src/exports/internal/server.js +0 -22
- package/src/exports/node/polyfills.js +0 -30
- package/src/runtime/server/app.js +0 -9
- package/src/runtime/server/remote.js +0 -457
- package/src/runtime/shared-server.js +0 -7
- package/src/runtime/telemetry/otel.js +0 -21
- package/src/runtime/telemetry/record_span.js +0 -65
- package/src/types/synthetic/$lib.md +0 -5
- /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
- /package/src/{runtime/telemetry/noop.js → telemetry.js} +0 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/** @import { Tracer, SpanStatusCode, PropagationAPI, ContextAPI } from '@opentelemetry/api' */
|
|
2
|
+
/** @import { RecordSpan } from 'types' */
|
|
3
|
+
import { HttpError, Redirect } from '../shared.js';
|
|
4
|
+
import { noop_span } from '../../../telemetry.js';
|
|
5
|
+
|
|
6
|
+
// Import this module by its bare specifier so bundled and external code share its state.
|
|
7
|
+
|
|
8
|
+
/** @type {Promise<{ tracer: Tracer, SpanStatusCode: typeof SpanStatusCode, propagation: PropagationAPI, context: ContextAPI }> | null} */
|
|
9
|
+
export let otel = null;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The caller passes in `import('@opentelemetry/api')` so the import lives behind
|
|
13
|
+
* `__SVELTEKIT_SERVER_TRACING_ENABLED__` in the bundled runtime and is eliminated
|
|
14
|
+
* from builds with tracing disabled, where the package may not be installed.
|
|
15
|
+
* @param {Promise<typeof import('@opentelemetry/api')>} api
|
|
16
|
+
* @returns {void}
|
|
17
|
+
*/
|
|
18
|
+
export function init_tracing(api) {
|
|
19
|
+
otel ??= api
|
|
20
|
+
.then((module) => {
|
|
21
|
+
return {
|
|
22
|
+
tracer: module.trace.getTracer('sveltekit'),
|
|
23
|
+
propagation: module.propagation,
|
|
24
|
+
context: module.context,
|
|
25
|
+
SpanStatusCode: module.SpanStatusCode
|
|
26
|
+
};
|
|
27
|
+
})
|
|
28
|
+
.catch(() => {
|
|
29
|
+
throw new Error(
|
|
30
|
+
'Tracing is enabled (see the SvelteKit plugin `tracing.server` option in your vite.config.js), but `@opentelemetry/api` is not available. This error will likely resolve itself when you set up your tracing instrumentation in `instrumentation.server.js`. For more information, see https://svelte.dev/docs/kit/observability#opentelemetry-api'
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** @type {RecordSpan} */
|
|
36
|
+
export async function record_span({ name, attributes, fn }) {
|
|
37
|
+
if (otel === null) {
|
|
38
|
+
return fn(noop_span);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const { SpanStatusCode, tracer } = await otel;
|
|
42
|
+
|
|
43
|
+
return tracer.startActiveSpan(name, { attributes }, async (span) => {
|
|
44
|
+
try {
|
|
45
|
+
return await fn(span);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (error instanceof HttpError) {
|
|
48
|
+
span.setAttributes({
|
|
49
|
+
[`${name}.result.type`]: 'known_error',
|
|
50
|
+
[`${name}.result.status`]: error.status,
|
|
51
|
+
[`${name}.result.message`]: error.body.message
|
|
52
|
+
});
|
|
53
|
+
if (error.status >= 500) {
|
|
54
|
+
span.recordException({
|
|
55
|
+
name: 'HttpError',
|
|
56
|
+
message: error.body.message
|
|
57
|
+
});
|
|
58
|
+
span.setStatus({
|
|
59
|
+
code: SpanStatusCode.ERROR,
|
|
60
|
+
message: error.body.message
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
} else if (error instanceof Redirect) {
|
|
64
|
+
span.setAttributes({
|
|
65
|
+
[`${name}.result.type`]: 'redirect',
|
|
66
|
+
[`${name}.result.status`]: error.status,
|
|
67
|
+
[`${name}.result.location`]: error.location
|
|
68
|
+
});
|
|
69
|
+
} else if (error instanceof Error) {
|
|
70
|
+
span.setAttributes({
|
|
71
|
+
[`${name}.result.type`]: 'unknown_error'
|
|
72
|
+
});
|
|
73
|
+
span.recordException({
|
|
74
|
+
name: error.name,
|
|
75
|
+
message: error.message,
|
|
76
|
+
// conditional so this compiles under consumers' `exactOptionalPropertyTypes`
|
|
77
|
+
...(error.stack !== undefined && { stack: error.stack })
|
|
78
|
+
});
|
|
79
|
+
span.setStatus({
|
|
80
|
+
code: SpanStatusCode.ERROR,
|
|
81
|
+
message: error.message
|
|
82
|
+
});
|
|
83
|
+
} else {
|
|
84
|
+
span.setAttributes({
|
|
85
|
+
[`${name}.result.type`]: 'unknown_error'
|
|
86
|
+
});
|
|
87
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
throw error;
|
|
91
|
+
} finally {
|
|
92
|
+
span.end();
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
|
2
|
+
|
|
3
|
+
export class HttpError {
|
|
4
|
+
/**
|
|
5
|
+
* @param {App.Error} error
|
|
6
|
+
*/
|
|
7
|
+
constructor(error) {
|
|
8
|
+
this.status = error.status;
|
|
9
|
+
this.body = error;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
toString() {
|
|
13
|
+
return JSON.stringify(this.body);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* An `HttpError` whose body is already in its final, user-facing form — either produced by the
|
|
19
|
+
* `handleError` hook on the server and reconstructed here from the response, or authored directly
|
|
20
|
+
* by the client runtime. Unlike a plain `HttpError` (which represents a fresh `error(...)` call
|
|
21
|
+
* that the hook has yet to see), `handleError` must not run on it.
|
|
22
|
+
* @extends HttpError
|
|
23
|
+
*/
|
|
24
|
+
export class HandledHttpError extends HttpError {}
|
|
25
|
+
|
|
26
|
+
export class Redirect {
|
|
27
|
+
/**
|
|
28
|
+
* @param {300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308} status
|
|
29
|
+
* @param {string} location
|
|
30
|
+
*/
|
|
31
|
+
constructor(status, location) {
|
|
32
|
+
try {
|
|
33
|
+
new Headers({ location });
|
|
34
|
+
} catch {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Invalid redirect location ${JSON.stringify(location)}: ` +
|
|
37
|
+
'this string contains characters that cannot be used in HTTP headers'
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.status = status;
|
|
42
|
+
this.location = location;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 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.
|
|
48
|
+
* `SvelteKitError` goes through `handleError`.
|
|
49
|
+
* @extends Error
|
|
50
|
+
*/
|
|
51
|
+
export class SvelteKitError extends Error {
|
|
52
|
+
/**
|
|
53
|
+
* @param {number} status
|
|
54
|
+
* @param {string} text
|
|
55
|
+
* @param {string} message
|
|
56
|
+
*/
|
|
57
|
+
constructor(status, text, message) {
|
|
58
|
+
super(message);
|
|
59
|
+
this.status = status;
|
|
60
|
+
this.text = text;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @template [T=undefined]
|
|
66
|
+
*/
|
|
67
|
+
export class ActionFailure {
|
|
68
|
+
/**
|
|
69
|
+
* @param {number} status
|
|
70
|
+
* @param {T} data
|
|
71
|
+
*/
|
|
72
|
+
constructor(status, data) {
|
|
73
|
+
this.status = status;
|
|
74
|
+
this.data = data;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Error thrown when form validation fails imperatively
|
|
80
|
+
*/
|
|
81
|
+
export class ValidationError extends Error {
|
|
82
|
+
/**
|
|
83
|
+
* @param {StandardSchemaV1.Issue[]} issues
|
|
84
|
+
*/
|
|
85
|
+
constructor(issues) {
|
|
86
|
+
super('Validation failed');
|
|
87
|
+
this.name = 'ValidationError';
|
|
88
|
+
this.issues = issues;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { createReadStream } from 'node:fs';
|
|
2
2
|
import { Readable } from 'node:stream';
|
|
3
|
-
import { SvelteKitError } from '../internal/
|
|
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
|
-
|
|
53
|
+
/** @param {Error} error */
|
|
54
|
+
const on_error = (error) => {
|
|
51
55
|
cancelled = true;
|
|
52
56
|
controller.error(error);
|
|
53
|
-
}
|
|
57
|
+
};
|
|
54
58
|
|
|
55
|
-
|
|
59
|
+
const on_end = () => {
|
|
56
60
|
if (cancelled) return;
|
|
57
61
|
controller.close();
|
|
58
|
-
}
|
|
62
|
+
};
|
|
59
63
|
|
|
60
|
-
|
|
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 {
|
|
122
|
+
* @returns {Request}
|
|
113
123
|
*/
|
|
114
|
-
|
|
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
|
|
@@ -129,14 +137,9 @@ export async function getRequest({ request, base, bodySizeLimit }) {
|
|
|
129
137
|
}
|
|
130
138
|
|
|
131
139
|
const controller = new AbortController();
|
|
132
|
-
|
|
133
|
-
// see https://github.com/nodejs/node/blob/5cf3c3e24c7257a0c6192ed8ef71efec8ddac22b/lib/internal/streams/readable.js#L1443-L1453
|
|
134
|
-
let errored = false;
|
|
135
|
-
let end_emitted = false;
|
|
136
|
-
request.once('error', () => (errored = true));
|
|
137
|
-
request.once('end', () => (end_emitted = true));
|
|
140
|
+
|
|
138
141
|
request.once('close', () => {
|
|
139
|
-
if (
|
|
142
|
+
if (request.readableAborted) {
|
|
140
143
|
controller.abort();
|
|
141
144
|
}
|
|
142
145
|
});
|
|
@@ -154,14 +157,53 @@ export async function getRequest({ request, base, bodySizeLimit }) {
|
|
|
154
157
|
});
|
|
155
158
|
}
|
|
156
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Drains any unconsumed request body once the response has been sent. When a
|
|
162
|
+
* route doesn't read the request body (for example a page route receiving a
|
|
163
|
+
* POST), the unread bytes remain buffered in the socket. On keep-alive
|
|
164
|
+
* connections Node's HTTP parser then reads those leftover bytes as the next
|
|
165
|
+
* request, fails to parse them, and resets the connection — losing any
|
|
166
|
+
* pipelined request. Resuming the request discards the bytes so the connection
|
|
167
|
+
* stays usable.
|
|
168
|
+
*
|
|
169
|
+
* Because `get_raw_body` attaches a `data` listener, Node marks the request as
|
|
170
|
+
* being consumed (`req._consuming`) and skips its own automatic drain, so we
|
|
171
|
+
* have to do it ourselves. The whole remaining body is read and discarded; this
|
|
172
|
+
* is the intended trade-off (keeping the connection reusable) over destroying it.
|
|
173
|
+
* @see https://github.com/sveltejs/kit/issues/14916
|
|
174
|
+
* @see https://github.com/sveltejs/kit/issues/15526
|
|
175
|
+
* @param {import('http').ServerResponse} res
|
|
176
|
+
*/
|
|
177
|
+
function drain_request(res) {
|
|
178
|
+
const req = res.req;
|
|
179
|
+
if (!req || req.readableEnded || req.destroyed) return;
|
|
180
|
+
|
|
181
|
+
// When the body went unread, get_raw_body's `data` listener is still attached
|
|
182
|
+
// and enqueues into a ReadableStream nobody consumes; it pauses the request at
|
|
183
|
+
// the high water mark, so one chunk sits in memory and the rest stays buffered
|
|
184
|
+
// in the socket. Remove only that `data` listener so the resumed stream drops
|
|
185
|
+
// the remaining bytes instead of re-buffering them. The `end` and `error`
|
|
186
|
+
// listeners stay attached so the body's ReadableStream is still closed (or
|
|
187
|
+
// errored) once draining completes, and a consumer that stopped reading
|
|
188
|
+
// mid-body sees a clean end instead of hanging.
|
|
189
|
+
const on_data = body_data_listeners.get(req);
|
|
190
|
+
if (on_data) {
|
|
191
|
+
req.removeListener('data', on_data);
|
|
192
|
+
body_data_listeners.delete(req);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
req.resume();
|
|
196
|
+
}
|
|
197
|
+
|
|
157
198
|
/**
|
|
158
199
|
* @param {import('http').ServerResponse} res
|
|
159
200
|
* @param {Response} response
|
|
160
|
-
* @returns {
|
|
201
|
+
* @returns {void}
|
|
161
202
|
*/
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
203
|
+
export function setResponse(res, response) {
|
|
204
|
+
res.once('finish', () => drain_request(res));
|
|
205
|
+
res.once('close', () => drain_request(res));
|
|
206
|
+
|
|
165
207
|
for (const [key, value] of response.headers) {
|
|
166
208
|
try {
|
|
167
209
|
res.setHeader(key, key === 'set-cookie' ? response.headers.getSetCookie() : value);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/** @import { ParamDefinition, ParamMatcher } from '@sveltejs/kit/params' */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Define [parameter matchers](https://svelte.dev/docs/kit/advanced-routing#Matching) for your app.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```js
|
|
8
|
+
* import { defineParams } from '@sveltejs/kit/params';
|
|
9
|
+
* import * as v from 'valibot';
|
|
10
|
+
*
|
|
11
|
+
* export const params = defineParams({
|
|
12
|
+
* locale: (param) => {
|
|
13
|
+
* if (param !== 'de' && param !== 'en') return;
|
|
14
|
+
* return param;
|
|
15
|
+
* },
|
|
16
|
+
* number: v.pipe(v.string(), v.toNumber())
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @template {Record<string, import('./public.js').ParamDefinition>} T
|
|
21
|
+
* @param {T} definitions
|
|
22
|
+
* @returns {import('./public.js').DefinedParams<T>}
|
|
23
|
+
*/
|
|
24
|
+
export function defineParams(definitions) {
|
|
25
|
+
/** @type {Record<string, import('./public.js').ParamMatcher>} */
|
|
26
|
+
const matchers = {};
|
|
27
|
+
|
|
28
|
+
for (const [key, definition] of Object.entries(definitions)) {
|
|
29
|
+
matchers[key] = normalize_param_definition(definition);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return /** @type {import('./public.js').DefinedParams<T>} */ (matchers);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {ParamDefinition} definition
|
|
37
|
+
* @returns {ParamMatcher}
|
|
38
|
+
*/
|
|
39
|
+
export function normalize_param_definition(definition) {
|
|
40
|
+
// standard schemas can be callable (e.g. ArkType), so this must be checked before the function case
|
|
41
|
+
if (
|
|
42
|
+
definition &&
|
|
43
|
+
(typeof definition === 'object' || typeof definition === 'function') &&
|
|
44
|
+
'~standard' in definition
|
|
45
|
+
) {
|
|
46
|
+
return definition;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (typeof definition === 'function') {
|
|
50
|
+
return /** @type {ParamMatcher} */ (
|
|
51
|
+
/** @type {unknown} */ ({
|
|
52
|
+
'~standard': {
|
|
53
|
+
validate(/** @type {unknown} */ value) {
|
|
54
|
+
const result = definition(/** @type {string} */ (value));
|
|
55
|
+
|
|
56
|
+
if (result === undefined) {
|
|
57
|
+
return { issues: [{ message: 'Invalid param' }] };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (/** @type {any} */ (result) instanceof Promise) return result; // will be validated and rejected upstream
|
|
61
|
+
|
|
62
|
+
return { value: result };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
throw new Error('Invalid param definition');
|
|
70
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The shape of a param matcher. See [matching](https://svelte.dev/docs/kit/advanced-routing#Matching) for more info.
|
|
5
|
+
*/
|
|
6
|
+
export type ParamMatcher<Output = any> = StandardSchemaV1<string, Output>;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A value that can be parsed from a URL param and losslessly encoded with `String(...)`.
|
|
10
|
+
*/
|
|
11
|
+
export type ParamValue = string | number | boolean | bigint;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A param matcher definition passed to [`defineParams`](https://svelte.dev/docs/kit/@sveltejs-kit-params#defineParams).
|
|
15
|
+
*/
|
|
16
|
+
export type ParamDefinition =
|
|
17
|
+
| ((param: string) => ParamValue | undefined)
|
|
18
|
+
| StandardSchemaV1<string, ParamValue>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The return type of [`defineParams`](https://svelte.dev/docs/kit/@sveltejs-kit-params#defineParams).
|
|
22
|
+
*/
|
|
23
|
+
export type DefinedParams<T extends Record<string, ParamDefinition>> = {
|
|
24
|
+
readonly [K in keyof T]: ParamEntry<T[K]>;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Normalizes a property of defineParams (schema or function) to standard schema.
|
|
29
|
+
*/
|
|
30
|
+
type ParamEntry<M> =
|
|
31
|
+
M extends StandardSchemaV1<any, any>
|
|
32
|
+
? StandardSchemaV1.InferOutput<M> extends ParamValue
|
|
33
|
+
? StandardSchemaV1<any, M>
|
|
34
|
+
: StandardSchemaV1<any, never>
|
|
35
|
+
: M extends (param: string) => infer R
|
|
36
|
+
? Exclude<R, undefined> extends ParamValue
|
|
37
|
+
? StandardSchemaV1<any, Exclude<R, undefined>>
|
|
38
|
+
: StandardSchemaV1<any, never>
|
|
39
|
+
: never;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Extracts the param type from a matcher.
|
|
43
|
+
*/
|
|
44
|
+
export type MatcherParam<M extends StandardSchemaV1<any, any>> =
|
|
45
|
+
M extends StandardSchemaV1<any, infer Inner>
|
|
46
|
+
? Inner extends ParamValue
|
|
47
|
+
? Inner
|
|
48
|
+
: Inner extends StandardSchemaV1<any, any>
|
|
49
|
+
? StandardSchemaV1.InferOutput<Inner> extends ParamValue
|
|
50
|
+
? StandardSchemaV1.InferOutput<Inner>
|
|
51
|
+
: never
|
|
52
|
+
: never
|
|
53
|
+
: never;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Define [parameter matchers](https://svelte.dev/docs/kit/advanced-routing#Matching) for your app.
|
|
57
|
+
*
|
|
58
|
+
* @template T
|
|
59
|
+
* @param definitions
|
|
60
|
+
*/
|
|
61
|
+
export function defineParams<T extends Record<string, ParamDefinition>>(
|
|
62
|
+
definitions: T
|
|
63
|
+
): DefinedParams<T>;
|