@sveltejs/kit 3.0.0-next.23 → 3.0.0-next.25

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 (105) hide show
  1. package/package.json +51 -8
  2. package/src/cli.js +3 -3
  3. package/src/constants.js +19 -1
  4. package/src/core/adapt/builder.js +3 -5
  5. package/src/core/env.js +137 -182
  6. package/src/core/postbuild/analyse.js +9 -4
  7. package/src/core/postbuild/prerender.js +5 -6
  8. package/src/core/sync/create_manifest_data/index.js +29 -33
  9. package/src/core/sync/sync.js +23 -40
  10. package/src/core/sync/write_app_manifest.js +83 -0
  11. package/src/core/sync/write_client_manifest.js +6 -4
  12. package/src/core/sync/write_server.js +12 -22
  13. package/src/core/sync/write_types/index.js +2 -2
  14. package/src/core/utils.js +10 -28
  15. package/src/exports/adapter.js +22 -0
  16. package/src/exports/hooks/public.d.ts +9 -1
  17. package/src/exports/node/index.js +77 -5
  18. package/src/exports/public.d.ts +1 -1
  19. package/src/exports/vite/build/build_server.js +3 -3
  20. package/src/exports/vite/build/service-worker.js +98 -0
  21. package/src/exports/vite/build/utils.js +48 -4
  22. package/src/exports/vite/dev/index.js +28 -25
  23. package/src/exports/vite/index.js +94 -747
  24. package/src/exports/vite/module_ids.js +1 -11
  25. package/src/exports/vite/plugins/env-vars.js +159 -0
  26. package/src/exports/vite/plugins/guard.js +205 -0
  27. package/src/exports/vite/preview/index.js +6 -4
  28. package/src/exports/vite/utils.js +99 -4
  29. package/src/runtime/app/env/{types.d.ts → client.js} +9 -4
  30. package/src/runtime/app/env/index.js +1 -2
  31. package/src/runtime/app/env/private/index.js +1 -0
  32. package/src/runtime/app/env/public/client.js +1 -1
  33. package/src/runtime/app/env/public/server.js +1 -1
  34. package/src/runtime/app/env/{internal.js → server.js} +4 -4
  35. package/src/runtime/app/forms/client.js +263 -0
  36. package/src/runtime/app/forms/index.js +1 -263
  37. package/src/runtime/app/forms/public.d.ts +1 -1
  38. package/src/runtime/app/forms/server.js +6 -0
  39. package/src/runtime/app/forms/shared.js +34 -0
  40. package/src/runtime/app/manifest/index.js +1 -1
  41. package/src/runtime/app/navigation/client.js +15 -0
  42. package/src/runtime/app/navigation/index.js +1 -15
  43. package/src/runtime/app/navigation/public.d.ts +1 -1
  44. package/src/runtime/app/navigation/server.js +15 -0
  45. package/src/runtime/app/paths/server.js +1 -1
  46. package/src/runtime/app/server/remote/form.js +11 -1
  47. package/src/runtime/app/server/remote/prerender.js +1 -2
  48. package/src/runtime/app/server/remote/query.js +3 -3
  49. package/src/runtime/app/state/client.svelte.js +207 -0
  50. package/src/runtime/app/state/index.js +1 -66
  51. package/src/runtime/app/state/public.d.ts +1 -1
  52. package/src/runtime/client/bundle.js +1 -1
  53. package/src/runtime/client/client.js +210 -278
  54. package/src/runtime/client/fetcher.js +18 -8
  55. package/src/runtime/client/remote-functions/form.svelte.js +9 -6
  56. package/src/runtime/client/remote-functions/prerender.svelte.js +1 -1
  57. package/src/runtime/client/remote-functions/query-live/iterator.js +1 -1
  58. package/src/runtime/client/remote-functions/shared.svelte.js +2 -2
  59. package/src/runtime/client/snapshots.js +1 -3
  60. package/src/runtime/client/stream.js +27 -20
  61. package/src/runtime/client/utils.js +7 -3
  62. package/src/runtime/components/root.svelte +5 -5
  63. package/src/runtime/env/dynamic/private.js +1 -1
  64. package/src/runtime/env/static/private.js +1 -1
  65. package/src/runtime/error-chain.js +54 -0
  66. package/src/runtime/form-utils.js +55 -87
  67. package/src/runtime/invalid-import.js +1 -0
  68. package/src/runtime/props.svelte.js +1 -1
  69. package/src/runtime/server/data/index.js +12 -27
  70. package/src/runtime/server/endpoint.js +8 -3
  71. package/src/runtime/server/env_module.js +1 -1
  72. package/src/runtime/server/errors.js +9 -12
  73. package/src/runtime/server/fetch.js +14 -16
  74. package/src/runtime/server/index.js +30 -27
  75. package/src/runtime/server/internal.js +34 -4
  76. package/src/runtime/server/page/actions.js +77 -124
  77. package/src/runtime/server/page/data_serializer.js +6 -10
  78. package/src/runtime/server/page/index.js +45 -82
  79. package/src/runtime/server/page/render.js +34 -52
  80. package/src/runtime/server/page/respond_with_error.js +7 -9
  81. package/src/runtime/server/remote-functions.js +138 -166
  82. package/src/runtime/server/respond.js +75 -30
  83. package/src/runtime/server/state.js +1 -0
  84. package/src/runtime/server/utils.js +0 -7
  85. package/src/runtime/utils.js +41 -0
  86. package/src/telemetry.js +1 -33
  87. package/src/types/ambient-private.d.ts +9 -17
  88. package/src/types/global-private.d.ts +8 -0
  89. package/src/types/internal.d.ts +27 -15
  90. package/src/types/private.d.ts +1 -1
  91. package/src/utils/exports.js +1 -0
  92. package/src/utils/filesystem.js +10 -22
  93. package/src/utils/functions.js +10 -0
  94. package/src/utils/routing.js +0 -11
  95. package/src/utils/streaming.js +5 -15
  96. package/src/utils/url.js +0 -11
  97. package/src/version.js +1 -1
  98. package/types/index.d.ts +54 -29
  99. package/types/index.d.ts.map +10 -5
  100. package/src/runtime/app/env/private.js +0 -1
  101. package/src/runtime/app/env/standard-schema.d.ts +0 -0
  102. package/src/runtime/app/state/client.js +0 -63
  103. package/src/runtime/client/state.svelte.js +0 -98
  104. package/src/runtime/server/ambient.d.ts +0 -4
  105. package/src/utils/path.js +0 -23
