@sveltejs/kit 3.0.0-next.13 → 3.0.0-next.16

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 (86) hide show
  1. package/package.json +7 -4
  2. package/src/cli.js +8 -5
  3. package/src/core/adapt/builder.js +20 -10
  4. package/src/core/config/index.js +4 -5
  5. package/src/core/env.js +3 -3
  6. package/src/core/generate_manifest/index.js +6 -0
  7. package/src/core/postbuild/entities.js +8 -2
  8. package/src/core/postbuild/fallback.js +2 -1
  9. package/src/core/postbuild/prerender.js +18 -13
  10. package/src/core/sync/create_manifest_data/conflict.js +1 -1
  11. package/src/core/sync/create_manifest_data/index.js +33 -2
  12. package/src/core/sync/sync.js +3 -2
  13. package/src/core/sync/utils.js +2 -2
  14. package/src/core/sync/write_app_types.js +72 -27
  15. package/src/core/sync/write_tsconfig/index.js +73 -54
  16. package/src/core/sync/write_tsconfig/utils.js +0 -61
  17. package/src/core/sync/write_tsconfig/validate.js +128 -0
  18. package/src/core/sync/write_types/index.js +7 -4
  19. package/src/exports/node/index.js +2 -7
  20. package/src/exports/public.d.ts +25 -18
  21. package/src/exports/vite/build/build_server.js +2 -3
  22. package/src/exports/vite/dev/index.js +44 -36
  23. package/src/exports/vite/index.js +91 -16
  24. package/src/exports/vite/utils.js +62 -15
  25. package/src/runner.js +4 -2
  26. package/src/runtime/app/forms.js +4 -7
  27. package/src/runtime/app/internal/transport.js +53 -0
  28. package/src/runtime/app/paths/client.js +2 -2
  29. package/src/runtime/app/paths/internal/client.js +2 -2
  30. package/src/runtime/app/server/remote/prerender.js +6 -10
  31. package/src/runtime/app/server/remote/query.js +3 -9
  32. package/src/runtime/app/server/remote/requested.js +2 -2
  33. package/src/runtime/app/server/remote/shared.js +1 -16
  34. package/src/runtime/client/client.js +177 -63
  35. package/src/runtime/client/fetcher.js +2 -13
  36. package/src/runtime/client/parse.js +1 -1
  37. package/src/runtime/client/remote-functions/command.svelte.js +1 -2
  38. package/src/runtime/client/remote-functions/form.svelte.js +3 -7
  39. package/src/runtime/client/remote-functions/prerender.svelte.js +2 -2
  40. package/src/runtime/client/remote-functions/query/index.js +1 -1
  41. package/src/runtime/client/remote-functions/query/proxy.js +2 -2
  42. package/src/runtime/client/remote-functions/query-batch.svelte.js +1 -1
  43. package/src/runtime/client/remote-functions/query-live/proxy.js +2 -12
  44. package/src/runtime/client/remote-functions/shared.svelte.js +1 -1
  45. package/src/runtime/client/utils.js +1 -0
  46. package/src/runtime/form-utils.js +4 -6
  47. package/src/runtime/pathname.js +44 -1
  48. package/src/runtime/server/data/index.js +5 -8
  49. package/src/runtime/server/dev.js +22 -0
  50. package/src/runtime/server/endpoint.js +3 -4
  51. package/src/runtime/server/fetch.js +25 -30
  52. package/src/runtime/server/index.js +64 -46
  53. package/src/runtime/server/internal.js +12 -5
  54. package/src/runtime/server/page/actions.js +18 -43
  55. package/src/runtime/server/page/crypto.js +2 -2
  56. package/src/runtime/server/page/csp.js +2 -1
  57. package/src/runtime/server/page/data_serializer.js +12 -13
  58. package/src/runtime/server/page/index.js +16 -32
  59. package/src/runtime/server/page/load_data.js +28 -44
  60. package/src/runtime/server/page/render.js +28 -12
  61. package/src/runtime/server/page/respond_with_error.js +8 -20
  62. package/src/runtime/server/page/serialize_data.js +2 -13
  63. package/src/runtime/server/page/server_routing.js +58 -9
  64. package/src/runtime/server/remote-functions.js +11 -15
  65. package/src/runtime/server/respond.js +44 -56
  66. package/src/runtime/server/state.js +60 -0
  67. package/src/runtime/server/utils.js +0 -20
  68. package/src/runtime/shared.js +20 -40
  69. package/src/runtime/utils.js +3 -0
  70. package/src/types/ambient-private.d.ts +1 -1
  71. package/src/types/ambient.d.ts +58 -7
  72. package/src/types/global-private.d.ts +1 -1
  73. package/src/types/internal.d.ts +47 -53
  74. package/src/utils/filesystem.js +1 -23
  75. package/src/utils/hash.js +21 -0
  76. package/src/utils/http.js +8 -7
  77. package/src/utils/import.js +2 -1
  78. package/src/utils/params.js +1 -1
  79. package/src/utils/regex.js +9 -0
  80. package/src/utils/routing.js +52 -27
  81. package/src/utils/url.js +1 -0
  82. package/src/version.js +1 -1
  83. package/types/index.d.ts +98 -28
  84. package/types/index.d.ts.map +1 -1
  85. package/src/exports/node/polyfills.js +0 -30
  86. package/src/runtime/server/app.js +0 -9
