@sveltejs/kit 3.0.0-next.12 → 3.0.0-next.14

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 (77) hide show
  1. package/package.json +11 -11
  2. package/src/cli.js +2 -2
  3. package/src/core/adapt/builder.js +8 -5
  4. package/src/core/generate_manifest/index.js +6 -0
  5. package/src/core/postbuild/entities.js +8 -2
  6. package/src/core/postbuild/fallback.js +2 -1
  7. package/src/core/postbuild/prerender.js +15 -6
  8. package/src/core/sync/create_manifest_data/conflict.js +1 -1
  9. package/src/core/sync/create_manifest_data/index.js +33 -2
  10. package/src/core/sync/write_app_types.js +73 -29
  11. package/src/core/sync/write_tsconfig/index.js +15 -2
  12. package/src/core/sync/write_types/index.js +5 -2
  13. package/src/exports/index.js +23 -12
  14. package/src/exports/internal/shared.js +4 -12
  15. package/src/exports/node/index.js +2 -7
  16. package/src/exports/public.d.ts +61 -12
  17. package/src/exports/vite/build/remote.js +10 -12
  18. package/src/exports/vite/dev/index.js +32 -19
  19. package/src/exports/vite/index.js +162 -133
  20. package/src/exports/vite/utils.js +1 -7
  21. package/src/runtime/app/server/remote/form.js +9 -1
  22. package/src/runtime/app/server/remote/prerender.js +13 -9
  23. package/src/runtime/app/server/remote/query.js +0 -6
  24. package/src/runtime/app/server/remote/requested.js +4 -4
  25. package/src/runtime/app/state/client.js +3 -0
  26. package/src/runtime/app/state/index.js +2 -2
  27. package/src/runtime/app/state/server.js +3 -0
  28. package/src/runtime/client/client.js +731 -342
  29. package/src/runtime/client/constants.js +2 -6
  30. package/src/runtime/client/fetcher.js +24 -25
  31. package/src/runtime/client/remote-functions/form.svelte.js +42 -14
  32. package/src/runtime/client/remote-functions/prerender.svelte.js +2 -2
  33. package/src/runtime/client/remote-functions/query/index.js +1 -1
  34. package/src/runtime/client/remote-functions/query/instance.svelte.js +2 -2
  35. package/src/runtime/client/remote-functions/query-batch.svelte.js +2 -2
  36. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +2 -2
  37. package/src/runtime/client/remote-functions/query-live/iterator.js +10 -8
  38. package/src/runtime/client/remote-functions/query-live/proxy.js +0 -10
  39. package/src/runtime/client/remote-functions/shared.svelte.js +12 -11
  40. package/src/runtime/client/state.svelte.js +10 -22
  41. package/src/runtime/client/types.d.ts +1 -2
  42. package/src/runtime/client/utils.js +22 -14
  43. package/src/runtime/components/root.svelte +4 -14
  44. package/src/runtime/form-utils.js +4 -6
  45. package/src/runtime/pathname.js +37 -0
  46. package/src/runtime/props.svelte.js +71 -0
  47. package/src/runtime/server/fetch.js +9 -14
  48. package/src/runtime/server/index.js +10 -24
  49. package/src/runtime/server/page/crypto.js +2 -2
  50. package/src/runtime/server/page/csp.js +88 -104
  51. package/src/runtime/server/page/index.js +1 -2
  52. package/src/runtime/server/page/load_data.js +16 -33
  53. package/src/runtime/server/page/render.js +46 -25
  54. package/src/runtime/server/page/respond_with_error.js +1 -3
  55. package/src/runtime/server/page/serialize_data.js +2 -13
  56. package/src/runtime/server/page/server_routing.js +58 -9
  57. package/src/runtime/server/respond.js +20 -29
  58. package/src/runtime/server/state.js +43 -0
  59. package/src/runtime/shared.js +4 -2
  60. package/src/runtime/utils.js +3 -0
  61. package/src/types/ambient-private.d.ts +2 -2
  62. package/src/types/ambient.d.ts +59 -8
  63. package/src/types/global-private.d.ts +1 -1
  64. package/src/types/internal.d.ts +4 -8
  65. package/src/types/private.d.ts +3 -12
  66. package/src/utils/hash.js +21 -0
  67. package/src/utils/http.js +8 -7
  68. package/src/utils/import.js +2 -1
  69. package/src/utils/params.js +1 -1
  70. package/src/utils/regex.js +9 -0
  71. package/src/utils/routing.js +52 -27
  72. package/src/utils/url.js +1 -0
  73. package/src/version.js +1 -1
  74. package/types/index.d.ts +165 -66
  75. package/types/index.d.ts.map +2 -1
  76. package/src/exports/node/polyfills.js +0 -30
  77. package/src/runtime/types.d.ts +0 -8