@@ -1,9 +1,8 @@
1
1
  /** @import { RequestEvent, SSRManifest } from '@sveltejs/kit' */
2
2
  /** @import { RemoteForm } from '$app/server' */
3
- /** @import { ActionResult } from '$app/forms' */
4
- /** @import { RemoteFormInternals, RemoteFunctionData, RemoteFunctionResponse, RemoteInternals, RequestState, SSROptions } from 'types' */
3
+ /** @import { RemoteFormInternals, RemoteFunctionData, RemoteFunctionResponse, RemoteInternals, RequestState, ServerActionResult } from 'types' */
5
4
 
6
- import { json, error } from '@sveltejs/kit';
5
+ import { error } from '@sveltejs/kit';
7
6
  import { Redirect, SvelteKitError } from '@sveltejs/kit/internal';
8
7
  import { with_request_store, merge_tracing, record_span } from '@sveltejs/kit/internal/server';
9
8
  import { app_dir, base } from '#app/paths';
@@ -11,10 +10,13 @@ import { is_form_content_type } from '../../utils/http.js';
11
10
  import { create_remote_key, parse_remote_arg, split_remote_key } from '../shared.js';
12
11
  import { stringify } from '#app/internal/transport';
13
12
  import { handle_error_and_jsonify } from './errors.js';
14
- import { normalize_error } from '../../utils/error.js';
15
- import { check_incorrect_fail_use, get_action_location } from './page/actions.js';
16
- import { DEV } from 'esm-env';
13
+ import {
14
+ action_error_result,
15
+ get_action_location,
16
+ method_not_allowed_result
17
+ } from './page/actions.js';
17
18
  import { deserialize_binary_form } from '../form-utils.js';
19
+ import { text_encoder } from '../utils.js';
18
20
  import { with_version_header } from './utils.js';
19
21
 
20
22
  /**
@@ -24,8 +26,121 @@ import { with_version_header } from './utils.js';
24
26
  */