@@ -1,6 +1,6 @@
1
1
  import { BROWSER, DEV } from 'esm-env';
2
2
  import { noop } from '../../utils/functions.js';
3
- import { hash } from '../../utils/hash.js';
3
+ import { hash_request } from '../../utils/hash.js';
4
4
  import { base64_decode } from '../utils.js';
5
5
 
6
6
  let loading = 0;
@@ -172,18 +172,7 @@ function build_selector(resource, opts) {
172
172
  return null;
173
173
  }
174
174
 
175
- /** @type {import('types').StrictBody[]} */
176
- const values = [];
177
-
178
- if (opts.headers) {
179
- values.push([...new Headers(opts.headers)].join(','));
180
- }
181
-
182
- if (body) {
183
- values.push(/** @type {import('types').StrictBody} */ (body));
184
- }
185
-
186
- selector += `[data-hash="${hash(...values)}"]`;
175
+ selector += `[data-hash="${hash_request(opts.headers, body)}"]`;
187
176
  }
188
177
 
189
178
  return selector;
@@ -4,7 +4,7 @@ import { exec, parse_route_id } from '../../utils/routing.js';
4
4
  * @param {import('./types.js').SvelteKitApp} app
5
5
  * @returns {import('types').CSRRoute[]}
6
6
  */
