@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.
Files changed (195) hide show
  1. package/package.json +62 -36
  2. package/src/cli.js +15 -12
  3. package/src/constants.js +6 -1
  4. package/src/core/adapt/builder.js +66 -72
  5. package/src/core/adapt/index.js +17 -6
  6. package/src/core/config/index.js +134 -80
  7. package/src/core/config/options.js +295 -273
  8. package/src/core/config/types.d.ts +1 -1
  9. package/src/core/env.js +146 -16
  10. package/src/core/generate_manifest/index.js +18 -15
  11. package/src/core/postbuild/analyse.js +8 -20
  12. package/src/core/postbuild/crawl.js +22 -6
  13. package/src/core/postbuild/entities.js +8 -2
  14. package/src/core/postbuild/fallback.js +4 -2
  15. package/src/core/postbuild/prerender.js +222 -70
  16. package/src/core/postbuild/queue.js +2 -1
  17. package/src/core/sync/create_manifest_data/conflict.js +1 -1
  18. package/src/core/sync/create_manifest_data/index.js +82 -62
  19. package/src/core/sync/sync.js +40 -31
  20. package/src/core/sync/ts.js +1 -1
  21. package/src/core/sync/utils.js +22 -2
  22. package/src/core/sync/{write_non_ambient.js → write_app_types.js} +107 -58
  23. package/src/core/sync/write_client_manifest.js +14 -29
  24. package/src/core/sync/write_env.js +7 -5
  25. package/src/core/sync/write_server.js +37 -45
  26. package/src/core/sync/write_tsconfig/index.js +274 -0
  27. package/src/core/sync/write_tsconfig/test-app/package.json +7 -0
  28. package/src/core/sync/write_tsconfig/utils.js +77 -0
  29. package/src/core/sync/write_tsconfig/validate.js +128 -0
  30. package/src/core/sync/write_types/index.js +127 -123
  31. package/src/core/utils.js +30 -5
  32. package/src/exports/env/index.js +77 -0
  33. package/src/exports/env/public.d.ts +55 -0
  34. package/src/exports/hooks/index.js +3 -9
  35. package/src/exports/hooks/public.d.ts +195 -0
  36. package/src/exports/hooks/sequence.js +9 -6
  37. package/src/exports/index.js +52 -29
  38. package/src/exports/internal/client.js +5 -0
  39. package/src/exports/internal/env.js +8 -5
  40. package/src/exports/internal/index.js +1 -90
  41. package/src/exports/internal/{event.js → server/event.js} +2 -3
  42. package/src/exports/internal/server/index.js +37 -0
  43. package/src/exports/internal/server/telemetry.js +95 -0
  44. package/src/exports/internal/shared.js +90 -0
  45. package/src/exports/node/index.js +64 -22
  46. package/src/exports/params/index.js +70 -0
  47. package/src/exports/params/public.d.ts +63 -0
  48. package/src/exports/public.d.ts +50 -1733
  49. package/src/exports/remote/index.js +11 -0
  50. package/src/exports/remote/public.d.ts +519 -0
  51. package/src/exports/url.js +86 -0
  52. package/src/exports/vite/build/build_server.js +54 -65
  53. package/src/exports/vite/build/remote.js +24 -19
  54. package/src/exports/vite/build/utils.js +0 -8
  55. package/src/exports/vite/dev/index.js +218 -146
  56. package/src/exports/vite/index.js +1590 -858
  57. package/src/exports/vite/module_ids.js +2 -2
  58. package/src/exports/vite/preview/index.js +40 -32
  59. package/src/exports/vite/public.d.ts +588 -0
  60. package/src/exports/vite/utils.js +84 -46
  61. package/src/pathname.js +55 -0
  62. package/src/runner.js +15 -0
  63. package/src/runtime/app/env/internal.js +4 -4
  64. package/src/runtime/app/env/types.d.ts +1 -1
  65. package/src/runtime/app/environment/index.js +3 -3
  66. package/src/runtime/app/{forms.js → forms/index.js} +73 -42
  67. package/src/runtime/app/forms/public.d.ts +2 -0
  68. package/src/runtime/app/forms/types.d.ts +56 -0
  69. package/src/runtime/app/internal/transport.js +53 -0
  70. package/src/runtime/app/manifest/index.js +1 -0
  71. package/src/runtime/app/{navigation.js → navigation/index.js} +2 -1
  72. package/src/runtime/app/navigation/public.d.ts +237 -0
  73. package/src/runtime/app/paths/client.js +37 -37
  74. package/src/runtime/app/paths/index.js +1 -1
  75. package/src/runtime/app/paths/internal/client.js +34 -2
  76. package/src/runtime/app/paths/internal/server.js +6 -23
  77. package/src/runtime/app/paths/internal.d.ts +3 -0
  78. package/src/runtime/app/paths/public.d.ts +1 -29
  79. package/src/runtime/app/paths/server.js +36 -17
  80. package/src/runtime/app/paths/types.d.ts +11 -19
  81. package/src/runtime/app/server/index.js +2 -2
  82. package/src/runtime/app/server/public.d.ts +201 -0
  83. package/src/runtime/app/server/remote/command.js +13 -11
  84. package/src/runtime/app/server/remote/form.js +61 -39
  85. package/src/runtime/app/server/remote/prerender.js +45 -45
  86. package/src/runtime/app/server/remote/query.js +106 -108
  87. package/src/runtime/app/server/remote/requested.js +27 -19
  88. package/src/runtime/app/server/remote/shared.js +76 -76
  89. package/src/runtime/app/service-worker/index.js +24 -0
  90. package/src/runtime/app/state/client.js +4 -2
  91. package/src/runtime/app/state/index.js +7 -5
  92. package/src/runtime/app/state/public.d.ts +72 -0
  93. package/src/runtime/app/state/server.js +3 -0
  94. package/src/runtime/app/stores.js +15 -78
  95. package/src/runtime/client/bundle.js +1 -1
  96. package/src/runtime/client/client-entry.js +3 -0
  97. package/src/runtime/client/client.js +1336 -648
  98. package/src/runtime/client/constants.js +3 -6
  99. package/src/runtime/client/entry.js +24 -3
  100. package/src/runtime/client/fetcher.js +25 -25
  101. package/src/runtime/client/ndjson.js +1 -1
  102. package/src/runtime/client/parse.js +1 -1
  103. package/src/runtime/client/payload.js +17 -0
  104. package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
  105. package/src/runtime/client/remote-functions/command.svelte.js +37 -61
  106. package/src/runtime/client/remote-functions/form.svelte.js +267 -178
  107. package/src/runtime/client/remote-functions/prerender.svelte.js +32 -11
  108. package/src/runtime/client/remote-functions/query/index.js +10 -17
  109. package/src/runtime/client/remote-functions/query/instance.svelte.js +64 -18
  110. package/src/runtime/client/remote-functions/query/proxy.js +5 -5
  111. package/src/runtime/client/remote-functions/query-batch.svelte.js +61 -70
  112. package/src/runtime/client/remote-functions/query-live/index.js +1 -1
  113. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +63 -21
  114. package/src/runtime/client/remote-functions/query-live/iterator.js +15 -12
  115. package/src/runtime/client/remote-functions/query-live/proxy.js +2 -12
  116. package/src/runtime/client/remote-functions/shared.svelte.js +90 -65
  117. package/src/runtime/client/snapshots.js +147 -0
  118. package/src/runtime/client/state.svelte.js +94 -55
  119. package/src/runtime/client/stream.js +3 -2
  120. package/src/runtime/client/types.d.ts +13 -9
  121. package/src/runtime/client/utils.js +22 -110
  122. package/src/runtime/components/root.svelte +56 -0
  123. package/src/runtime/env/dynamic/private.js +7 -0
  124. package/src/runtime/env/dynamic/public.js +7 -0
  125. package/src/runtime/env/static/private.js +6 -0
  126. package/src/runtime/env/static/public.js +6 -0
  127. package/src/runtime/form-utils.js +172 -61
  128. package/src/runtime/pathname.js +20 -32
  129. package/src/runtime/props.svelte.js +72 -0
  130. package/src/runtime/server/constants.js +0 -3
  131. package/src/runtime/server/cookie.js +72 -55
  132. package/src/runtime/server/csrf.js +65 -0
  133. package/src/runtime/server/data/index.js +49 -53
  134. package/src/runtime/server/dev.js +22 -0
  135. package/src/runtime/server/endpoint.js +6 -7
  136. package/src/runtime/server/env_module.js +0 -5
  137. package/src/runtime/server/errors.js +160 -0
  138. package/src/runtime/server/fetch.js +32 -39
  139. package/src/runtime/server/index.js +90 -64
  140. package/src/runtime/server/internal.js +71 -0
  141. package/src/runtime/server/page/actions.js +85 -64
  142. package/src/runtime/server/page/crypto.js +2 -2
  143. package/src/runtime/server/page/csp.js +88 -104
  144. package/src/runtime/server/page/data_serializer.js +24 -25
  145. package/src/runtime/server/page/index.js +36 -53
  146. package/src/runtime/server/page/load_data.js +50 -57
  147. package/src/runtime/server/page/render.js +173 -239
  148. package/src/runtime/server/page/respond_with_error.js +17 -31
  149. package/src/runtime/server/page/serialize_data.js +2 -13
  150. package/src/runtime/server/page/server_routing.js +85 -26
  151. package/src/runtime/server/remote-functions.js +640 -0
  152. package/src/runtime/server/respond.js +190 -131
  153. package/src/runtime/server/sourcemaps.js +183 -0
  154. package/src/runtime/server/state.js +53 -0
  155. package/src/runtime/server/utils.js +13 -155
  156. package/src/runtime/shared.js +20 -40
  157. package/src/runtime/utils.js +3 -0
  158. package/src/types/ambient-private.d.ts +11 -1
  159. package/src/types/ambient.d.ts +87 -36
  160. package/src/types/global-private.d.ts +25 -25
  161. package/src/types/internal.d.ts +163 -157
  162. package/src/types/private.d.ts +41 -1
  163. package/src/utils/error.js +28 -4
  164. package/src/utils/escape.js +9 -25
  165. package/src/utils/features.js +1 -1
  166. package/src/utils/filesystem.js +1 -23
  167. package/src/utils/fork.js +7 -2
  168. package/src/utils/hash.js +21 -0
  169. package/src/utils/http.js +8 -7
  170. package/src/utils/import.js +9 -2
  171. package/src/utils/imports.js +83 -0
  172. package/src/utils/mime.js +9 -0
  173. package/src/utils/page_nodes.js +6 -7
  174. package/src/utils/params.js +67 -0
  175. package/src/utils/regex.js +9 -0
  176. package/src/utils/routing.js +145 -73
  177. package/src/utils/shared-iterator.js +5 -0
  178. package/src/utils/streaming.js +14 -4
  179. package/src/utils/url.js +33 -2
  180. package/src/version.js +1 -1
  181. package/types/index.d.ts +2616 -2433
  182. package/types/index.d.ts.map +120 -106
  183. package/src/core/sync/write_ambient.js +0 -18
  184. package/src/core/sync/write_root.js +0 -148
  185. package/src/core/sync/write_tsconfig.js +0 -250
  186. package/src/exports/internal/server.js +0 -22
  187. package/src/exports/node/polyfills.js +0 -30
  188. package/src/runtime/server/app.js +0 -9
  189. package/src/runtime/server/remote.js +0 -457
  190. package/src/runtime/shared-server.js +0 -7
  191. package/src/runtime/telemetry/otel.js +0 -21
  192. package/src/runtime/telemetry/record_span.js +0 -65
  193. package/src/types/synthetic/$lib.md +0 -5
  194. /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
  195. /package/src/{runtime/telemetry/noop.js → telemetry.js} +0 -0