25
27
  const KEEP_ALIVE_INTERVAL = 30_000;
26
28
 
29
+ /**
30
+ * @param {RequestEvent} event
31
+ * @param {RequestState} state
32
+ * @param {import('types').RemoteQueryLiveInternals} internals
33
+ * @param {any} arg
34
+ */
35
+ export function create_live_query_response(event, state, internals, arg) {
36
+ const cancellation = new AbortController();
37
+ const live_event = {
38
+ ...event,
39
+ request: new Request(event.request, {
40
+ signal: AbortSignal.any([event.request.signal, cancellation.signal])
41
+ })
42
+ };
43
+
44
+ const generator = internals.run(live_event, state, arg);
45
+
46
+ let open = true;
47
+ let pulling = false;
48
+ /** @type {ReadableStreamDefaultController<Uint8Array>} */
49
+ let stream_controller;
50
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
51
+ let keep_alive;
52
+ /** @type {string | undefined} */
53
+ let result;
54
+
55
+ function schedule_keep_alive() {
56
+ clearTimeout(keep_alive);
57
+ keep_alive = setTimeout(() => {
58
+ if (!open) return;
59
+ if ((stream_controller.desiredSize ?? 0) > 0) {
60
+ stream_controller.enqueue(text_encoder.encode(': keep-alive\n\n'));
61
+ }
62
+ schedule_keep_alive();
63
+ }, KEEP_ALIVE_INTERVAL);
64
+ }
65
+
66
+ /** @param {any} data */
67
+ function send(data) {
68
+ if (!open) return;
69
+ stream_controller.enqueue(text_encoder.encode('data: ' + JSON.stringify(data) + '\n\n'));
70
+ schedule_keep_alive();
71
+ }
72
+
73
+ /** @param {boolean} cancelled */
74
+ function teardown(cancelled) {
75
+ if (!open) return;
76
+ open = false;
77
+ clearTimeout(keep_alive);
78
+ cancellation.abort();
79
+ if (!cancelled) stream_controller.close();
80
+ // AsyncGenerator.return() cannot interrupt a pending next(). Cleanup is
81
+ // cooperative via request.signal, so stream cancellation must not await it.
82
+ void generator.return(undefined).catch(() => {});
83
+ }
84
+
85
+ event.request.signal.addEventListener('abort', () => teardown(true), { once: true });
86
+
87
+ return new Response(
88
+ new ReadableStream({
89
+ start(controller) {
90
+ stream_controller = controller;
91
+ schedule_keep_alive();
92
+ },
93
+ async pull() {
94
+ if (!open || pulling) return;
95
+ pulling = true;
96
+
97
+ try {
98
+ while (open) {
99
+ const { value, done } = await generator.next();
100
+
101
+ if (!open) return;
102
+
103
+ if (done) {
104
+ teardown(false);
105
+ return;
106
+ }
107
+
108
+ if (result !== (result = stringify(value))) {
109
+ send({ type: 'result', result });
110
+ return;
111
+ }
112
+ }
113
+ } catch (error) {
114
+ if (!open) return;
115
+
116
+ if (error instanceof Redirect) {
117
+ send({ type: 'redirect', location: error.location });
118
+ } else {
119
+ const transformed = await handle_error_and_jsonify(event, state, error);
120
+
121
+ send({ type: 'error', error: transformed });
122
+ }
123
+
124
+ teardown(false);
125
+ } finally {
126
+ pulling = false;
127
+ }
128
+ },
129
+ cancel() {
130
+ teardown(true);
131
+ }
132
+ }),
133
+ {
134
+ headers: {
135
+ 'cache-control': 'private, no-store',
136
+ 'content-type': 'text/event-stream'
137
+ }
138
+ }
139
+ );
140
+ }
141
+
27
142
  /** @type {typeof handle_remote_call_internal} */
