@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
package/src/utils/routing.js
CHANGED
|
@@ -1,10 +1,33 @@
|
|
|
1
|
+
/** @import { ParamMatcher, ParamValue } from '@sveltejs/kit/params' */
|
|
1
2
|
import { BROWSER } from 'esm-env';
|
|
2
|
-
import {
|
|
3
|
+
import { escape_for_regexp } from './regex.js';
|
|
3
4
|
|
|
4
|
-
const param_pattern = /^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;
|
|
5
|
+
const param_pattern = /^(\[)?(\.\.\.)?([\w-]+)(?:=([\w-]+))?(\])?$/;
|
|
5
6
|
|
|
6
7
|
const root_group_pattern = /^\/\((?:[^)]+)\)$/;
|
|
7
8
|
|
|
9
|
+
const escape_sequence_pattern = /\[([ux])\+([^\]]+)\]/;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Decodes the codepoints of an `[x+nn]` or `[u+nnnn]` escape sequence
|
|
13
|
+
* @param {string} code the sequence without its `[x+`/`[u+` prefix or `]` suffix
|
|
14
|
+
*/
|
|
15
|
+
export function decode_escape_sequence(code) {
|
|
16
|
+
return String.fromCodePoint(...code.split('-').map((codepoint) => parseInt(codepoint, 16)));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Encodes the characters that `decode_pathname` leaves untouched, so that a decoded
|
|
21
|
+
* escape sequence still matches the pattern `parse_route_id` builds for it
|
|
22
|
+
* @param {string} str
|
|
23
|
+
*/
|
|
24
|
+
export function encode_pathname_chars(str) {
|
|
25
|
+
return str.replace(
|
|
26
|
+
/[%/?#]/g,
|
|
27
|
+
(char) => '%' + char.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0')
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
8
31
|
/**
|
|
9
32
|
* Creates the regex pattern, extracts parameter names, and generates types for a route
|
|
10
33
|
* @param {string} id
|
|
@@ -20,7 +43,7 @@ export function parse_route_id(id) {
|
|
|
20
43
|
`^${get_route_segments(id)
|
|
21
44
|
.map((segment) => {
|
|
22
45
|
// special case — /[...rest]/ could contain zero segments
|
|
23
|
-
const rest_match = /^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(segment);
|
|
46
|
+
const rest_match = /^\[\.\.\.([\w-]+)(?:=([\w-]+))?\]$/.exec(segment);
|
|
24
47
|
if (rest_match) {
|
|
25
48
|
params.push({
|
|
26
49
|
name: rest_match[1],
|
|
@@ -32,7 +55,7 @@ export function parse_route_id(id) {
|
|
|
32
55
|
return '(?:/([^]*))?';
|
|
33
56
|
}
|
|
34
57
|
// special case — /[[optional]]/ could contain zero segments
|
|
35
|
-
const optional_match = /^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(segment);
|
|
58
|
+
const optional_match = /^\[\[([\w-]+)(?:=([\w-]+))?\]\]$/.exec(segment);
|
|
36
59
|
if (optional_match) {
|
|
37
60
|
params.push({
|
|
38
61
|
name: optional_match[1],
|
|
@@ -52,19 +75,8 @@ export function parse_route_id(id) {
|
|
|
52
75
|
const result = parts
|
|
53
76
|
.map((content, i) => {
|
|
54
77
|
if (i % 2) {
|
|
55
|
-
if (content.startsWith('x+')) {
|
|
56
|
-
return escape(
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (content.startsWith('u+')) {
|
|
60
|
-
return escape(
|
|
61
|
-
String.fromCharCode(
|
|
62
|
-
...content
|
|
63
|
-
.slice(2)
|
|
64
|
-
.split('-')
|
|
65
|
-
.map((code) => parseInt(code, 16))
|
|
66
|
-
)
|
|
67
|
-
);
|
|
78
|
+
if (content.startsWith('x+') || content.startsWith('u+')) {
|
|
79
|
+
return escape(decode_escape_sequence(content.slice(2)));
|
|
68
80
|
}
|
|
69
81
|
|
|
70
82
|
// We know the match cannot be null in the browser because manifest generation
|
|
@@ -73,7 +85,7 @@ export function parse_route_id(id) {
|
|
|
73
85
|
const match = /** @type {RegExpExecArray} */ (param_pattern.exec(content));
|
|
74
86
|
if (!BROWSER && !match) {
|
|
75
87
|
throw new Error(
|
|
76
|
-
`Invalid param: ${content}. Params and matcher names can only have underscores and alphanumeric characters.`
|
|
88
|
+
`Invalid param: ${content}. Params and matcher names can only have underscores, hyphens, and alphanumeric characters.`
|
|
77
89
|
);
|
|
78
90
|
}
|
|
79
91
|
|
|
@@ -104,7 +116,7 @@ export function parse_route_id(id) {
|
|
|
104
116
|
return { pattern, params };
|
|
105
117
|
}
|
|
106
118
|
|
|
107
|
-
const optional_param_regex = /\/\[\[\w+?(
|
|
119
|
+
const optional_param_regex = /\/\[\[[\w-]+?(?:=[\w-]+)?\]\]/;
|
|
108
120
|
|
|
109
121
|
/**
|
|
110
122
|
* Removes optional params from a route ID.
|
|
@@ -134,13 +146,43 @@ export function get_route_segments(route) {
|
|
|
134
146
|
return route.slice(1).split('/').filter(affects_path);
|
|
135
147
|
}
|
|
136
148
|
|
|
149
|
+
/**
|
|
150
|
+
* @param {ParamMatcher} matcher
|
|
151
|
+
* @param {string} value
|
|
152
|
+
* @returns {{ success: true, value: any } | { success: false }}
|
|
153
|
+
*/
|
|
154
|
+
function run_matcher(matcher, value) {
|
|
155
|
+
const result = matcher['~standard'].validate(value);
|
|
156
|
+
|
|
157
|
+
if (result instanceof Promise) {
|
|
158
|
+
throw new Error('Async param matchers are not supported');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (result.issues) {
|
|
162
|
+
return { success: false };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const parsed = result.value;
|
|
166
|
+
|
|
167
|
+
if (
|
|
168
|
+
typeof parsed !== 'string' &&
|
|
169
|
+
typeof parsed !== 'number' &&
|
|
170
|
+
typeof parsed !== 'boolean' &&
|
|
171
|
+
typeof parsed !== 'bigint'
|
|
172
|
+
) {
|
|
173
|
+
throw new Error('Param matcher must return a string, number, boolean, or bigint');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return { success: true, value: parsed };
|
|
177
|
+
}
|
|
178
|
+
|
|
137
179
|
/**
|
|
138
180
|
* @param {RegExpMatchArray} match
|
|
139
181
|
* @param {import('types').RouteParam[]} params
|
|
140
|
-
* @param {Record<string,
|
|
182
|
+
* @param {Record<string, ParamMatcher>} matchers
|
|
141
183
|
*/
|
|
142
184
|
export function exec(match, params, matchers) {
|
|
143
|
-
/** @type {Record<string,
|
|
185
|
+
/** @type {Record<string, any>} */
|
|
144
186
|
const result = {};
|
|
145
187
|
|
|
146
188
|
const values = match.slice(1);
|
|
@@ -173,61 +215,76 @@ export function exec(match, params, matchers) {
|
|
|
173
215
|
}
|
|
174
216
|
}
|
|
175
217
|
|
|
176
|
-
|
|
177
|
-
result[param.name] = value;
|
|
218
|
+
const decoded = decodeURIComponent(value);
|
|
178
219
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
220
|
+
if (param.matcher) {
|
|
221
|
+
const outcome = run_matcher(matchers[param.matcher], decoded);
|
|
222
|
+
|
|
223
|
+
if (!outcome.success) {
|
|
224
|
+
// in the `/[[a=b]]/...` case, if the value didn't satisfy the matcher,
|
|
225
|
+
// keep track of the number of skipped optional parameters and continue
|
|
226
|
+
if (param.optional && param.chained) {
|
|
227
|
+
buffered++;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
186
230
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
!next_param &&
|
|
190
|
-
!next_value &&
|
|
191
|
-
Object.keys(result).length === values_needing_match.length
|
|
192
|
-
) {
|
|
193
|
-
buffered = 0;
|
|
231
|
+
// otherwise, if the matcher returns `false`, the route did not match
|
|
232
|
+
return;
|
|
194
233
|
}
|
|
195
|
-
|
|
234
|
+
|
|
235
|
+
result[param.name] = outcome.value;
|
|
236
|
+
} else {
|
|
237
|
+
result[param.name] = decoded;
|
|
196
238
|
}
|
|
197
239
|
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
240
|
+
// Now that the params match, reset the buffer if the next param isn't the [...rest]
|
|
241
|
+
// and the next value is defined, otherwise the buffer will cause us to skip values
|
|
242
|
+
const next_param = params[i + 1];
|
|
243
|
+
const next_value = values[i + 1];
|
|
244
|
+
if (next_param && !next_param.rest && next_param.optional && next_value && param.chained) {
|
|
245
|
+
buffered = 0;
|
|
203
246
|
}
|
|
204
247
|
|
|
205
|
-
//
|
|
206
|
-
|
|
248
|
+
// There are no more params and no more values, but all non-empty values have been matched
|
|
249
|
+
if (!next_param && !next_value && Object.keys(result).length === values_needing_match.length) {
|
|
250
|
+
buffered = 0;
|
|
251
|
+
}
|
|
252
|
+
continue;
|
|
207
253
|
}
|
|
208
254
|
|
|
209
255
|
if (buffered) return;
|
|
210
256
|
return result;
|
|
211
257
|
}
|
|
212
258
|
|
|
259
|
+
/**
|
|
260
|
+
* `decode_pathname` leaves these characters untouched, so routes have to match their encoded forms
|
|
261
|
+
* @type {Record<string, string>}
|
|
262
|
+
*/
|
|
263
|
+
const encoded = {
|
|
264
|
+
'%': '%25',
|
|
265
|
+
'/': '%2[Ff]',
|
|
266
|
+
'?': '%3[Ff]',
|
|
267
|
+
'#': '%23'
|
|
268
|
+
};
|
|
269
|
+
|
|
213
270
|
/** @param {string} str */
|
|
214
271
|
function escape(str) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
.replace(/%/g, '%25')
|
|
222
|
-
.replace(/\//g, '%2[Ff]')
|
|
223
|
-
.replace(/\?/g, '%3[Ff]')
|
|
224
|
-
.replace(/#/g, '%23')
|
|
225
|
-
// escape characters that have special meaning in regex
|
|
226
|
-
.replace(/[.*+?^${}()|\\]/g, '\\$&')
|
|
227
|
-
);
|
|
272
|
+
// the replacements in `encoded` are regex source themselves, so they must not be escaped again
|
|
273
|
+
return str
|
|
274
|
+
.normalize()
|
|
275
|
+
.split(/([%/?#])/)
|
|
276
|
+
.map((part, i) => (i % 2 ? encoded[part] : escape_for_regexp(part)))
|
|
277
|
+
.join('');
|
|
228
278
|
}
|
|
229
279
|
|
|
230
|
-
const basic_param_pattern = /\[(\[)?(\.\.\.)?(\w+?)(?:=(\w+))?\]\]?/g;
|
|
280
|
+
const basic_param_pattern = /\[(\[)?(\.\.\.)?([\w-]+?)(?:=([\w-]+))?\]\]?/g;
|
|
281
|
+
|
|
282
|
+
// escape sequences are expanded in the same pass as the params, so that a param
|
|
283
|
+
// value containing `[x+2f]` is not itself expanded
|
|
284
|
+
export const segment_pattern = new RegExp(
|
|
285
|
+
`${escape_sequence_pattern.source}|${basic_param_pattern.source}`,
|
|
286
|
+
'g'
|
|
287
|
+
);
|
|
231
288
|
|
|
232
289
|
/**
|
|
233
290
|
* Populate a route ID with params to resolve a pathname.
|
|
@@ -242,7 +299,7 @@ const basic_param_pattern = /\[(\[)?(\.\.\.)?(\w+?)(?:=(\w+))?\]\]?/g;
|
|
|
242
299
|
* ); // `/blog/hello-world/something/else`
|
|
243
300
|
* ```
|
|
244
301
|
* @param {string} id
|
|
245
|
-
* @param {Record<string,
|
|
302
|
+
* @param {Record<string, ParamValue | undefined>} params
|
|
246
303
|
* @returns {string}
|
|
247
304
|
*/
|
|
248
305
|
export function resolve_route(id, params) {
|
|
@@ -253,21 +310,36 @@ export function resolve_route(id, params) {
|
|
|
253
310
|
'/' +
|
|
254
311
|
segments
|
|
255
312
|
.map((segment) =>
|
|
256
|
-
segment.replace(
|
|
257
|
-
|
|
313
|
+
segment.replace(segment_pattern, (_, escape_type, escape_code, optional, rest, name) => {
|
|
314
|
+
if (escape_type) return encode_pathname_chars(decode_escape_sequence(escape_code));
|
|
315
|
+
|
|
316
|
+
const value = params[name];
|
|
258
317
|
|
|
259
|
-
|
|
260
|
-
if (!param_value) {
|
|
318
|
+
if (value === undefined || value === '') {
|
|
261
319
|
if (optional) return '';
|
|
262
|
-
if (rest &&
|
|
320
|
+
if (rest && value !== undefined) return '';
|
|
263
321
|
throw new Error(`Missing parameter '${name}' in route ${id}`);
|
|
264
322
|
}
|
|
265
323
|
|
|
266
|
-
if (
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
324
|
+
if (typeof value === 'string') {
|
|
325
|
+
if (value.startsWith('/') || value.endsWith('/')) {
|
|
326
|
+
throw new Error(
|
|
327
|
+
`Parameter '${name}' in route ${id} cannot start or end with a slash -- this would cause an invalid route like foo//bar`
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return value;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (
|
|
335
|
+
typeof value === 'number' ||
|
|
336
|
+
typeof value === 'boolean' ||
|
|
337
|
+
typeof value === 'bigint'
|
|
338
|
+
) {
|
|
339
|
+
return String(value);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
throw new Error('Parameter values must be a string, number, boolean, or bigint');
|
|
271
343
|
})
|
|
272
344
|
)
|
|
273
345
|
.filter(Boolean)
|
|
@@ -289,8 +361,8 @@ export function has_server_load(node) {
|
|
|
289
361
|
* @template {{pattern: RegExp, params: import('types').RouteParam[]}} Route
|
|
290
362
|
* @param {string} path - The decoded pathname to match
|
|
291
363
|
* @param {Route[]} routes
|
|
292
|
-
* @param {Record<string,
|
|
293
|
-
* @returns {{ route: Route, params: Record<string,
|
|
364
|
+
* @param {Record<string, ParamMatcher>} matchers
|
|
365
|
+
* @returns {{ route: Route, params: Record<string, any> } | null}
|
|
294
366
|
*/
|
|
295
367
|
export function find_route(path, routes, matchers) {
|
|
296
368
|
for (const route of routes) {
|
|
@@ -301,7 +373,7 @@ export function find_route(path, routes, matchers) {
|
|
|
301
373
|
if (matched) {
|
|
302
374
|
return {
|
|
303
375
|
route,
|
|
304
|
-
params:
|
|
376
|
+
params: matched
|
|
305
377
|
};
|
|
306
378
|
}
|
|
307
379
|
}
|
|
@@ -52,6 +52,11 @@ export class SharedIterator {
|
|
|
52
52
|
/** @type {unknown} */
|
|
53
53
|
#terminal_error = undefined;
|
|
54
54
|
|
|
55
|
+
/** Whether `done()` or `fail()` has been broadcast. */
|
|
56
|
+
get closed() {
|
|
57
|
+
return this.#closed;
|
|
58
|
+
}
|
|
59
|
+
|
|
55
60
|
/**
|
|
56
61
|
* @param {(instance: SharedIterator<T>) => (() => void)} [start]
|
|
57
62
|
*/
|
package/src/utils/streaming.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { noop } from './functions.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Create an async iterator and a function to push values into it
|
|
3
5
|
* @template T
|
|
@@ -30,10 +32,18 @@ export function create_async_iterator() {
|
|
|
30
32
|
};
|
|
31
33
|
},
|
|
32
34
|
add: (promise) => {
|
|
33
|
-
|
|
34
|
-
void promise.
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
const next = Promise.withResolvers();
|
|
36
|
+
void next.promise.catch(noop); // prevent unhandled rejection potentially crashing the process
|
|
37
|
+
deferred.push(next);
|
|
38
|
+
|
|
39
|
+
void promise.then(
|
|
40
|
+
(value) => {
|
|
41
|
+
deferred[++resolved].resolve(value);
|
|
42
|
+
},
|
|
43
|
+
(error) => {
|
|
44
|
+
deferred[++resolved].reject(error);
|
|
45
|
+
}
|
|
46
|
+
);
|
|
37
47
|
}
|
|
38
48
|
};
|
|
39
49
|
}
|
package/src/utils/url.js
CHANGED
|
@@ -4,7 +4,7 @@ import { BROWSER, DEV } from 'esm-env';
|
|
|
4
4
|
* Matches a URI scheme. See https://www.rfc-editor.org/rfc/rfc3986#section-3.1
|
|
5
5
|
* @type {RegExp}
|
|
6
6
|
*/
|
|
7
|
-
export const SCHEME = /^[a-z][a-z\d+\-.]
|
|
7
|
+
export const SCHEME = /^[a-z][a-z\d+\-.]*:/i;
|
|
8
8
|
|
|
9
9
|
const internal = new URL('a://');
|
|
10
10
|
|
|
@@ -27,6 +27,37 @@ export function is_root_relative(path) {
|
|
|
27
27
|
return path[0] === '/' && path[1] !== '/';
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Relative reference from `from` to `to`, which must differ only by a trailing slash
|
|
32
|
+
* @param {string} from
|
|
33
|
+
* @param {string} to
|
|
34
|
+
* @returns {string}
|
|
35
|
+
*/
|
|
36
|
+
export function relative_pathname(from, to) {
|
|
37
|
+
const segment = to.replace(/\/$/, '').split('/').at(-1);
|
|
38
|
+
|
|
39
|
+
return from.endsWith('/') ? `../${segment}` : `${segment}/`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {string} location
|
|
44
|
+
* @param {string} allowed
|
|
45
|
+
*/
|
|
46
|
+
export function matches_external_allowlist_entry(location, allowed) {
|
|
47
|
+
if (location === allowed) return true;
|
|
48
|
+
|
|
49
|
+
// TODO replace the try/catch with `URL.parse` when browser support allows (Chrome 126, Firefox 126, Safari 18)
|
|
50
|
+
try {
|
|
51
|
+
const allow = new URL(allowed);
|
|
52
|
+
const loc = new URL(location, allow);
|
|
53
|
+
|
|
54
|
+
// this is stricter than `loc.origin === allow.origin`, which can fail in `blob:` cases
|
|
55
|
+
return loc.protocol === allow.protocol && loc.host === allow.host;
|
|
56
|
+
} catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
30
61
|
/**
|
|
31
62
|
* @param {string} path
|
|
32
63
|
* @param {import('types').TrailingSlash} trailing_slash
|
|
@@ -119,6 +150,7 @@ export function make_trackable(url, callback, search_params_callback, allow_hash
|
|
|
119
150
|
/**
|
|
120
151
|
* URL properties that could change during the lifetime of the page,
|
|
121
152
|
* which excludes things like `origin`
|
|
153
|
+
* @type {(keyof URL)[]}
|
|
122
154
|
*/
|
|
123
155
|
const tracked_url_properties = ['href', 'pathname', 'search', 'toString', 'toJSON'];
|
|
124
156
|
if (allow_hash) tracked_url_properties.push('hash');
|
|
@@ -127,7 +159,6 @@ export function make_trackable(url, callback, search_params_callback, allow_hash
|
|
|
127
159
|
Object.defineProperty(tracked, property, {
|
|
128
160
|
get() {
|
|
129
161
|
callback();
|
|
130
|
-
// @ts-expect-error
|
|
131
162
|
return url[property];
|
|
132
163
|
},
|
|
133
164
|
|
package/src/version.js
CHANGED