@@ -1,10 +1,33 @@
1
+ /** @import { ParamMatcher, ParamValue } from '@sveltejs/kit/params' */
1
2
  import { BROWSER } from 'esm-env';
2
- import { decode_params } from './url.js';
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(String.fromCharCode(parseInt(content.slice(2), 16)));
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+?(?:=\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, import('@sveltejs/kit').ParamMatcher>} matchers
182
+ * @param {Record<string, ParamMatcher>} matchers
141
183
  */
142
184
  export function exec(match, params, matchers) {
143
- /** @type {Record<string, 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
- if (!param.matcher || matchers[param.matcher](value)) {
177
- result[param.name] = value;
218
+ const decoded = decodeURIComponent(value);
178
219
 
179
- // Now that the params match, reset the buffer if the next param isn't the [...rest]
180
- // and the next value is defined, otherwise the buffer will cause us to skip values
181
- const next_param = params[i + 1];
182
- const next_value = values[i + 1];
183
- if (next_param && !next_param.rest && next_param.optional && next_value && param.chained) {
184
- buffered = 0;
185
- }
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
- // There are no more params and no more values, but all non-empty values have been matched
188
- if (
189
- !next_param &&
190
- !next_value &&
191
- Object.keys(result).length === values_needing_match.length
192
- ) {
193
- buffered = 0;
231
+ // otherwise, if the matcher returns `false`, the route did not match
232
+ return;
194
233
  }
195
- continue;
234
+
235
+ result[param.name] = outcome.value;
236
+ } else {
237
+ result[param.name] = decoded;
196
238
  }
197
239
 
198
- // in the `/[[a=b]]/...` case, if the value didn't satisfy the matcher,
199
- // keep track of the number of skipped optional parameters and continue
200
- if (param.optional && param.chained) {
201
- buffered++;
202
- continue;
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
- // otherwise, if the matcher returns `false`, the route did not match
206
- return;
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
- return (
216
- str
217
- .normalize()
218
- // escape [ and ] before escaping other characters, since they are used in the replacements
219
- .replace(/[[\]]/g, '\\$&')
220
- // replace %, /, ? and # with their encoded versions because decode_pathname leaves them untouched
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, string | undefined>} params
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(basic_param_pattern, (_, optional, rest, name) => {
257
- const param_value = params[name];
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
- // This is nested so TS correctly narrows the type
260
- if (!param_value) {
318
+ if (value === undefined || value === '') {
261
319
  if (optional) return '';
262
- if (rest && param_value !== undefined) return '';
320
+ if (rest && value !== undefined) return '';
263
321
  throw new Error(`Missing parameter '${name}' in route ${id}`);
264
322
  }
265
323
 
266
- if (param_value.startsWith('/') || param_value.endsWith('/'))
267
- throw new Error(
268
- `Parameter '${name}' in route ${id} cannot start or end with a slash -- this would cause an invalid route like foo//bar`
269
- );
270
- return param_value;
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, import('@sveltejs/kit').ParamMatcher>} matchers
293
- * @returns {{ route: Route, params: Record<string, string> } | null}
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: decode_params(matched)
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
  */
@@ -1,3 +1,5 @@
1
+ import { noop } from './functions.js';
2
+
1
3
  /**
2
4
  * Create an async iterator and a function to push values into it
3
5
  * @template T
@@ -30,10 +32,18 @@ export function create_async_iterator() {
30
32
  };
31
33
  },
32
34
  add: (promise) => {
33
- deferred.push(Promise.withResolvers());
34
- void promise.then((value) => {
35
- deferred[++resolved].resolve(value);
36
- });
35
+ const next = Promise.withResolvers();
36
+ void next.promise.catch(noop); // prevent unhandled rejection potentially crashing the process
37
+ deferred.push(next);
38
+
39
+ void promise.then(
40
+ (value) => {
41
+ deferred[++resolved].resolve(value);
42
+ },
43
+ (error) => {
44
+ deferred[++resolved].reject(error);
45
+ }
46
+ );
37
47
  }
38
48
  };
39
49
  }
package/src/utils/url.js CHANGED
@@ -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+\-.]+:/i;
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
@@ -1,4 +1,4 @@
1
1
  // generated during release, do not modify
2
2
 
3
3
  /** @type {string} */
4
- export const VERSION = '3.0.0-next.2';
4
+ export const VERSION = '3.0.0-next.21';