28
- export async function handle_remote_call(event, state, options, manifest, id) {
143
+ export async function handle_remote_call(event, state, manifest, id) {
29
144
  return record_span({
30
145
  name: 'sveltekit.remote.call',
31
146
  attributes: {
@@ -34,7 +149,7 @@ export async function handle_remote_call(event, state, options, manifest, id) {
34
149
  fn: async (current) => {
35
150
  const traced_event = merge_tracing(event, current);
36
151
  const response = await with_request_store({ event: traced_event, state }, () =>
37
- handle_remote_call_internal(traced_event, state, options, manifest, id)
152
+ handle_remote_call_internal(traced_event, state, manifest, id)
38
153
  );
39
154
  return with_version_header(response);
40
155
  }
@@ -44,11 +159,10 @@ export async function handle_remote_call(event, state, options, manifest, id) {
44
159
  /**
45
160
  * @param {RequestEvent} event
46
161
  * @param {RequestState} state
47
- * @param {SSROptions} options
48
162
  * @param {SSRManifest} manifest
49
163
  * @param {string} id
50
164
  */
51
- async function handle_remote_call_internal(event, state, options, manifest, id) {
165
+ async function handle_remote_call_internal(event, state, manifest, id) {
52
166
  const [hash, name, additional_args] = id.split('/');
53
167
  const remotes = manifest._.remotes;
54
168
 
@@ -88,119 +202,7 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
88
202
  new URL(event.request.url).searchParams.get('payload')
89
203
  );
90
204
 
91
- const generator = internals.run(event, state, parse_remote_arg(payload));
92
-
93
- const encoder = new TextEncoder();
94
-
95
- let closed = false;
96
-
97
- /** @type {ReturnType<typeof setTimeout> | undefined} */
98
- let keep_alive;
99
-
100
- /**
101
- * (Re)schedule the keep-alive comment. Called whenever a message is sent, so
102
- * that a keep-alive is only emitted once `KEEP_ALIVE_INTERVAL` has elapsed
103
- * without any other activity.
104
- * @param {ReadableStreamDefaultController} controller
105
- */
106
- function schedule_keep_alive(controller) {
107
- clearTimeout(keep_alive);
108
- keep_alive = setTimeout(() => {
109
- if (closed || event.request.signal.aborted) return;
110
- // SSE comments (lines starting with `:`) are ignored by the client
111
- controller.enqueue(encoder.encode(': keep-alive\n\n'));
112
- schedule_keep_alive(controller);
113
- }, KEEP_ALIVE_INTERVAL);
114
- }
115
-
116
- /**
117
- * @param {ReadableStreamDefaultController} controller
118
- * @param {any} payload
119
- */
120
- function send(controller, payload) {
121
- controller.enqueue(encoder.encode('data: ' + JSON.stringify(payload) + '\n\n'));
122
- schedule_keep_alive(controller);
123
- }
124
-
125
- /** @type {string | undefined} */
126
- let result = undefined;
127
-
128
- async function cancel() {
129
- if (closed) return;
130
- closed = true;
131
- clearTimeout(keep_alive);
132
- await generator.return(undefined);
133
- }
134
-
135
- event.request.signal.addEventListener('abort', cancel, { once: true });
136
-
137
- return new Response(
138
- new ReadableStream({
139
- start(controller) {
140
- schedule_keep_alive(controller);
141
- },
142
- async pull(controller) {
143
- if (event.request.signal.aborted) {
144
- await cancel();
145
- controller.close();
146
- return;
147
- }
148
-
149
- try {
150
- while (true) {
151
- const { value, done } = await generator.next();
152
-
153
- if (done) {
154
- await cancel();
155
- controller.close();
156
- return;
157
- }
158
-
159
- // only send changed data
160
- if (result !== (result = stringify(value))) {
161
- send(controller, {
162
- type: 'result',
163
- result
164
- });
165
-
166
- return;
167
- }
168
- }
169
- } catch (error) {
170
- if (!event.request.signal.aborted) {
171
- if (error instanceof Redirect) {
172
- send(controller, {
173
- type: 'redirect',
174
- location: error.location
175
- });
176
- } else {
177
- const transformed = await handle_error_and_jsonify(
178
- event,
179
- state,
180
- options,
181
- error
182
- );
183
-
184
- send(controller, {
185
- type: 'error',
186
- error: transformed
187
- });
188
- }
189
- }
190
-
191
- await cancel();
192
- controller.close();
193
- }
194
- },
195
- cancel
196
- }),
197
- {
198
- headers: {
199
- 'cache-control': 'private, no-store',
200
- 'content-type': 'text/event-stream'
201
- }
202
- }
203
- );
205
+ return create_live_query_response(event, state, internals, parse_remote_arg(payload));
204
206
  }
205
207
 
206
208
  case 'query_batch': {
@@ -217,7 +219,7 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
217
219
 
218
220
  const args = await Promise.all(payloads.map((payload) => parse_remote_arg(payload)));
219
221
 
220
- data._ = await with_request_store({ event, state }, () => internals.run(args, options));
222
+ data._ = await with_request_store({ event, state }, () => internals.run(args));
221
223
 
222
224
  break;
223
225
  }
@@ -262,7 +264,7 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
262
264
 
263
265
  if (data._.issues) {
264
266
  // special case — don't serialize refreshes/reconnects
265
- return json(
267
+ return Response.json(
266
268
  /** @type {RemoteFunctionResponse} */ ({
267
269
  type: 'result',
268
270
  data: stringify(data)
@@ -308,9 +310,9 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
308
310
  }
309
311
  }
310
312
 
311
- await collect_remote_data(data, event, state, options);
313
+ await collect_remote_data(data, event, state);
312
314
 
313
- return json(
315
+ return Response.json(
314
316
  /** @type {RemoteFunctionResponse} */ ({
315
317
  type: 'result',
316
318
  data: stringify(data)
@@ -319,9 +321,9 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
319
321
  );
320
322
  } catch (error) {
321
323
  if (error instanceof Redirect) {
322
- const data = await collect_remote_data({ redirect: error.location }, event, state, options);
324
+ const data = await collect_remote_data({ redirect: error.location }, event, state);
323
325
 
324
- return json(
326
+ return Response.json(
325
327
  /** @type {RemoteFunctionResponse} */ ({
326
328
  type: 'result',
327
329
  data: stringify(data)
@@ -330,9 +332,9 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
330
332
  );
331
333
  }
332
334
 
333
- const transformed = await handle_error_and_jsonify(event, state, options, error);
335
+ const transformed = await handle_error_and_jsonify(event, state, error);
334
336
 
335
- return json(
337
+ return Response.json(
336
338
  /** @type {RemoteFunctionResponse} */ ({
337
339
  type: 'error',
338
340
  error: transformed
@@ -355,9 +357,8 @@ async function handle_remote_call_internal(event, state, options, manifest, id)
355
357
  * @param {RemoteFunctionData} data
356
358
  * @param {RequestEvent} event
357
359
  * @param {RequestState} state
358
- * @param {SSROptions} options
359
360
  */
360
- export async function collect_remote_data(data, event, state, options) {
361
+ export async function collect_remote_data(data, event, state) {
361
362
  /**
362
363
  *
363
364
  * @param {unknown} error
@@ -365,7 +366,7 @@ export async function collect_remote_data(data, event, state, options) {
365
366
  */
366
367
  function convert_error(error) {
367
368
  // TODO 4.0 remove the `Promise.resolve(...)`
368
- return Promise.resolve(handle_error_and_jsonify(event, state, options, error));
369
+ return Promise.resolve(handle_error_and_jsonify(event, state, error));
369
370
  }
370
371
 
371
372
  /** @type {Promise<any>[]} */
@@ -531,7 +532,7 @@ export async function handle_remote_form_post(event, state, manifest, id) {
531
532
  * @param {RequestState} state
532
533
  * @param {SSRManifest} manifest
533
534
  * @param {string} id
534
- * @returns {Promise<ActionResult>}
535
+ * @returns {Promise<ServerActionResult>}
535
536
  */
536
537
  async function handle_remote_form_post_internal(event, state, manifest, id) {
537
538
  const location = get_action_location(event.url);
@@ -547,21 +548,7 @@ async function handle_remote_form_post_internal(event, state, manifest, id) {
547
548
  );
548
549
 
549
550
  if (!form) {
550
- event.setHeaders({
551
- // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
552
- // "The server must generate an Allow header field in a 405 status code response"
553
- allow: 'GET'
554
- });
555
- return {
556
- type: 'error',
557
- location,
558
- // We're lying a bit with the types here; this will be transformed into a proper App.Error object later
559
- error: new SvelteKitError(
560
- 405,
561
- 'Method Not Allowed',
562
- `POST method not allowed. No form actions exist for ${DEV ? `the page at ${event.route.id}` : 'this page'}`
563
- )
564
- };
551
+ return method_not_allowed_result(event, location);
565
552
  }
566
553
 
567
554
  if (action_id) {
@@ -591,22 +578,7 @@ async function handle_remote_form_post_internal(event, state, manifest, id) {
591
578
  location
592
579
  };
593
580
  } catch (e) {
594
- const err = normalize_error(e);
595
-
596
- if (err instanceof Redirect) {
597
- return {
598
- type: 'redirect',
599
- status: err.status,
600
- location: err.location
601
- };
602
- }
603
-
604
- return {
605
- type: 'error',
606
- location,
607
- // @ts-expect-error We're lying a bit with the types here; this will be transformed into a proper App.Error object later
608
- error: check_incorrect_fail_use(err)
609
- };
581
+ return action_error_result(e, location);
610
582
  }
611
583
  }
612
584
 
@@ -1,6 +1,6 @@
1
1
  /** @import { SSRNode } from 'types' */
2
2
  import { DEV } from 'esm-env';
3
- import { json, text } from '@sveltejs/kit';
3
+ import { text } from '@sveltejs/kit';
4
4
  import { Redirect, SvelteKitError } from '@sveltejs/kit/internal';
5
5
  import {
6
6
  merge_tracing,
@@ -45,6 +45,7 @@ import {
45
45
  } from '../pathname.js';
46
46
  import { server_data_serializer } from './page/data_serializer.js';
47
47
  import { get_remote_id, handle_remote_call } from './remote-functions.js';
48
+ import { hooks, options } from './internal.js';
48
49
 
49
50
  /** @type {import('types').RequiredResolveOptions['transformPageChunk']} */
50
51
  const default_transform = ({ html }) => html;
@@ -85,12 +86,11 @@ export const respond = propagate_context(internal_respond);
85
86
 
86
87
  /**
87
88
  * @param {Request} request
88
- * @param {import('types').SSROptions} options
89
89
  * @param {import('@sveltejs/kit').SSRManifest} manifest
90
90
  * @param {import('types').RequestState} state
91
91
  * @returns {Promise<Response>}
92
92
  */
93
- export async function internal_respond(request, options, manifest, state) {
93
+ export async function internal_respond(request, manifest, state) {
94
94
  /** URL but stripped from the potential `/__data.json` suffix and its search param */
95
95
  const url = new URL(request.url);
96
96
 
@@ -100,7 +100,7 @@ export async function internal_respond(request, options, manifest, state) {
100
100
 
101
101
  if (!__SVELTEKIT_DEV__) {
102
102
  const request_origin = request.headers.get('origin');
103
- const self_origin = get_self_origin(options.paths_origin, url.origin);
103
+ const self_origin = get_self_origin(__SVELTEKIT_PATHS_ORIGIN__, url.origin);
104
104
 
105
105
  if (remote_id) {
106
106
  if (
@@ -111,9 +111,9 @@ export async function internal_respond(request, options, manifest, state) {
111
111
  })
112
112
  ) {
113
113
  const message = 'Cross-site remote requests are forbidden';
114
- return json({ message }, { status: 403 });
114
+ return Response.json({ message }, { status: 403 });
115
115
  }
116
- } else if (options.csrf_check_origin) {
116
+ } else if (__SVELTEKIT_CSRF_CHECK_ORIGIN__) {
117
117
  const forbidden = is_csrf_forbidden({
118
118
  request,
119
119
  request_origin,
@@ -126,7 +126,7 @@ export async function internal_respond(request, options, manifest, state) {
126
126
  const opts = { status: 403 };
127
127
 
128
128
  if (request.headers.get('accept') === 'application/json') {
129
- return json({ message }, opts);
129
+ return Response.json({ message }, opts);
130
130
  }
131
131
 
132
132
  return text(message, opts);
@@ -134,7 +134,7 @@ export async function internal_respond(request, options, manifest, state) {
134
134
  }
135
135
  }
136
136
 
137
- if (options.hash_routing && url.pathname !== base + '/' && url.pathname !== '/[fallback]') {
137
+ if (__SVELTEKIT_HASH_ROUTING__ && url.pathname !== base + '/' && url.pathname !== '/[fallback]') {
138
138
  return text('Not found', { status: 404 });
139
139
  }
140
140
 
@@ -243,7 +243,6 @@ export async function internal_respond(request, options, manifest, state) {
243
243
  // @ts-expect-error this has to be assigned lazily
244
244
  event.fetch = create_fetch({
245
245
  event,
246
- options,
247
246
  manifest,
248
247
  state,
249
248
  get_cookie_header,
@@ -263,7 +262,16 @@ export async function internal_respond(request, options, manifest, state) {
263
262
 
264
263
  // reroute could alter the given URL, so we pass a copy
265
264
  resolved_path =
266
- (await options.hooks.reroute({ url: new URL(url), fetch: event.fetch })) ?? url.pathname;
265
+ (await hooks.reroute({ url: new URL(url), fetch: event.fetch })) ?? url.pathname;
266
+
267
+ if (!manifest._.routes.length && resolved_path !== url.pathname) {
268
+ state.rerouted_url = denormalise_url({
269
+ request_url: request.url,
270
+ resolved_path,
271
+ is_data_request,
272
+ is_route_resolution_request
273
+ }).toString();
274
+ }
267
275
  } catch {
268
276
  return text('Internal Server Error', {
269
277
  status: 500
@@ -300,12 +308,12 @@ export async function internal_respond(request, options, manifest, state) {
300
308
  !state.prerendering?.fallback &&
301
309
  has_prerendered_path(manifest, resolved_path)
302
310
  ) {
303
- const url = new URL(request.url);
304
- url.pathname = is_data_request
305
- ? add_data_suffix(resolved_path)
306
- : is_route_resolution_request
307
- ? add_resolution_suffix(resolved_path)
308
- : resolved_path;
311
+ const url = denormalise_url({
312
+ request_url: request.url,
313
+ resolved_path,
314
+ is_data_request,
315
+ is_route_resolution_request
316
+ });
309
317
 
310
318
  try {
311
319
  // `fetch` automatically decodes the body, so we need to delete the related headers to not break the response
@@ -323,7 +331,7 @@ export async function internal_respond(request, options, manifest, state) {
323
331
  statusText: response.statusText
324
332
  });
325
333
  } catch (error) {
326
- return await handle_fatal_error(event, state, options, error);
334
+ return await handle_fatal_error(event, state, error);
327
335
  }
328
336
  }
329
337
 
@@ -369,7 +377,7 @@ export async function internal_respond(request, options, manifest, state) {
369
377
  event.params = result.params;
370
378
  }
371
379
  } catch (e) {
372
- return await handle_fatal_error(event, state, options, e);
380
+ return await handle_fatal_error(event, state, e);
373
381
  }
374
382
  }
375
383
 
@@ -452,10 +460,10 @@ export async function internal_respond(request, options, manifest, state) {
452
460
  add_cookies_to_headers(response.headers, new_cookies.values());
453
461
  return response;
454
462
  } catch (err) {
455
- return await handle_fatal_error(event, state, options, err);
463
+ return await handle_fatal_error(event, state, err);
456
464
  }
457
465
  }
458
- return await handle_fatal_error(event, state, options, e);
466
+ return await handle_fatal_error(event, state, e);
459
467
  }
460
468
 
461
469
  async function handle() {
@@ -485,7 +493,7 @@ export async function internal_respond(request, options, manifest, state) {
485
493
  };
486
494
 
487
495
  return await with_request_store({ event: traced_event, state }, () =>
488
- options.hooks.handle({
496
+ hooks.handle({
489
497
  event: traced_event,
490
498
  resolve: (event, opts) => {
491
499
  return record_span({
@@ -592,7 +600,6 @@ export async function internal_respond(request, options, manifest, state) {
592
600
  return await respond_with_error({
593
601
  event,
594
602
  state,
595
- options,
596
603
  manifest,
597
604
  error: new SvelteKitError(
598
605
  400,
@@ -603,11 +610,10 @@ export async function internal_respond(request, options, manifest, state) {
603
610
  });
604
611
  }
605
612
 
606
- if (options.hash_routing || state.prerendering?.fallback) {
613
+ if (__SVELTEKIT_HASH_ROUTING__ || state.prerendering?.fallback) {
607
614
  return await render_response({
608
615
  event,
609
616
  state,
610
- options,
611
617
  manifest,
612
618
  page_config: { ssr: false, csr: true },
613
619
  status: 200,
@@ -622,12 +628,12 @@ export async function internal_respond(request, options, manifest, state) {
622
628
  ],
623
629
  fetched: [],
624
630
  resolve_opts,
625
- data_serializer: server_data_serializer(event, state, options)
631
+ data_serializer: server_data_serializer(event, state)
626
632
  });
627
633
  }
628
634
 
629
635
  if (remote_id) {
630
- return await handle_remote_call(event, state, options, manifest, remote_id);
636
+ return await handle_remote_call(event, state, manifest, remote_id);
631
637
  }
632
638
 
633
639
  if (route) {
@@ -641,7 +647,6 @@ export async function internal_respond(request, options, manifest, state) {
641
647
  event,
642
648
  state,
643
649
  route,
644
- options,
645
650
  manifest,
646
651
  invalidated_data_nodes,
647
652
  trailing_slash
@@ -676,7 +681,6 @@ export async function internal_respond(request, options, manifest, state) {
676
681
  event,
677
682
  state,
678
683
  route.page,
679
- options,
680
684
  manifest,
681
685
  page_nodes,
682
686
  resolve_opts
@@ -754,6 +758,25 @@ export async function internal_respond(request, options, manifest, state) {
754
758
  // if this request came direct from the user, rather than
755
759
  // via our own `fetch`, render a 404 page
756
760
  if (state.depth === 0) {
761
+ // Error-page data requests only invalidate the root layout.
762
+ if (
763
+ !state.prerendering &&
764
+ is_data_request &&
765
+ invalidated_data_nodes?.length === 1 &&
766
+ invalidated_data_nodes[0]
767
+ ) {
768
+ return await render_data(
769
+ event,
770
+ state,
771
+ { page: { layouts: [], leaf: 0 } },
772
+ manifest,
773
+ invalidated_data_nodes,
774
+ // there is no route to take a trailing slash option from, and the
775
+ // SSR'd error page sees the pathname as-is
776
+ 'ignore'
777
+ );
778
+ }
779
+
757
780
  if (non_html_fetch_destinations.has(event.request.headers.get('sec-fetch-dest') ?? '')) {
758
781
  return text('Not Found', {
759
782
  status: 404,
@@ -764,7 +787,6 @@ export async function internal_respond(request, options, manifest, state) {
764
787
  return await respond_with_error({
765
788
  event,
766
789
  state,
767
- options,
768
790
  manifest,
769
791
  error: new SvelteKitError(404, 'Not Found', `Not found: ${event.url.pathname}`),
770
792
  resolve_opts
@@ -786,7 +808,7 @@ export async function internal_respond(request, options, manifest, state) {
786
808
  // and I don't even know how to describe it. need to investigate at some point
787
809
 
788
810
  // HttpError from endpoint can end up here - TODO should it be handled there instead?
789
- return await handle_fatal_error(event, state, options, e);
811
+ return await handle_fatal_error(event, state, e);
790
812
  } finally {
791
813
  event.cookies.set = () => {
792
814
  throw new Error('Cannot use `cookies.set(...)` after the response has been generated');
@@ -833,3 +855,26 @@ function propagate_context(fn) {
833
855
  });
834
856
  };
835
857
  }
858
+
859
+ /**
860
+ * @param {object} opts
861
+ * @param {string} opts.request_url - The original request URL
862
+ * @param {string} opts.resolved_path - The resolved pathname
863
+ * @param {boolean} opts.is_data_request - Whether the request is a data request
864
+ * @param {boolean} opts.is_route_resolution_request -
865
+ * @returns {URL}
866
+ */
867
+ function denormalise_url({
868
+ request_url,
869
+ resolved_path,
870
+ is_data_request,
871
+ is_route_resolution_request
872
+ }) {
873
+ const url = new URL(request_url);
874
+ url.pathname = is_data_request
875
+ ? add_data_suffix(resolved_path)
876
+ : is_route_resolution_request
877
+ ? add_resolution_suffix(resolved_path)
878
+ : resolved_path;
879
+ return url;
880
+ }
@@ -36,6 +36,7 @@ export function create_request_state(options) {
36
36
  prerender_default: undefined,
37
37
  error: false,
38
38
  depth: 0,
39
+ rerouted_url: null,
39
40
  ...transient_fields()
40
41
  };
41
42
  }