7
- export function parse({ nodes, server_loads, dictionary, matchers }) {
7
+ export function parse_routes({ nodes, server_loads, dictionary, matchers }) {
8
8
  const layouts_with_server_load = new Set(server_loads);
9
9
 
10
10
  return Object.entries(dictionary).map(([id, [leaf, layouts, errors]]) => {
@@ -1,6 +1,5 @@
1
1
  /** @import { RemoteCommand, RemoteQueryUpdate } from '@sveltejs/kit' */
2
2
  import { app_dir, base } from '$app/paths/internal/client';
3
- import { app } from '../client.js';
4
3
  import { stringify_command_arg } from '../../shared.js';
5
4
  import { get_remote_request_headers, categorize_updates, remote_request } from './shared.svelte.js';
6
5
 
@@ -48,7 +47,7 @@ export function command(id) {
48
47
  const response = await remote_request(`${base}/${app_dir}/remote/${id}`, {
49
48
  method: 'POST',
50
49
  body: JSON.stringify({
51
- payload: await stringify_command_arg(arg, app.hooks.transport),
50
+ payload: await stringify_command_arg(arg),
52
51
  refreshes: Array.from(refreshes ?? [])
53
52
  }),
54
53
  headers
@@ -262,13 +262,9 @@ export function form(id) {
262
262
 
263
263
  if (response.redirect) {
264
264
  // Use internal version to allow redirects to external URLs
265
- void _goto(
266
- response.redirect,
267
- {
268
- refreshAll: should_refresh
269
- },
270
- 0
271
- );
265
+ void _goto(response.redirect, {
266
+ refreshAll: should_refresh
267
+ });
272
268
  return true;
273
269
  }
274
270
 
@@ -57,7 +57,7 @@ function put(url, encoded) {
57
57
  */
58
58
  export function prerender(id) {
59
59
  return (arg) => {
60
- const payload = stringify_remote_arg(arg, app.hooks.transport);
60
+ const payload = stringify_remote_arg(arg);
61
61
  const cache_key = create_remote_key(id, payload);
62
62
 
63
63
  let resource = prerender_resources.get(cache_key)?.deref();
@@ -100,7 +100,7 @@ export function prerender(id) {
100
100
 
101
101
  if (result.redirect) {
102
102
  // Use internal version to allow redirects to external URLs
103
- void _goto(result.redirect, {}, 0);
103
+ void _goto(result.redirect);
104
104
  return;
105
105
  }
106
106
 
@@ -30,7 +30,7 @@ export function query(id) {
30
30
 
31
31
  if (result.redirect) {
32
32
  // Use internal version to allow redirects to external URLs
33
- await _goto(result.redirect, {}, 0);
33
+ await _goto(result.redirect);
34
34
  }
35
35
  });
36
36
  };
@@ -1,4 +1,4 @@
1
- import { app, query_map } from '../../client.js';
1
+ import { query_map } from '../../client.js';
2
2
  import {
3
3
  pin_in_effect,
4
4
  pin_while_resolving,
@@ -29,7 +29,7 @@ export class QueryProxy {
29
29
  */
30
30
  constructor(id, arg, fn) {
31
31
  this.#id = id;
32
- this.#payload = stringify_remote_arg(arg, app.hooks.transport);
32
+ this.#payload = stringify_remote_arg(arg);
33
33
  this.#key = create_remote_key(id, this.#payload);
34
34
  Object.defineProperty(this, QUERY_RESOURCE_KEY, { value: this.#key });
35
35
  this.#fn = fn;
@@ -50,7 +50,7 @@ export function query_batch(id) {
50
50
 
51
51
  if (response.redirect) {
52
52
  // Use internal version to allow redirects to external URLs
53
- await _goto(response.redirect, {}, 0);
53
+ await _goto(response.redirect);
54
54
 
55
55
  // settle all batched promises (with `undefined`, like a redirect
56
56
  // from a non-batched query) so that callers don't hang forever
@@ -1,4 +1,4 @@
1
- import { app, live_query_map } from '../../client.js';
1
+ import { live_query_map } from '../../client.js';
2
2
  import { pin_in_effect, pin_while_resolving, QUERY_RESOURCE_KEY } from '../shared.svelte.js';
3
3
  import { create_remote_key, stringify_remote_arg } from '../../../shared.js';
4
4
  import { LiveQuery } from './instance.svelte.js';
@@ -20,7 +20,7 @@ export class LiveQueryProxy {
20
20
  */
21
21
  constructor(id, arg) {
22
22
  this.#id = id;
23
- this.#payload = stringify_remote_arg(arg, app.hooks.transport);
23
+ this.#payload = stringify_remote_arg(arg);
24
24
  this.#key = create_remote_key(id, this.#payload);
25
25
  Object.defineProperty(this, QUERY_RESOURCE_KEY, { value: this.#key });
26
26
 
@@ -74,16 +74,6 @@ export class LiveQueryProxy {
74
74
  return this.#get_cached_query().done;
75
75
  }
76
76
 
77
- /**
78
- * @deprecated Use `for await (const value of liveQuery())` instead.
79
- * @returns {AsyncGenerator<T>}
80
- */
81
- run() {
82
- throw new Error(
83
- '`.run()` has been removed from live queries. Use `for await (const value of liveQuery())` instead.'
84
- );
85
- }
86
-
87
77
  reconnect() {
88
78
  return this.#get_cached_query().reconnect();
89
79
  }
@@ -197,7 +197,7 @@ export async function remote_request(url, init) {
197
197
  export async function handle_side_channel_response(response) {
198
198
  if (response.type === 'redirect') {
199
199
  // Use internal version to allow redirects to external URLs
200
- await _goto(response.location, {}, 0);
200
+ await _goto(response.location);
201
201
  throw new Redirect(307, response.location);
202
202
  }
203
203
 
@@ -122,6 +122,7 @@ export function get_link_info(a, base, uses_hash_router) {
122
122
  /** @type {URL | undefined} */
123
123
  let url;
124
124
 
125
+ // TODO replace the try/catch with `URL.parse` when browser support allows (Chrome 126, Firefox 126, Safari 18)
125
126
  try {
126
127
  url = new URL(a instanceof SVGAElement ? a.href.baseVal : a.href, document.baseURI);
127
128
 
@@ -3,12 +3,10 @@
3
3
 
4
4
  import { DEV } from 'esm-env';
5
5
  import * as devalue from 'devalue';
6
- import { text_encoder } from './utils.js';
6
+ import { text_decoder, text_encoder } from './utils.js';
7
7
  import { noop } from '../utils/functions.js';
8
8
  import { SvelteKitError } from '@sveltejs/kit/internal';
9
9
 
10
- const decoder = new TextDecoder();
11
-
12
10
  /**
13
11
  * Sets a parsed form field value in a nested object, mutating the original object.
14
12
  * @param {Record<string, any>} object
@@ -283,7 +281,7 @@ export async function deserialize_binary_form(request, form_id) {
283
281
  const file_offsets_buffer = await get_buffer(HEADER_BYTES + data_length, file_offsets_length);
284
282
  if (!file_offsets_buffer) throw deserialize_error('file offset table too short');
285
283
 
286
- const parsed_offsets = JSON.parse(decoder.decode(file_offsets_buffer));
284
+ const parsed_offsets = JSON.parse(text_decoder.decode(file_offsets_buffer));
287
285
 
288
286
  if (
289
287
  !Array.isArray(parsed_offsets) ||
@@ -298,7 +296,7 @@ export async function deserialize_binary_form(request, form_id) {
298
296
 
299
297
  /** @type {Array<{ offset: number, size: number }>} */
300
298
  const file_spans = [];
301
- const [data, meta] = devalue.parse(decoder.decode(data_buffer), {
299
+ const [data, meta] = devalue.parse(text_decoder.decode(data_buffer), {
302
300
  File: ([name, type, size, last_modified, index]) => {
303
301
  if (
304
302
  typeof name !== 'string' ||
@@ -488,7 +486,7 @@ class LazyFile {
488
486
  });
489
487
  }
490
488
  async text() {
491
- return decoder.decode(await this.arrayBuffer());
489
+ return text_decoder.decode(await this.arrayBuffer());
492
490
  }
493
491
  }
494
492
 
@@ -22,13 +22,14 @@ export function strip_data_suffix(pathname) {
22
22
  }
23
23
 
24
24
  const ROUTE_SUFFIX = '/__route.js';
25
+ const HTML_ROUTE_SUFFIX = '.html__route.js';
25
26
 
26
27
  /**
27
28
  * @param {string} pathname
28
29
  * @returns {boolean}
29
30
  */
30
31
  export function has_resolution_suffix(pathname) {
31
- return pathname.endsWith(ROUTE_SUFFIX);
32
+ return pathname.endsWith(ROUTE_SUFFIX) || pathname.endsWith(HTML_ROUTE_SUFFIX);
32
33
  }
33
34
 
34
35
  /**
@@ -37,6 +38,7 @@ export function has_resolution_suffix(pathname) {
37
38
  * @returns {string}
38
39
  */
39
40
  export function add_resolution_suffix(pathname) {
41
+ if (pathname.endsWith('.html')) return pathname.replace(/\.html$/, HTML_ROUTE_SUFFIX);
40
42
  return pathname.replace(/\/$/, '') + ROUTE_SUFFIX;
41
43
  }
42
44
 
@@ -45,5 +47,46 @@ export function add_resolution_suffix(pathname) {
45
47
  * @returns {string}
46
48
  */
47
49
  export function strip_resolution_suffix(pathname) {
50
+ if (pathname.endsWith(HTML_ROUTE_SUFFIX)) {
51
+ return pathname.slice(0, -HTML_ROUTE_SUFFIX.length) + '.html';
52
+ }
53
+
48
54
  return pathname.slice(0, -ROUTE_SUFFIX.length);
49
55
  }
56
+
57
+ const ROUTES_PREFIX = '/routes';
58
+
59
+ /**
60
+ * The pathname of the route-ID-keyed resolution module for a given route ID,
61
+ * e.g. `/_app/routes/blog/[slug]/__route.js` (before prefixing with `base`).
62
+ * @param {string} app_dir
63
+ * @param {string} route_id
64
+ * @returns {string}
65
+ */
66
+ export function route_id_resolution_pathname(app_dir, route_id) {
67
+ return add_resolution_suffix(`/${app_dir}${ROUTES_PREFIX}${route_id === '/' ? '' : route_id}`);
68
+ }
69
+
70
+ /**
71
+ * Whether a pathname (with the `/__route.js` suffix already stripped, and `base` NOT yet stripped)
72
+ * is a route-ID resolution request rather than a pathname resolution request.
73
+ * @param {string} pathname
74
+ * @param {string} base
75
+ * @param {string} app_dir
76
+ * @returns {boolean}
77
+ */
78
+ export function is_route_id_resolution_path(pathname, base, app_dir) {
79
+ const prefix = `${base}/${app_dir}${ROUTES_PREFIX}`;
80
+ return pathname === prefix || pathname.startsWith(prefix + '/');
81
+ }
82
+
83
+ /**
84
+ * Extract the route ID from a decoded, base-stripped, suffix-stripped pathname,
85
+ * e.g. `/_app/routes/blog/[slug]` -> `/blog/[slug]`, `/_app/routes` -> `/`.
86
+ * @param {string} pathname
87
+ * @param {string} app_dir
88
+ * @returns {string}
89
+ */
90
+ export function extract_route_id(pathname, app_dir) {
91
+ return pathname.slice(`/${app_dir}${ROUTES_PREFIX}`.length) || '/';
92
+ }
@@ -11,22 +11,20 @@ import { with_version_header } from '../utils.js';
11
11
 
12
12
  /**
13
13
  * @param {import('@sveltejs/kit').RequestEvent} event
14
- * @param {import('types').RequestState} event_state
14
+ * @param {import('types').RequestState} state
15
15
  * @param {import('types').SSRRoute} route
16
16
  * @param {import('types').SSROptions} options
17
17
  * @param {import('@sveltejs/kit').SSRManifest} manifest
18
- * @param {import('types').SSRState} state
19
18
  * @param {boolean[] | undefined} invalidated_data_nodes
20
19
  * @param {import('types').TrailingSlash} trailing_slash
21
20
  * @returns {Promise<Response>}
22
21
  */
23
22
  export async function render_data(
24
23
  event,
25
- event_state,
24
+ state,
26
25
  route,
27
26
  options,
28
27
  manifest,
29
- state,
30
28
  invalidated_data_nodes,
31
29
  trailing_slash
32
30
  ) {
@@ -60,7 +58,6 @@ export async function render_data(
60
58
  // load this. for the child, return as is. for the final result, stream things
61
59
  return load_server_data({
62
60
  event: new_event,
63
- event_state,
64
61
  state,
65
62
  node,
66
63
  parent: async () => {
@@ -95,7 +92,7 @@ export async function render_data(
95
92
  return fn();
96
93
  });
97
94
 
98
- const data_serializer = server_data_serializer_json(event, event_state, options);
95
+ const data_serializer = server_data_serializer_json(event, state, options);
99
96
  await Promise.all(
100
97
  promises.map(async (p, i) => {
101
98
  const node = await p.catch(async (error) => {
@@ -103,7 +100,7 @@ export async function render_data(
103
100
  throw error;
104
101
  }
105
102
 
106
- const transformed = await handle_error_and_jsonify(event, event_state, options, error);
103
+ const transformed = await handle_error_and_jsonify(event, state, options, error);
107
104
 
108
105
  return /** @type {import('types').ServerErrorNode} */ ({
109
106
  type: 'error',
@@ -151,7 +148,7 @@ export async function render_data(
151
148
  if (error instanceof Redirect) {
152
149
  return redirect_json_response(error);
153
150
  } else {
154
- const transformed = await handle_error_and_jsonify(event, event_state, options, error);
151
+ const transformed = await handle_error_and_jsonify(event, state, options, error);
155
152
  return json_response(transformed, transformed.status);
156
153
  }
157
154
  }
@@ -0,0 +1,22 @@
1
+ const symbol = Symbol.for('sveltekit.global_state');
2
+ const state = /** @type {Record<symbol, any>} */ (
3
+ /** @type {Record<symbol, unknown>} */ (/** @type {unknown} */ (globalThis))[symbol] ??= {}
4
+ );
5
+
6
+ /**
7
+ * Persist a value across dev server reloads, in case a freshly-invalidated module
8
+ * reads global state that is not set until a request is handled
9
+ * @param {symbol} key
10
+ * @param {any} value
11
+ */
12
+ export function save(key, value) {
13
+ state[key] = value;
14
+ }
15
+
16
+ /**
17
+ * Restore a value after a dev server reload
18
+ * @param {symbol} key
19
+ */
20
+ export function restore(key) {
21
+ return state[key];
22
+ }
@@ -6,12 +6,11 @@ import { method_not_allowed } from './utils.js';
6
6
 
7
7
  /**
8
8
  * @param {import('@sveltejs/kit').RequestEvent} event
9
- * @param {import('types').RequestState} event_state
9
+ * @param {import('types').RequestState} state
10
10
  * @param {import('types').SSREndpoint} mod
11
- * @param {import('types').SSRState} state
12
11
  * @returns {Promise<Response>}
13
12
  */
14
- export async function render_endpoint(event, event_state, mod, state) {
13
+ export async function render_endpoint(event, state, mod) {
15
14
  const method = /** @type {import('types').HttpMethod} */ (event.request.method);
16
15
 
17
16
  let handler = mod[method] || mod.fallback;
@@ -42,7 +41,7 @@ export async function render_endpoint(event, event_state, mod, state) {
42
41
  }
43
42
 
44
43
  try {
45
- const response = await with_request_store({ event, state: event_state }, () =>
44
+ const response = await with_request_store({ event, state }, () =>
46
45
  handler(/** @type {import('@sveltejs/kit').RequestEvent<Record<string, any>>} */ (event))
47
46
  );
48
47
 
@@ -4,13 +4,14 @@ import { respond } from './respond.js';
4
4
  import * as paths from '$app/paths/internal/server';
5
5
  import { read_implementation } from './internal.js';
6
6
  import { has_prerendered_path } from './utils.js';
7
+ import { fork_state_for_subrequest } from './state.js';
7
8
 
8
9
  /**
9
10
  * @param {{
10
11
  * event: import('@sveltejs/kit').RequestEvent;
11
12
  * options: import('types').SSROptions;
12
13
  * manifest: import('@sveltejs/kit').SSRManifest;
13
- * state: import('types').SSRState;
14
+ * state: import('types').RequestState;
14
15
  * get_cookie_header: (url: URL, header: string | null) => string;
15
16
  * set_internal: (name: string, value: string, opts: import('./page/types.js').Cookie['options']) => void;
16
17
  * }} opts
@@ -194,38 +195,32 @@ function normalize_fetch_input(info, init, url) {
194
195
  * @param {Request} request
195
196
  * @param {import('types').SSROptions} options
196
197
  * @param {import('@sveltejs/kit').SSRManifest} manifest
197
- * @param {import('types').SSRState} state
198
+ * @param {import('types').RequestState} state
198
199
  * @returns {Promise<Response>}
199
200
  */
200
201
  async function internal_fetch(request, options, manifest, state) {
201
- if (request.signal) {
202
- if (request.signal.aborted) {
203
- throw new DOMException('The operation was aborted.', 'AbortError');
204
- }
205
-
206
- let remove_abort_listener = noop;
207
- /** @type {Promise<never>} */
208
- const abort_promise = new Promise((_, reject) => {
209
- const on_abort = () => {
210
- reject(new DOMException('The operation was aborted.', 'AbortError'));
211
- };
212
- request.signal.addEventListener('abort', on_abort, { once: true });
213
- remove_abort_listener = () => request.signal.removeEventListener('abort', on_abort);
214
- });
202
+ if (request.signal?.aborted) {
203
+ throw new DOMException('The operation was aborted.', 'AbortError');
204
+ }
215
205
 
216
- const result = await Promise.race([
217
- respond(request, options, manifest, {
218
- ...state,
219
- depth: state.depth + 1
220
- }),
221
- abort_promise
222
- ]);
223
- remove_abort_listener();
224
- return result;
225
- } else {
226
- return await respond(request, options, manifest, {
227
- ...state,
228
- depth: state.depth + 1
229
- });
206
+ const subrequest_state = fork_state_for_subrequest(state);
207
+
208
+ if (!request.signal) {
209
+ return await respond(request, options, manifest, subrequest_state);
230
210
  }
211
+
212
+ let remove_abort_listener = noop;
213
+ /** @type {Promise<never>} */
214
+ const abort_promise = new Promise((_, reject) => {
215
+ const on_abort = () => {
216
+ reject(new DOMException('The operation was aborted.', 'AbortError'));
217
+ };
218
+ request.signal.addEventListener('abort', on_abort, { once: true });
219
+ remove_abort_listener = () => request.signal.removeEventListener('abort', on_abort);
220
+ });
221
+
222
+ return Promise.race([
223
+ respond(request, options, manifest, subrequest_state),
224
+ abort_promise
225
+ ]).finally(remove_abort_listener);
231
226
  }
@@ -1,11 +1,13 @@
1
1
  import { noop } from '../../utils/functions.js';
2
2
  import { IN_WEBCONTAINER } from './constants.js';
3
3
  import { respond } from './respond.js';
4
+ import { create_request_state } from './state.js';
4
5
  import { options, get_hooks } from '__SERVER__/internal.js';
5
- import { set_read_implementation, set_manifest } from './internal.js';
6
+ import { set_read_implementation, set_manifest, fix_stack_trace } from './internal.js';
6
7
  import { set_env } from '__sveltekit/env';
7
- import { set_app } from './app.js';
8
8
  import { SvelteKitError } from '@sveltejs/kit/internal';
9
+ import { DEV } from 'esm-env';
10
+ import { init_transport } from '#app/internal/transport';
9
11
 
10
12
  /** @type {Promise<any>} */
11
13
  let init_promise;
@@ -13,6 +15,38 @@ let init_promise;
13
15
  /** @type {Promise<void> | null} */
14
16
  let current = null;
15
17
 
18
+ /**
19
+ * Responses that were created with our monkey-patched `fetch`, which may need
20
+ * to have their `content-encoding` and `content-length` headers removed
21
+ * if returned directly (i.e. `fetch` is being used to proxy a request)
22
+ * @type {WeakMap<Response, Error>}
23
+ */
24
+ const decoded_responses = new WeakMap();
25
+
26
+ if (DEV) {
27
+ const fetch = globalThis.fetch;
28
+
29
+ /**
30
+ * @param {RequestInfo | URL} info
31
+ * @param {RequestInit} [init]
32
+ */
33
+ globalThis.fetch = async (info, init) => {
34
+ const response = await fetch(info, init);
35
+ const encoding = response.headers.get('content-encoding');
36
+
37
+ if (encoding) {
38
+ decoded_responses.set(
39
+ response,
40
+ new Error(
41
+ `Cannot return \`fetch(...)\` directly from a handler if the response has a \`Content-Encoding: ${encoding}\` header. The body has already been decoded`
42
+ )
43
+ );
44
+ }
45
+
46
+ return response;
47
+ };
48
+ }
49
+
16
50
  export class Server {
17
51
  /** @type {import('types').SSROptions} */
18
52
  #options;
@@ -67,31 +101,17 @@ export class Server {
67
101
  const result = read(file);
68
102
  if (result instanceof ReadableStream) {
69
103
  return result;
70
- } else {
71
- return new ReadableStream({
72
- async start(controller) {
73
- try {
74
- const stream = await Promise.resolve(result);
75
- if (!stream) {
76
- controller.close();
77
- return;
78
- }
79
-
80
- const reader = stream.getReader();
81
-
82
- while (true) {
83
- const { done, value } = await reader.read();
84
- if (done) break;
85
- controller.enqueue(value);
86
- }
87
-
88
- controller.close();
89
- } catch (error) {
90
- controller.error(error);
91
- }
92
- }
93
- });
94
104
  }
105
+
106
+ // TODO remove the cast once TypeScript's lib includes `ReadableStream.from`
107
+ return /** @type {ReadableStream} */ (
108
+ /** @type {any} */ (ReadableStream).from(
109
+ (async function* () {
110
+ const stream = await result;
111
+ if (stream) yield* stream;
112
+ })()
113
+ )
114
+ );
95
115
  };
96
116
 
97
117
  set_read_implementation(wrapped_read);
@@ -132,15 +152,10 @@ export class Server {
132
152
  console.error('Remote function schema validation failed:', issues);
133
153
  return { message: 'Bad Request', status: 400 };
134
154
  }),
135
- reroute: module.reroute || noop,
136
- transport: module.transport || {}
155
+ reroute: module.reroute || noop
137
156
  };
138
157
 
139
- set_app({
140
- decoders: module.transport
141
- ? Object.fromEntries(Object.entries(module.transport).map(([k, v]) => [k, v.decode]))
142
- : {}
143
- });
158
+ init_transport(module.transport ?? {});
144
159
 
145
160
  if (module.init) {
146
161
  await module.init();
@@ -156,13 +171,8 @@ export class Server {
156
171
  handleValidationError: () => {
157
172
  return { message: 'Bad Request' };
158
173
  },
159
- reroute: noop,
160
- transport: {}
174
+ reroute: noop
161
175
  };
162
-
163
- set_app({
164
- decoders: {}
165
- });
166
176
  } else {
167
177
  throw e;
168
178
  }
@@ -172,13 +182,21 @@ export class Server {
172
182
 
173
183
  /**
174
184
  * @param {Request} request
175
- * @param {import('types').RequestOptions} options
185
+ * @param {import('types').InternalRequestOptions} options
176
186
  */
177
- respond(request, options) {
178
- return respond(request, this.#options, this.#manifest, {
179
- ...options,
180
- error: false,
181
- depth: 0
182
- });
187
+ async respond(request, options) {
188
+ const response = await respond(
189
+ request,
190
+ this.#options,
191
+ this.#manifest,
192
+ create_request_state(options, this.#options.hooks)
193
+ );
194
+
195
+ if (DEV) {
196
+ const error = decoded_responses.get(response);
197
+ if (error) console.error(fix_stack_trace(error));
198
+ }
199
+
200
+ return response;
183
201
  }
184
202
  }
@@ -1,17 +1,23 @@
1
1
  /** @import { SSRManifest } from '@sveltejs/kit'; */
2
+ import { restore, save } from './dev.js';
2
3
 
3
- /**
4
- * @type {((path: string) => ReadableStream<any>) | null}
5
- */
6
- export let read_implementation = null;
4
+ const read_implementation_key = Symbol.for('sveltekit.read_implementation');
5
+ const manifest_key = Symbol.for('sveltekit.manifest');
6
+
7
+ export let read_implementation = /** @type {((path: string) => ReadableStream<any>) | null} */ (
8
+ (__SVELTEKIT_DEV__ && restore(read_implementation_key)) ?? null
9
+ );
7
10
 
8
- export let manifest = /** @type {SSRManifest} */ (/** @type {unknown} */ (null));
11
+ export let manifest = /** @type {SSRManifest} */ (
12
+ (__SVELTEKIT_DEV__ && restore(manifest_key)) ?? null
13
+ );
9
14
 
10
15
  /**
11
16
  * @param {(path: string) => ReadableStream<any>} fn
12
17
  */
13
18
  export function set_read_implementation(fn) {
14
19
  read_implementation = fn;
20
+ if (__SVELTEKIT_DEV__) save(read_implementation_key, fn);
15
21
  }
16
22
 
17
23
  /**
@@ -20,6 +26,7 @@ export function set_read_implementation(fn) {
20
26
  */
21
27
  export function set_manifest(value) {
22
28
  manifest = value;
29
+ if (__SVELTEKIT_DEV__) save(manifest_key, value);
23
30
  }
24
31
 
25
32
  export { fix_stack_trace, set_fix_stack_trace } from './sourcemaps.js';