@@ -47,3 +47,40 @@ export function add_resolution_suffix(pathname) {
47
47
  export function strip_resolution_suffix(pathname) {
48
48
  return pathname.slice(0, -ROUTE_SUFFIX.length);
49
49
  }
50
+
51
+ const ROUTES_PREFIX = '/routes';
52
+
53
+ /**
54
+ * The pathname of the route-ID-keyed resolution module for a given route ID,
55
+ * e.g. `/_app/routes/blog/[slug]/__route.js` (before prefixing with `base`).
56
+ * @param {string} app_dir
57
+ * @param {string} route_id
58
+ * @returns {string}
59
+ */
60
+ export function route_id_resolution_pathname(app_dir, route_id) {
61
+ return add_resolution_suffix(`/${app_dir}${ROUTES_PREFIX}${route_id === '/' ? '' : route_id}`);
62
+ }
63
+
64
+ /**
65
+ * Whether a pathname (with the `/__route.js` suffix already stripped, and `base` NOT yet stripped)
66
+ * is a route-ID resolution request rather than a pathname resolution request.
67
+ * @param {string} pathname
68
+ * @param {string} base
69
+ * @param {string} app_dir
70
+ * @returns {boolean}
71
+ */
72
+ export function is_route_id_resolution_path(pathname, base, app_dir) {
73
+ const prefix = `${base}/${app_dir}${ROUTES_PREFIX}`;
74
+ return pathname === prefix || pathname.startsWith(prefix + '/');
75
+ }
76
+
77
+ /**
78
+ * Extract the route ID from a decoded, base-stripped, suffix-stripped pathname,
79
+ * e.g. `/_app/routes/blog/[slug]` -> `/blog/[slug]`, `/_app/routes` -> `/`.
80
+ * @param {string} pathname
81
+ * @param {string} app_dir
82
+ * @returns {string}
83
+ */
84
+ export function extract_route_id(pathname, app_dir) {
85
+ return pathname.slice(`/${app_dir}${ROUTES_PREFIX}`.length) || '/';
86
+ }
@@ -0,0 +1,71 @@
1
+ /** @import { Component } from 'svelte'; */
2
+ /** @import { Page } from '@sveltejs/kit'; */
3
+
4
+ import { noop } from '../utils/functions.js';
5
+
6
+ export class Props {
7
+ /** @type {Page} */
8
+ page;
9
+
10
+ /**
11
+ * An array of the `+layout.svelte` and `+page.svelte` component instances
12
+ * that currently live on the page — used for capturing and restoring snapshots.
13
+ * It's updated/manipulated through `bind:this` in `Root.svelte`.
14
+ * @type {Array<Record<string, any>>}
15
+ */
16
+ components = [];
17
+
18
+ /** @type {any} */
19
+ form;
20
+
21
+ /** @type {App.Error | undefined} */
22
+ error;
23
+
24
+ /** @type {RenderNode} */
25
+ tree;
26
+
27
+ /** @type {(error: unknown, reset: () => void) => void} */
28
+ onerror;
29
+
30
+ /**
31
+ * @param {{
32
+ * page: Page;
33
+ * tree: RenderNode;
34
+ * form: any;
35
+ * error: App.Error | undefined;
36
+ * onerror?: (error: unknown, reset: () => void) => void;
37
+ * }} props
38
+ */
39
+ constructor({ page, tree, form, error, onerror = noop }) {
40
+ this.page = page;
41
+ this.tree = tree;
42
+ this.onerror = onerror;
43
+
44
+ this.form = $state.raw(form);
45
+ this.error = $state.raw(error);
46
+ }
47
+ }
48
+
49
+ export class RenderNode {
50
+ /** @type {Component} */
51
+ component;
52
+
53
+ /** @type {Component | undefined} */
54
+ error;
55
+
56
+ /** @type {Record<string, any>} */
57
+ data = $state.raw({});
58
+
59
+ /** @type {RenderNode | undefined} */
60
+ child = $state.raw();
61
+
62
+ /**
63
+ *
64
+ * @param {Component} component
65
+ * @param {Component} error
66
+ */
67
+ constructor(component, error) {
68
+ this.component = component;
69
+ this.error = error;
70
+ }
71
+ }
@@ -4,6 +4,7 @@ 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 {{
@@ -117,7 +118,7 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade
117
118
  return await fetch(request);
118
119
  }
119
120
 
120
- if (has_prerendered_path(manifest, paths.base + decoded)) {
121
+ if (has_prerendered_path(manifest, decoded)) {
121
122
  // The path of something prerendered could match a different route
122
123
  // that is still in the manifest, leading to the wrong route being loaded.
123
124
  // We therefore bail early here. The prerendered logic is different for
@@ -142,11 +143,9 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade
142
143
  request.headers.set('accept', '*/*');
143
144
  }
144
145
 
145
- if (!request.headers.has('accept-language')) {
146
- request.headers.set(
147
- 'accept-language',
148
- /** @type {string} */ (event.request.headers.get('accept-language'))
149
- );
146
+ const accept_language = event.request.headers.get('accept-language');
147
+ if (accept_language && !request.headers.has('accept-language')) {
148
+ request.headers.set('accept-language', accept_language);
150
149
  }
151
150
 
152
151
  const response = await internal_fetch(request, options, manifest, state);
@@ -200,6 +199,8 @@ function normalize_fetch_input(info, init, url) {
200
199
  * @returns {Promise<Response>}
201
200
  */
202
201
  async function internal_fetch(request, options, manifest, state) {
202
+ const subrequest_state = fork_state_for_subrequest(state);
203
+
203
204
  if (request.signal) {
204
205
  if (request.signal.aborted) {
205
206
  throw new DOMException('The operation was aborted.', 'AbortError');
@@ -216,18 +217,12 @@ async function internal_fetch(request, options, manifest, state) {
216
217
  });
217
218
 
218
219
  const result = await Promise.race([
219
- respond(request, options, manifest, {
220
- ...state,
221
- depth: state.depth + 1
222
- }),
220
+ respond(request, options, manifest, subrequest_state),
223
221
  abort_promise
224
222
  ]);
225
223
  remove_abort_listener();
226
224
  return result;
227
225
  } else {
228
- return await respond(request, options, manifest, {
229
- ...state,
230
- depth: state.depth + 1
231
- });
226
+ return await respond(request, options, manifest, subrequest_state);
232
227
  }
233
228
  }
@@ -67,31 +67,17 @@ export class Server {
67
67
  const result = read(file);
68
68
  if (result instanceof ReadableStream) {
69
69
  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
70
  }
71
+
72
+ // TODO remove the cast once TypeScript's lib includes `ReadableStream.from`
73
+ return /** @type {ReadableStream} */ (
74
+ /** @type {any} */ (ReadableStream).from(
75
+ (async function* () {
76
+ const stream = await result;
77
+ if (stream) yield* stream;
78
+ })()
79
+ )
80
+ );
95
81
  };
96
82
 
97
83
  set_read_implementation(wrapped_read);
@@ -1,4 +1,4 @@
1
- import { text_encoder } from '../../utils.js';
1
+ import { base64_encode, text_encoder } from '../../utils.js';
2
2
 
3
3
  /**
4
4
  * SHA-256 hashing function adapted from https://bitwiseshiftleft.github.io/sjcl
@@ -102,7 +102,7 @@ export function sha256(data) {
102
102
  const bytes = new Uint8Array(out.buffer);
103
103
  reverse_endianness(bytes);
104
104
 
105
- return btoa(String.fromCharCode(...bytes));
105
+ return base64_encode(bytes);
106
106
  }
107
107
 
108
108
  /** The SHA-256 initialization vector */
@@ -1,11 +1,12 @@
1
1
  import { escape_html } from '../../../utils/escape.js';
2
+ import { base64_encode } from '../../utils.js';
2
3
  import { sha256 } from './crypto.js';
3
4
 
4
5
  const array = new Uint8Array(16);
5
6
 
6
7
  function generate_nonce() {
7
8
  crypto.getRandomValues(array);
8
- return btoa(String.fromCharCode(...array));
9
+ return base64_encode(array);
9
10
  }
10
11
 
11
12
  const quoted = new Set([
@@ -53,19 +54,19 @@ class BaseProvider {
53
54
  #directives;
54
55
 
55
56
  /** @type {Set<import('types').Csp.Source>} */
56
- #script_src;
57
+ #script_src = new Set();
57
58
 
58
59
  /** @type {Set<import('types').Csp.Source>} */
59
- #script_src_elem;
60
+ #script_src_elem = new Set();
60
61
 
61
62
  /** @type {Set<import('types').Csp.Source>} */
62
- #style_src;
63
+ #style_src = new Set();
63
64
 
64
65
  /** @type {Set<import('types').Csp.Source>} */
65
- #style_src_attr;
66
+ #style_src_attr = new Set();
66
67
 
67
68
  /** @type {Set<import('types').Csp.Source>} */
68
- #style_src_elem;
69
+ #style_src_elem = new Set();
69
70
 
70
71
  /** @type {boolean} */
71
72
  script_needs_nonce;
@@ -90,12 +91,6 @@ class BaseProvider {
90
91
 
91
92
  const d = this.#directives;
92
93
 
93
- this.#script_src = new Set();
94
- this.#script_src_elem = new Set();
95
- this.#style_src = new Set();
96
- this.#style_src_attr = new Set();
97
- this.#style_src_elem = new Set();
98
-
99
94
  const effective_script_src = d['script-src'] || d['default-src'];
100
95
  const script_src_elem = d['script-src-elem'];
101
96
  const effective_style_src = d['style-src'] || d['default-src'];
@@ -117,32 +112,25 @@ class BaseProvider {
117
112
 
118
113
  // ...and add unsafe-inline so we can inject <style> elements
119
114
  // Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list, so we remove those during dev when injecting unsafe-inline
120
- if (effective_style_src && !effective_style_src.includes('unsafe-inline')) {
121
- d['style-src'] = [
122
- ...effective_style_src.filter(
123
- (value) => !(value.startsWith('sha256-') || value.startsWith('nonce-'))
124
- ),
125
- 'unsafe-inline'
126
- ];
127
- }
128
-
129
- if (style_src_attr && !style_src_attr.includes('unsafe-inline')) {
130
- d['style-src-attr'] = [
131
- ...style_src_attr.filter(
132
- (value) => !(value.startsWith('sha256-') || value.startsWith('nonce-'))
133
- ),
134
- 'unsafe-inline'
135
- ];
136
- }
137
-
138
- if (style_src_elem && !style_src_elem.includes('unsafe-inline')) {
139
- d['style-src-elem'] = [
140
- ...style_src_elem.filter(
141
- (value) => !(value.startsWith('sha256-') || value.startsWith('nonce-'))
142
- ),
143
- 'unsafe-inline'
144
- ];
145
- }
115
+ /**
116
+ * @template {'style-src' | 'style-src-attr' | 'style-src-elem'} K
117
+ * @param {K} key
118
+ * @param {import('types').CspDirectives[K]} directive
119
+ */
120
+ const add_unsafe_inline = (key, directive) => {
121
+ if (directive && !directive.includes('unsafe-inline')) {
122
+ d[key] = /** @type {import('types').CspDirectives[K]} */ ([
123
+ ...directive.filter(
124
+ (value) => !(value.startsWith('sha256-') || value.startsWith('nonce-'))
125
+ ),
126
+ 'unsafe-inline'
127
+ ]);
128
+ }
129
+ };
130
+
131
+ add_unsafe_inline('style-src', effective_style_src);
132
+ add_unsafe_inline('style-src-attr', style_src_attr);
133
+ add_unsafe_inline('style-src-elem', style_src_elem);
146
134
  }
147
135
 
148
136
  /** @param {(import('types').Csp.Source | import('types').Csp.ActionSource)[] | undefined} directive */
@@ -175,13 +163,16 @@ class BaseProvider {
175
163
  this.#nonce = nonce;
176
164
  }
177
165
 
178
- /** @param {string} content */
179
- add_script(content) {
180
- if (!this.#script_needs_csp) return;
181
-
182
- /** @type {`nonce-${string}` | `sha256-${string}`} */
183
- const source = this.#use_hashes ? `sha256-${sha256(content)}` : `nonce-${this.#nonce}`;
166
+ /**
167
+ * @param {string} content
168
+ * @returns {`nonce-${string}` | `sha256-${string}`}
169
+ */
170
+ #get_source(content) {
171
+ return this.#use_hashes ? `sha256-${sha256(content)}` : `nonce-${this.#nonce}`;
172
+ }
184
173
 
174
+ /** @param {`nonce-${string}` | `sha256-${string}`} source */
175
+ #add_script_source(source) {
185
176
  if (this.#script_src_needs_csp) {
186
177
  this.#script_src.add(source);
187
178
  }
@@ -191,15 +182,17 @@ class BaseProvider {
191
182
  }
192
183
  }
193
184
 
185
+ /** @param {string} content */
186
+ add_script(content) {
187
+ if (!this.#script_needs_csp) return;
188
+
189
+ this.#add_script_source(this.#get_source(content));
190
+ }
191
+
194
192
  /** @param {`sha256-${string}`[]} hashes */
195
193
  add_script_hashes(hashes) {
196
194
  for (const hash of hashes) {
197
- if (this.#script_src_needs_csp) {
198
- this.#script_src.add(hash);
199
- }
200
- if (this.#script_src_elem_needs_csp) {
201
- this.#script_src_elem.add(hash);
202
- }
195
+ this.#add_script_source(hash);
203
196
  }
204
197
  }
205
198
 
@@ -207,8 +200,7 @@ class BaseProvider {
207
200
  add_style(content) {
208
201
  if (!this.#style_needs_csp) return;
209
202
 
210
- /** @type {`nonce-${string}` | `sha256-${string}`} */
211
- const source = this.#use_hashes ? `sha256-${sha256(content)}` : `nonce-${this.#nonce}`;
203
+ const source = this.#get_source(content);
212
204
 
213
205
  if (this.#style_src_needs_csp) {
214
206
  this.#style_src.add(source);
@@ -251,40 +243,34 @@ class BaseProvider {
251
243
 
252
244
  const directives = { ...this.#directives };
253
245
 
254
- if (this.#style_src.size > 0) {
255
- directives['style-src'] = [
256
- ...(directives['style-src'] || directives['default-src'] || []),
257
- ...this.#style_src
258
- ];
259
- }
260
-
261
- if (this.#style_src_attr.size > 0) {
262
- directives['style-src-attr'] = [
263
- ...(directives['style-src-attr'] || []),
264
- ...this.#style_src_attr
265
- ];
266
- }
267
-
268
- if (this.#style_src_elem.size > 0) {
269
- directives['style-src-elem'] = [
270
- ...(directives['style-src-elem'] || []),
271
- ...this.#style_src_elem
272
- ];
273
- }
274
-
275
- if (this.#script_src.size > 0) {
276
- directives['script-src'] = [
277
- ...(directives['script-src'] || directives['default-src'] || []),
278
- ...this.#script_src
279
- ];
280
- }
281
-
282
- if (this.#script_src_elem.size > 0) {
283
- directives['script-src-elem'] = [
284
- ...(directives['script-src-elem'] || []),
285
- ...this.#script_src_elem
286
- ];
287
- }
246
+ /**
247
+ * @template {'style-src' | 'style-src-attr' | 'style-src-elem' | 'script-src' | 'script-src-elem'} K
248
+ * @param {K} key
249
+ * @param {Set<import('types').Csp.Source>} sources
250
+ * @param {import('types').CspDirectives[K]} [base]
251
+ */
252
+ const merge_sources = (key, sources, base) => {
253
+ if (sources.size > 0) {
254
+ directives[key] = /** @type {import('types').CspDirectives[K]} */ ([
255
+ ...(base || []),
256
+ ...sources
257
+ ]);
258
+ }
259
+ };
260
+
261
+ merge_sources(
262
+ 'style-src',
263
+ this.#style_src,
264
+ directives['style-src'] || directives['default-src']
265
+ );
266
+ merge_sources('style-src-attr', this.#style_src_attr, directives['style-src-attr']);
267
+ merge_sources('style-src-elem', this.#style_src_elem, directives['style-src-elem']);
268
+ merge_sources(
269
+ 'script-src',
270
+ this.#script_src,
271
+ directives['script-src'] || directives['default-src']
272
+ );
273
+ merge_sources('script-src-elem', this.#script_src_elem, directives['script-src-elem']);
288
274
 
289
275
  for (const key in directives) {
290
276
  if (is_meta && (key === 'frame-ancestors' || key === 'report-uri' || key === 'sandbox')) {
@@ -300,13 +286,11 @@ class BaseProvider {
300
286
 
301
287
  const directive = [key];
302
288
  if (Array.isArray(value)) {
303
- value.forEach((value) => {
304
- if (quoted.has(value) || crypto_pattern.test(value)) {
305
- directive.push(`'${value}'`);
306
- } else {
307
- directive.push(value);
308
- }
309
- });
289
+ for (const source of value) {
290
+ directive.push(
291
+ quoted.has(source) || crypto_pattern.test(source) ? `'${source}'` : source
292
+ );
293
+ }
310
294
  }
311
295
 
312
296
  header.push(directive.join(' '));
@@ -337,17 +321,17 @@ class CspReportOnlyProvider extends BaseProvider {
337
321
  constructor(use_hashes, directives, nonce) {
338
322
  super(use_hashes, directives, nonce);
339
323
 
340
- if (Object.values(directives).filter((v) => !!v).length > 0) {
341
- // If we're generating content-security-policy-report-only,
342
- // if there are any directives, we need a report-uri or report-to (or both)
343
- // else it's just an expensive noop.
344
- const has_report_to = directives['report-to']?.length ?? 0 > 0;
345
- const has_report_uri = directives['report-uri']?.length ?? 0 > 0;
346
- if (!has_report_to && !has_report_uri) {
347
- throw Error(
348
- '`content-security-policy-report-only` must be specified with either the `report-to` or `report-uri` directives, or both'
349
- );
350
- }
324
+ // If we're generating content-security-policy-report-only,
325
+ // if there are any directives, we need a report-uri or report-to (or both)
326
+ // else it's just an expensive noop.
327
+ if (
328
+ Object.values(directives).some((v) => !!v) &&
329
+ !directives['report-to']?.length &&
330
+ !directives['report-uri']?.length
331
+ ) {
332
+ throw Error(
333
+ '`content-security-policy-report-only` must be specified with either the `report-to` or `report-uri` directives, or both'
334
+ );
351
335
  }
352
336
  }
353
337
  }
@@ -2,7 +2,7 @@
2
2
  /** @import { ActionResult, RequestEvent, SSRManifest } from '@sveltejs/kit' */
3
3
  /** @import { PageNodeIndexes, RequestState, RequiredResolveOptions, ServerDataNode, SSRNode, SSROptions, SSRState } from 'types' */
4
4
  import { text } from '@sveltejs/kit';
5
- import { HttpError, Redirect } from '@sveltejs/kit/internal';
5
+ import { Redirect } from '@sveltejs/kit/internal';
6
6
  import { compact } from '../../../utils/array.js';
7
7
  import { get_status, normalize_error } from '../../../utils/error.js';
8
8
  import { noop } from '../../../utils/functions.js';
@@ -386,7 +386,6 @@ export async function render_page(
386
386
  options,
387
387
  manifest,
388
388
  state,
389
- status: e instanceof HttpError ? e.status : 500,
390
389
  error: e,
391
390
  resolve_opts
392
391
  });
@@ -334,14 +334,24 @@ export function create_universal_fetch(event, state, fetched, csr, resolve_opts)
334
334
  );
335
335
  }
336
336
 
337
+ const request_body =
338
+ input instanceof Request && cloned_body
339
+ ? await new Response(cloned_body).text()
340
+ : init?.body;
341
+
342
+ if (
343
+ request_body &&
344
+ typeof request_body !== 'string' &&
345
+ !ArrayBuffer.isView(request_body)
346
+ ) {
347
+ // requests whose body can't be hashed aren't serialized — the browser repeats the fetch
348
+ return;
349
+ }
350
+
337
351
  fetched.push({
338
352
  url: same_origin ? url.href.slice(event.url.origin.length) : url.href,
339
353
  method: event.request.method,
340
- request_body: /** @type {string | ArrayBufferView | undefined} */ (
341
- input instanceof Request && cloned_body
342
- ? await stream_to_string(cloned_body)
343
- : init?.body
344
- ),
354
+ request_body: /** @type {string | ArrayBufferView | null | undefined} */ (request_body),
345
355
  request_headers: cloned_headers,
346
356
  response_body: body,
347
357
  response,
@@ -361,16 +371,7 @@ export function create_universal_fetch(event, state, fetched, csr, resolve_opts)
361
371
  const [a, b] = response.body.tee();
362
372
 
363
373
  void (async () => {
364
- let result = new Uint8Array();
365
-
366
- for await (const chunk of a) {
367
- const combined = new Uint8Array(result.length + chunk.length);
368
-
369
- combined.set(result, 0);
370
- combined.set(chunk, result.length);
371
-
372
- result = combined;
373
- }
374
+ const result = new Uint8Array(await new Response(a).arrayBuffer());
374
375
 
375
376
  if (dependency) {
376
377
  dependency.body = new Uint8Array(result);
@@ -486,21 +487,3 @@ export function create_universal_fetch(event, state, fetched, csr, resolve_opts)
486
487
  return response;
487
488
  };
488
489
  }
489
-
490
- /**
491
- * @param {ReadableStream<Uint8Array>} stream
492
- */
493
- async function stream_to_string(stream) {
494
- let result = '';
495
- const reader = stream.getReader();
496
- const decoder = new TextDecoder();
497
- while (true) {
498
- const { done, value } = await reader.read();
499
- if (done) {
500
- result += decoder.decode();
501
- break;
502
- }
503
- result += decoder.decode(value, { stream: true });
504
- }
505
- return result;
506
- }