@sveltejs/kit 2.60.1 → 2.61.0

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 (45) hide show
  1. package/package.json +8 -9
  2. package/src/core/postbuild/analyse.js +1 -3
  3. package/src/core/sync/create_manifest_data/conflict.js +72 -0
  4. package/src/core/sync/create_manifest_data/index.js +1 -65
  5. package/src/core/sync/write_non_ambient.js +2 -2
  6. package/src/core/sync/write_types/index.js +1 -1
  7. package/src/exports/public.d.ts +23 -30
  8. package/src/exports/vite/build/build_server.js +36 -13
  9. package/src/exports/vite/dev/index.js +4 -2
  10. package/src/exports/vite/index.js +18 -16
  11. package/src/runtime/app/server/index.js +1 -2
  12. package/src/runtime/app/server/remote/form.js +10 -0
  13. package/src/runtime/app/server/remote/query.js +100 -36
  14. package/src/runtime/client/client.js +13 -8
  15. package/src/runtime/client/remote-functions/cache.svelte.js +157 -0
  16. package/src/runtime/client/remote-functions/form.svelte.js +235 -196
  17. package/src/runtime/client/remote-functions/index.js +2 -2
  18. package/src/runtime/client/remote-functions/prerender.svelte.js +1 -2
  19. package/src/runtime/client/remote-functions/query/cache.js +4 -0
  20. package/src/runtime/client/remote-functions/query/index.js +48 -0
  21. package/src/runtime/client/remote-functions/query/instance.svelte.js +249 -0
  22. package/src/runtime/client/remote-functions/query/proxy.js +156 -0
  23. package/src/runtime/client/remote-functions/query-batch.svelte.js +1 -1
  24. package/src/runtime/client/remote-functions/query-live/cache.js +4 -0
  25. package/src/runtime/client/remote-functions/query-live/index.js +31 -0
  26. package/src/runtime/client/remote-functions/{query-live.svelte.js → query-live/instance.svelte.js} +61 -310
  27. package/src/runtime/client/remote-functions/query-live/iterator.js +91 -0
  28. package/src/runtime/client/remote-functions/query-live/proxy.js +144 -0
  29. package/src/runtime/client/remote-functions/shared.svelte.js +53 -6
  30. package/src/runtime/client/utils.js +1 -1
  31. package/src/runtime/form-utils.js +7 -16
  32. package/src/runtime/server/index.js +2 -3
  33. package/src/runtime/server/page/actions.js +2 -9
  34. package/src/runtime/server/page/csp.js +3 -4
  35. package/src/runtime/server/page/render.js +13 -14
  36. package/src/runtime/server/respond.js +60 -36
  37. package/src/runtime/server/utils.js +23 -3
  38. package/src/types/global-private.d.ts +5 -0
  39. package/src/types/internal.d.ts +29 -6
  40. package/src/utils/routing.js +3 -1
  41. package/src/utils/shared-iterator.js +213 -0
  42. package/src/version.js +1 -1
  43. package/types/index.d.ts +27 -31
  44. package/types/index.d.ts.map +1 -1
  45. package/src/runtime/client/remote-functions/query.svelte.js +0 -512
@@ -6,7 +6,6 @@ import { HttpError, Redirect } from '@sveltejs/kit/internal';
6
6
  import { untrack } from 'svelte';
7
7
  import { create_remote_key, split_remote_key } from '../../shared.js';
8
8
  import { navigating, page } from '../state.svelte.js';
9
- import { noop } from '../../../utils/functions.js';
10
9
 
11
10
  /** Indicates a query function, as opposed to a query instance */
12
11
  export const QUERY_FUNCTION_ID = Symbol('sveltekit.query_function_id');
@@ -16,17 +15,65 @@ export const QUERY_OVERRIDE_KEY = Symbol('sveltekit.query_override_key');
16
15
  export const QUERY_RESOURCE_KEY = Symbol('sveltekit.query_resource_key');
17
16
 
18
17
  /**
19
- * @returns {boolean} Returns `true` if we are in an effect
18
+ * If we're inside a reactive context, pin a cache entry for as long as the
19
+ * surrounding effect is alive. Without this, a transiently-referenced
20
+ * `QueryProxy`/`LiveQueryProxy` (e.g. one produced by `{await fn()}` in a
21
+ * template or by `$derived(await fn())`) would be eligible for GC as soon as
22
+ * the awaited value has been read, after which the FinalizationRegistry
23
+ * would evict the cache entry — even though the consuming effect is still
24
+ * alive and may rely on the entry being refreshed (e.g. via `refreshAll()`
25
+ * or a server-initiated single-flight refresh).
26
+ *
27
+ * @template TResource
28
+ * @param {Map<string, Map<string, { resource: TResource }>>} cache_map
29
+ * @param {{ manual_ref: (entry: any, id: string, payload: string) => () => void }} cache
30
+ * @param {string} id
31
+ * @param {string} payload
20
32
  */
21
- export function is_in_effect() {
33
+ export function pin_in_effect(cache_map, cache, id, payload) {
22
34
  try {
23
- $effect.pre(noop);
24
- return true;
35
+ $effect.pre(() => {
36
+ const entry = cache_map.get(id)?.get(payload);
37
+ if (!entry) return;
38
+ return cache.manual_ref(entry, id, payload);
39
+ });
25
40
  } catch {
26
- return false;
41
+ // not in an effect context — nothing to pin
27
42
  }
28
43
  }
29
44
 
45
+ /**
46
+ * Wrap a proxy's `then`/`catch`/`finally` function so that the underlying
47
+ * cache entry stays pinned for the lifetime of the awaited promise. Without
48
+ * this, a proxy awaited outside any effect (e.g. in an event handler) could
49
+ * be GC'd between the `.then` getter returning the thenable and the
50
+ * underlying promise settling, causing the FinalizationRegistry to evict the
51
+ * cache entry mid-flight and the awaited value to resolve to Svelte's
52
+ * `UNINITIALIZED` sentinel from a torn-down `$derived`.
53
+ *
54
+ * @template TResource
55
+ * @template {(...args: any[]) => Promise<any>} TThen
56
+ * @param {Map<string, Map<string, { resource: TResource }>>} cache_map
57
+ * @param {{ manual_ref: (entry: any, id: string, payload: string) => () => void }} cache
58
+ * @param {string} id
59
+ * @param {string} payload
60
+ * @param {TThen} then
61
+ * @returns {TThen}
62
+ */
63
+ export function pin_while_resolving(cache_map, cache, id, payload, then) {
64
+ return /** @type {TThen} */ (
65
+ (...a) => {
66
+ const entry = cache_map.get(id)?.get(payload);
67
+ const release = entry ? cache.manual_ref(entry, id, payload) : undefined;
68
+ const promise = then(...a);
69
+ if (release) {
70
+ promise.then(release, release);
71
+ }
72
+ return promise;
73
+ }
74
+ );
75
+ }
76
+
30
77
  /**
31
78
  * @returns {{ 'x-sveltekit-pathname': string, 'x-sveltekit-search': string }}
32
79
  */
@@ -249,7 +249,7 @@ export const updated_listener = {
249
249
  export function create_updated_store() {
250
250
  const { set, subscribe } = writable(false);
251
251
 
252
- if (DEV || !BROWSER) {
252
+ if (__SVELTEKIT_DEV__ || !BROWSER) {
253
253
  return {
254
254
  subscribe,
255
255
  // eslint-disable-next-line @typescript-eslint/require-await
@@ -145,10 +145,6 @@ export async function deserialize_binary_form(request) {
145
145
  if (!request.body) {
146
146
  throw deserialize_error('no body');
147
147
  }
148
- const content_length = parseInt(request.headers.get('content-length') ?? '');
149
- if (Number.isNaN(content_length)) {
150
- throw deserialize_error('invalid Content-Length header');
151
- }
152
148
 
153
149
  const reader = request.body.getReader();
154
150
 
@@ -228,16 +224,11 @@ export async function deserialize_binary_form(request) {
228
224
  }
229
225
  const header_view = new DataView(header.buffer, header.byteOffset, header.byteLength);
230
226
  const data_length = header_view.getUint32(1, true);
231
-
232
- if (HEADER_BYTES + data_length > content_length) {
233
- throw deserialize_error('data overflow');
234
- }
235
-
236
227
  const file_offsets_length = header_view.getUint16(5, true);
237
228
 
238
- if (HEADER_BYTES + data_length + file_offsets_length > content_length) {
239
- throw deserialize_error('file offset table overflow');
240
- }
229
+ // Validation uses embedded binary header fields (data_length, file_offsets_length)
230
+ // rather than Content-Length, which proxies/middleboxes may strip or corrupt.
231
+ // See: https://github.com/sveltejs/kit/issues/15299
241
232
 
242
233
  // Read the form data
243
234
  const data_buffer = await get_buffer(HEADER_BYTES, data_length);
@@ -289,9 +280,6 @@ export async function deserialize_binary_form(request) {
289
280
  file_offsets[index] = undefined;
290
281
 
291
282
  offset += files_start_offset;
292
- if (offset + size > content_length) {
293
- throw deserialize_error('file data overflow');
294
- }
295
283
 
296
284
  file_spans.push({ offset, size });
297
285
 
@@ -716,8 +704,11 @@ export function create_field_proxy(target, get_input, set_input, get_issues, pat
716
704
  }
717
705
  }
718
706
 
707
+ const value =
708
+ typeof input_value === 'boolean' ? (input_value ? 'on' : 'off') : input_value;
709
+
719
710
  return Object.defineProperties(base_props, {
720
- value: { value: input_value, enumerable: true }
711
+ value: { value, enumerable: true }
721
712
  });
722
713
  }
723
714
 
@@ -5,7 +5,6 @@ import { IN_WEBCONTAINER } from './constants.js';
5
5
  import { respond } from './respond.js';
6
6
  import { set_private_env, set_public_env } from '../shared-server.js';
7
7
  import { options, get_hooks } from '__SERVER__/internal.js';
8
- import { DEV } from 'esm-env';
9
8
  import { filter_env } from '../../utils/env.js';
10
9
  import { format_server_error } from './utils.js';
11
10
  import { set_read_implementation, set_manifest } from '__sveltekit/server';
@@ -102,7 +101,7 @@ export class Server {
102
101
  set_read_implementation(wrapped_read);
103
102
  }
104
103
 
105
- // During DEV and for some adapters this function might be called in quick succession,
104
+ // During dev and for some adapters this function might be called in quick succession,
106
105
  // so we need to make sure we're not invoking this logic (most notably the init hook) multiple times
107
106
  await (init_promise ??= (async () => {
108
107
  try {
@@ -141,7 +140,7 @@ export class Server {
141
140
  await module.init();
142
141
  }
143
142
  } catch (e) {
144
- if (DEV) {
143
+ if (__SVELTEKIT_DEV__) {
145
144
  this.#options.hooks = {
146
145
  handle: () => {
147
146
  throw e;
@@ -7,7 +7,7 @@ import { HttpError, Redirect, ActionFailure, SvelteKitError } from '@sveltejs/ki
7
7
  import { with_request_store, merge_tracing } from '@sveltejs/kit/internal/server';
8
8
  import { get_status, normalize_error } from '../../../utils/error.js';
9
9
  import { is_form_content_type, negotiate } from '../../../utils/http.js';
10
- import { handle_error_and_jsonify } from '../utils.js';
10
+ import { create_replacer, handle_error_and_jsonify } from '../utils.js';
11
11
  import { record_span } from '../../telemetry/record_span.js';
12
12
 
13
13
  /** @param {RequestEvent} event */
@@ -301,14 +301,7 @@ function validate_action_return(data) {
301
301
  * @param {ServerHooks['transport']} transport
302
302
  */
303
303
  export function uneval_action_response(data, route_id, transport) {
304
- const replacer = (/** @type {any} */ thing) => {
305
- for (const key in transport) {
306
- const encoded = transport[key].encode(thing);
307
- if (encoded) {
308
- return `app.decode('${key}', ${devalue.uneval(encoded, replacer)})`;
309
- }
310
- }
311
- };
304
+ const replacer = create_replacer(transport);
312
305
 
313
306
  return try_serialize(data, (value) => devalue.uneval(value, replacer), route_id);
314
307
  }
@@ -1,4 +1,3 @@
1
- import { DEV } from 'esm-env';
2
1
  import { escape_html } from '../../../utils/escape.js';
3
2
  import { sha256 } from './crypto.js';
4
3
 
@@ -87,7 +86,7 @@ class BaseProvider {
87
86
  */
88
87
  constructor(use_hashes, directives, nonce) {
89
88
  this.#use_hashes = use_hashes;
90
- this.#directives = DEV ? { ...directives } : directives; // clone in dev so we can safely mutate
89
+ this.#directives = __SVELTEKIT_DEV__ ? { ...directives } : directives; // clone in dev so we can safely mutate
91
90
 
92
91
  const d = this.#directives;
93
92
 
@@ -103,7 +102,7 @@ class BaseProvider {
103
102
  const style_src_attr = d['style-src-attr'];
104
103
  const style_src_elem = d['style-src-elem'];
105
104
 
106
- if (DEV) {
105
+ if (__SVELTEKIT_DEV__) {
107
106
  // remove strict-dynamic in dev...
108
107
  // TODO reinstate this if we can figure out how to make strict-dynamic work
109
108
  // if (d['default-src']) {
@@ -164,7 +163,7 @@ class BaseProvider {
164
163
 
165
164
  this.#script_needs_csp = this.#script_src_needs_csp || this.#script_src_elem_needs_csp;
166
165
  this.#style_needs_csp =
167
- !DEV &&
166
+ !__SVELTEKIT_DEV__ &&
168
167
  (this.#style_src_needs_csp ||
169
168
  this.#style_src_attr_needs_csp ||
170
169
  this.#style_src_elem_needs_csp);
@@ -15,7 +15,12 @@ import { create_server_routing_response, generate_route_object } from './server_
15
15
  import { add_resolution_suffix } from '../../pathname.js';
16
16
  import { try_get_request_store, with_request_store } from '@sveltejs/kit/internal/server';
17
17
  import { text_encoder } from '../../utils.js';
18
- import { count_non_ssi_comments, get_global_name, handle_error_and_jsonify } from '../utils.js';
18
+ import {
19
+ count_non_ssi_comments,
20
+ create_replacer,
21
+ get_global_name,
22
+ handle_error_and_jsonify
23
+ } from '../utils.js';
19
24
  import { create_remote_key } from '../../shared.js';
20
25
  import { get_status } from '../../../utils/error.js';
21
26
 
@@ -302,13 +307,15 @@ export async function render_response({
302
307
  return `${assets}/${path}`;
303
308
  };
304
309
 
305
- // inline styles can come from `bundleStrategy: 'inline'` or `inlineStyleThreshold`
306
310
  const style = client.inline
307
311
  ? client.inline?.style
308
312
  : Array.from(inline_styles.values()).join('\n');
309
313
 
310
314
  if (style) {
311
- const attributes = DEV ? ['data-sveltekit'] : [];
315
+ // We always inline all styles to avoid FOUC during development.
316
+ // Once that's accomplished, we find and remove the style node using the
317
+ // `data-sveltekit` attribute once CSR kicks in
318
+ const attributes = __SVELTEKIT_DEV__ ? ['data-sveltekit'] : [];
312
319
  if (csp.style_needs_nonce) attributes.push(`nonce="${csp.nonce}"`);
313
320
  csp.add_style(style);
314
321
  head.add_style(style, attributes);
@@ -557,15 +564,7 @@ export async function render_response({
557
564
  }
558
565
  }
559
566
 
560
- // TODO this is repeated in a few places — dedupe it
561
- const replacer = (/** @type {any} */ thing) => {
562
- for (const key in options.hooks.transport) {
563
- const encoded = options.hooks.transport[key].encode(thing);
564
- if (encoded) {
565
- return `app.decode('${key}', ${devalue.uneval(encoded, replacer)})`;
566
- }
567
- }
568
- };
567
+ const replacer = create_replacer(options.hooks.transport);
569
568
 
570
569
  if (Object.keys(query).length > 0) {
571
570
  serialized_query_data = `${global}.query = ${devalue.uneval(query, replacer)};\n\n\t\t\t\t\t\t`;
@@ -605,10 +604,10 @@ export async function render_response({
605
604
  }
606
605
 
607
606
  if (options.service_worker) {
608
- let opts = DEV ? ", { type: 'module' }" : '';
607
+ let opts = __SVELTEKIT_DEV__ ? ", { type: 'module' }" : '';
609
608
  if (options.service_worker_options != null) {
610
609
  const service_worker_options = { ...options.service_worker_options };
611
- if (DEV) {
610
+ if (__SVELTEKIT_DEV__) {
612
611
  service_worker_options.type = 'module';
613
612
  }
614
613
  opts = `, ${s(service_worker_options)}`;
@@ -40,8 +40,6 @@ import { get_remote_id, handle_remote_call } from './remote.js';
40
40
  import { record_span } from '../telemetry/record_span.js';
41
41
  import { otel } from '../telemetry/otel.js';
42
42
 
43
- /* global __SVELTEKIT_ADAPTER_NAME__ */
44
-
45
43
  /** @type {import('types').RequiredResolveOptions['transformPageChunk']} */
46
44
  const default_transform = ({ html }) => html;
47
45
 
@@ -153,7 +151,8 @@ export async function internal_respond(request, options, manifest, state) {
153
151
  refreshes: null,
154
152
  requested: null,
155
153
  reconnects: null,
156
- batches: null
154
+ batches: null,
155
+ live_iterators: null
157
156
  },
158
157
  is_in_remote_function: false,
159
158
  is_in_render: false,
@@ -228,6 +227,7 @@ export async function internal_respond(request, options, manifest, state) {
228
227
  });
229
228
  }
230
229
 
230
+ /** @type {string | null} */
231
231
  let resolved_path = url.pathname;
232
232
 
233
233
  if (!remote_id) {
@@ -249,10 +249,24 @@ export async function internal_respond(request, options, manifest, state) {
249
249
  }
250
250
  }
251
251
 
252
+ /** @type {import('types').RequiredResolveOptions} */
253
+ let resolve_opts = {
254
+ transformPageChunk: default_transform,
255
+ filterSerializedResponseHeaders: default_filter,
256
+ preload: default_preload
257
+ };
258
+
259
+ /** @type {import('types').TrailingSlash} */
260
+ let trailing_slash = 'never';
261
+
262
+ /** @type {PageNodes | undefined} */
263
+ let page_nodes;
264
+
252
265
  try {
253
266
  resolved_path = decode_pathname(resolved_path);
254
267
  } catch {
255
- return text('Malformed URI', { status: 400 });
268
+ resolved_path = null;
269
+ return await handle();
256
270
  }
257
271
 
258
272
  // try to serve the rerouted prerendered resource if it exists
@@ -326,19 +340,8 @@ export async function internal_respond(request, options, manifest, state) {
326
340
  }
327
341
  }
328
342
 
329
- /** @type {import('types').RequiredResolveOptions} */
330
- let resolve_opts = {
331
- transformPageChunk: default_transform,
332
- filterSerializedResponseHeaders: default_filter,
333
- preload: default_preload
334
- };
335
-
336
- /** @type {import('types').TrailingSlash} */
337
- let trailing_slash = 'never';
338
-
339
343
  try {
340
- /** @type {PageNodes | undefined} */
341
- const page_nodes = route?.page
344
+ page_nodes = route?.page
342
345
  ? new PageNodes(await load_page_nodes(route.page, manifest))
343
346
  : undefined;
344
347
 
@@ -393,16 +396,36 @@ export async function internal_respond(request, options, manifest, state) {
393
396
  prerender = page_nodes.prerender();
394
397
  }
395
398
 
396
- if (state.before_handle) {
397
- state.before_handle(event, config, prerender);
398
- }
399
-
400
399
  if (state.emulator?.platform) {
401
400
  event.platform = await state.emulator.platform({ config, prerender });
402
401
  }
402
+
403
+ if (state.before_handle) {
404
+ return await state.before_handle(event, config, prerender, handle);
405
+ }
406
+ }
407
+ }
408
+
409
+ return await handle();
410
+ } catch (e) {
411
+ if (e instanceof Redirect) {
412
+ try {
413
+ const response =
414
+ is_data_request || remote_id
415
+ ? redirect_json_response(e)
416
+ : route?.page && is_action_json_request(event)
417
+ ? action_json_redirect(e)
418
+ : redirect_response(e.status, e.location);
419
+ add_cookies_to_headers(response.headers, new_cookies.values());
420
+ return response;
421
+ } catch (err) {
422
+ return await handle_fatal_error(event, event_state, options, err);
403
423
  }
404
424
  }
425
+ return await handle_fatal_error(event, event_state, options, e);
426
+ }
405
427
 
428
+ async function handle() {
406
429
  set_trailing_slash(trailing_slash);
407
430
 
408
431
  if (state.prerendering && !state.prerendering.fallback && !state.prerendering.inside_reroute) {
@@ -518,22 +541,6 @@ export async function internal_respond(request, options, manifest, state) {
518
541
  }
519
542
 
520
543
  return response;
521
- } catch (e) {
522
- if (e instanceof Redirect) {
523
- try {
524
- const response =
525
- is_data_request || remote_id
526
- ? redirect_json_response(e)
527
- : route?.page && is_action_json_request(event)
528
- ? action_json_redirect(e)
529
- : redirect_response(e.status, e.location);
530
- add_cookies_to_headers(response.headers, new_cookies.values());
531
- return response;
532
- } catch (err) {
533
- return await handle_fatal_error(event, event_state, options, err);
534
- }
535
- }
536
- return await handle_fatal_error(event, event_state, options, e);
537
544
  }
538
545
 
539
546
  /**
@@ -551,6 +558,23 @@ export async function internal_respond(request, options, manifest, state) {
551
558
  };
552
559
  }
553
560
 
561
+ if (resolved_path === null) {
562
+ return await respond_with_error({
563
+ event,
564
+ event_state,
565
+ options,
566
+ manifest,
567
+ state,
568
+ status: 400,
569
+ error: new SvelteKitError(
570
+ 400,
571
+ 'Malformed URI',
572
+ `Failed to decode URI: ${event.url.pathname}`
573
+ ),
574
+ resolve_opts
575
+ });
576
+ }
577
+
554
578
  if (options.hash_routing || state.prerendering?.fallback) {
555
579
  return await render_response({
556
580
  event,
@@ -1,3 +1,5 @@
1
+ /** @import { ServerHooks } from 'types' */
2
+ import * as devalue from 'devalue';
1
3
  import { DEV } from 'esm-env';
2
4
  import { json, text } from '@sveltejs/kit';
3
5
  import { HttpError } from '@sveltejs/kit/internal';
@@ -40,7 +42,7 @@ export function allowed_methods(mod) {
40
42
  * @param {import('types').SSROptions} options
41
43
  */
42
44
  export function get_global_name(options) {
43
- return DEV ? '__sveltekit_dev' : `__sveltekit_${options.version_hash}`;
45
+ return __SVELTEKIT_DEV__ ? '__sveltekit_dev' : `__sveltekit_${options.version_hash}`;
44
46
  }
45
47
 
46
48
  /**
@@ -53,7 +55,7 @@ export function get_global_name(options) {
53
55
  export function static_error_page(options, status, message) {
54
56
  let page = options.templates.error({ status, message: escape_html(message) });
55
57
 
56
- if (DEV) {
58
+ if (__SVELTEKIT_DEV__) {
57
59
  // inject Vite HMR client, for easier debugging
58
60
  page = page.replace('</head>', '<script type="module" src="/@vite/client"></script></head>');
59
61
  }
@@ -103,7 +105,7 @@ export async function handle_error_and_jsonify(event, state, options, error) {
103
105
  return { message: 'Unknown Error', ...error.body };
104
106
  }
105
107
 
106
- if (DEV && typeof error == 'object') {
108
+ if (__SVELTEKIT_DEV__ && typeof error == 'object') {
107
109
  fix_stack_trace(error);
108
110
  }
109
111
 
@@ -261,3 +263,21 @@ export function get_node_type(node_id) {
261
263
  export function count_non_ssi_comments(str) {
262
264
  return (str.match(/<!--(?!#)/g) ?? []).length;
263
265
  }
266
+
267
+ /**
268
+ * Creates a serialiser for non-arbitrary POJOs using the app's transport hook
269
+ * @param {ServerHooks['transport']} transport
270
+ * @returns {(thing: unknown) => string | undefined}
271
+ */
272
+ export function create_replacer(transport) {
273
+ /** @param {unknown} thing */
274
+ const replacer = (thing) => {
275
+ for (const key in transport) {
276
+ const encoded = transport[key].encode(thing);
277
+ if (encoded) {
278
+ return `app.decode('${key}', ${devalue.uneval(encoded, replacer)})`;
279
+ }
280
+ }
281
+ };
282
+ return replacer;
283
+ }
@@ -3,6 +3,11 @@ declare global {
3
3
  const __SVELTEKIT_APP_DIR__: string;
4
4
  const __SVELTEKIT_APP_VERSION_FILE__: string;
5
5
  const __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: number;
6
+ /**
7
+ * True if the user ran `vite dev`. This is different from `esm-env` because
8
+ * it is influenced by `NODE_ENV` which can still be true during `vite preview`
9
+ */
10
+ const __SVELTEKIT_DEV__: boolean;
6
11
  const __SVELTEKIT_EMBEDDED__: boolean;
7
12
  const __SVELTEKIT_PATHS_ASSETS__: string;
8
13
  const __SVELTEKIT_PATHS_BASE__: string;
@@ -35,6 +35,7 @@ import {
35
35
  } from './private.js';
36
36
  import { Span } from '@opentelemetry/api';
37
37
  import type { PageOptions } from '../exports/vite/static_analysis/index.js';
38
+ import { SharedIterator } from '../utils/shared-iterator.js';
38
39
 
39
40
  export interface ServerModule {
40
41
  Server: typeof InternalServer;
@@ -179,9 +180,15 @@ export class InternalServer extends Server {
179
180
  request: Request,
180
181
  options: RequestOptions & {
181
182
  prerendering?: PrerenderOptions;
182
- read: (file: string) => NonSharedBuffer;
183
- /** A hook called before `handle` during dev, so that `AsyncLocalStorage` can be populated. */
184
- before_handle?: (event: RequestEvent, config: any, prerender: PrerenderOption) => void;
183
+ /** @internal for saving dependencies during prerendering and generating fallback pages */
184
+ read: (file: string) => Buffer<ArrayBuffer>;
185
+ /** @internal used during development to check feature availability depending on the current route */
186
+ before_handle?: (
187
+ event: RequestEvent,
188
+ config: any,
189
+ prerender: PrerenderOption,
190
+ handle: () => Promise<Response>
191
+ ) => Promise<Response>;
185
192
  emulator?: Emulator;
186
193
  }
187
194
  ): Promise<Response>;
@@ -469,7 +476,10 @@ export interface SSRNode {
469
476
  universal_id?: string;
470
477
  server_id?: string;
471
478
 
472
- /** inlined styles */
479
+ /**
480
+ * During development, all styles are inlined for the page to avoid FOUC.
481
+ * But in production, this stores styles that are below the inline threshold
482
+ */
473
483
  inline_styles?(): MaybePromise<
474
484
  Record<string, string | ((assets: string, base: string) => string)>
475
485
  >;
@@ -565,12 +575,18 @@ export interface SSRState {
565
575
  * prerender option is inherited by the endpoint, unless overridden.
566
576
  */
567
577
  prerender_default?: PrerenderOption;
568
- read?: (file: string) => NonSharedBuffer;
578
+ /** @internal reads from the filesystem when user code tries to fetch a static asset */
579
+ read?: (file: string) => Buffer<ArrayBuffer>;
569
580
  /**
570
581
  * Used to set up `__SVELTEKIT_TRACK__` which checks if a used feature is supported.
571
582
  * E.g. if `read` from `$app/server` is used, it checks whether the route's config is compatible.
572
583
  */
573
- before_handle?: (event: RequestEvent, config: any, prerender: PrerenderOption) => void;
584
+ before_handle?: (
585
+ event: RequestEvent,
586
+ config: any,
587
+ prerender: PrerenderOption,
588
+ handle: () => Promise<Response>
589
+ ) => Promise<Response>;
574
590
  emulator?: Emulator;
575
591
  }
576
592
 
@@ -718,6 +734,13 @@ export interface RequestState {
718
734
  }
719
735
  >
720
736
  >;
737
+ /**
738
+ * A per-request cache of shared live-query iterators, keyed by remote id
739
+ * and stringified argument payload. Used so that multiple `for await`
740
+ * consumers of the same `query.live` call within one request multiplex a
741
+ * single underlying user generator.
742
+ */
743
+ live_iterators: null | Map<string, SharedIterator<any>>;
721
744
  };
722
745
  readonly is_in_remote_function: boolean;
723
746
  readonly is_in_render: boolean;
@@ -3,6 +3,8 @@ import { decode_params } from './url.js';
3
3
 
4
4
  const param_pattern = /^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;
5
5
 
6
+ const root_group_pattern = /^\/\((?:.+)\)$/;
7
+
6
8
  /**
7
9
  * Creates the regex pattern, extracts parameter names, and generates types for a route
8
10
  * @param {string} id
@@ -12,7 +14,7 @@ export function parse_route_id(id) {
12
14
  const params = [];
13
15
 
14
16
  const pattern =
15
- id === '/'
17
+ id === '/' || root_group_pattern.test(id)
16
18
  ? /^\/$/
17
19
  : new RegExp(
18
20
  `^${get_route_segments(id)