@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,4 +1,4 @@
1
- import { json, text } from '@sveltejs/kit';
1
+ import { text } from '@sveltejs/kit';
2
2
  import {
3
3
  HandledHttpError,
4
4
  HttpError,
@@ -8,17 +8,16 @@ import {
8
8
  import { with_request_store } from '@sveltejs/kit/internal/server';
9
9
  import { add_deprecated_handle_error_properties, coalesce_to_error } from '../../utils/error.js';
10
10
  import { negotiate } from '../../utils/http.js';
11
- import { fix_stack_trace } from './internal.js';
11
+ import { fix_stack_trace, hooks, options } from './internal.js';
12
12
  import { escape_html } from '../../utils/escape.js';
13
13
 
14
14
  /**
15
15
  * @param {import('@sveltejs/kit').RequestEvent} event
16
16
  * @param {import('types').RequestState} state
17
- * @param {import('types').SSROptions} options
18
17
  * @param {unknown} error
19
18
  */
20
- export async function handle_fatal_error(event, state, options, error) {
21
- const body = await handle_error_and_jsonify(event, state, options, error);
19
+ export async function handle_fatal_error(event, state, error) {
20
+ const body = await handle_error_and_jsonify(event, state, error);
22
21
  const status = body.status;
23
22
 
24
23
  // sec-fetch-dest would be nicer, but non-browser clients and plain HTTP hosts don't send it
@@ -28,22 +27,21 @@ export async function handle_fatal_error(event, state, options, error) {
28
27
  ]);
29
28
 
30
29
  if (event.isDataRequest || type === 'application/json') {
31
- return json(body, {
30
+ return Response.json(body, {
32
31
  status
33
32
  });
34
33
  }
35
34
 
36
- return static_error_page(options, status, body.message);
35
+ return static_error_page(status, body.message);
37
36
  }
38
37
 
39
38
  /**
40
39
  * @param {import('@sveltejs/kit').RequestEvent} event
41
40
  * @param {import('types').RequestState} state
42
- * @param {import('types').SSROptions} options
43
41
  * @param {any} error
44
42
  * @returns {App.Error | Promise<App.Error>}
45
43
  */
46
- export function handle_error_and_jsonify(event, state, options, error) {
44
+ export function handle_error_and_jsonify(event, state, error) {
47
45
  if (error instanceof HandledHttpError) {
48
46
  return error.body;
49
47
  }
@@ -90,7 +88,7 @@ export function handle_error_and_jsonify(event, state, options, error) {
90
88
  const input = { ...caught, event };
91
89
  if (__SVELTEKIT_DEV__) add_deprecated_handle_error_properties(input, fallback);
92
90
 
93
- result = with_request_store({ event, state }, () => options.hooks.handleError(input));
91
+ result = with_request_store({ event, state }, () => hooks.handleError(input));
94
92
  } catch (hook_error) {
95
93
  log_handle_error_hook_failure(error, hook_error);
96
94
  return { status: fallback.status, message: 'Internal Error' };
@@ -141,11 +139,10 @@ function log_handle_error_hook_failure(error, hook_error) {
141
139
  /**
142
140
  * Return as a response that renders the error.html
143
141
  *
144
- * @param {import('types').SSROptions} options
145
142
  * @param {number} status
146
143
  * @param {string} message
147
144
  */
148
- export function static_error_page(options, status, message) {
145
+ export function static_error_page(status, message) {
149
146
  let page = options.templates.error({ status, message: escape_html(message) });
150
147
 
151
148
  if (__SVELTEKIT_DEV__) {
@@ -2,14 +2,13 @@ import { parseSetCookie } from 'cookie';
2
2
  import { noop } from '../../utils/functions.js';
3
3
  import { respond } from './respond.js';
4
4
  import * as paths from '#app/paths';
5
- import { read_implementation } from './internal.js';
5
+ import { hooks, read_implementation } from './internal.js';
6
6
  import { has_prerendered_path } from './utils.js';
7
7
  import { fork_state_for_subrequest } from './state.js';
8
8
 
9
9
  /**
10
10
  * @param {{
11
11
  * event: import('@sveltejs/kit').RequestEvent;
12
- * options: import('types').SSROptions;
13
12
  * manifest: import('@sveltejs/kit').SSRManifest;
14
13
  * state: import('types').RequestState;
15
14
  * get_cookie_header: (url: URL, header: string | null) => string;
@@ -17,7 +16,7 @@ import { fork_state_for_subrequest } from './state.js';
17
16
  * }} opts
18
17
  * @returns {typeof fetch}
19
18
  */
20
- export function create_fetch({ event, options, manifest, state, get_cookie_header, set_internal }) {
19
+ export function create_fetch({ event, manifest, state, get_cookie_header, set_internal }) {
21
20
  /**
22
21
  * @type {typeof fetch}
23
22
  */
@@ -30,7 +29,7 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade
30
29
  let credentials =
31
30
  (info instanceof Request ? info.credentials : init?.credentials) ?? 'same-origin';
32
31
 
33
- return options.hooks.handleFetch({
32
+ return hooks.handleFetch({
34
33
  event,
35
34
  request: original_request,
36
35
  fetch: async (info, init) => {
@@ -148,18 +147,19 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade
148
147
  request.headers.set('accept-language', accept_language);
149
148
  }
150
149
 
151
- const response = await internal_fetch(request, options, manifest, state);
150
+ const response = await internal_fetch(request, manifest, state);
152
151
 
153
152
  for (const str of response.headers.getSetCookie()) {
154
- const { name, value, ...options } = parseSetCookie(str, { decode: (v) => v });
153
+ const { name, value, ...cookie_options } = parseSetCookie(str, { decode: (v) => v });
155
154
 
156
- const path = options.path ?? (url.pathname.split('/').slice(0, -1).join('/') || '/');
155
+ const path =
156
+ cookie_options.path ?? (url.pathname.split('/').slice(0, -1).join('/') || '/');
157
157
 
158
- // options.sameSite is string, something more specific is required - type cast is safe
158
+ // sameSite is string, something more specific is required - type cast is safe
159
159
  set_internal(name, /** @type {string} */ (value), {
160
160
  path,
161
161
  encode: (value) => value,
162
- .../** @type {import('cookie').SerializeOptions} */ (options)
162
+ .../** @type {import('cookie').SerializeOptions} */ (cookie_options)
163
163
  });
164
164
  }
165
165
 
@@ -193,12 +193,11 @@ function normalize_fetch_input(info, init, url) {
193
193
 
194
194
  /**
195
195
  * @param {Request} request
196
- * @param {import('types').SSROptions} options
197
196
  * @param {import('@sveltejs/kit').SSRManifest} manifest
198
197
  * @param {import('types').RequestState} state
199
198
  * @returns {Promise<Response>}
200
199
  */
201
- async function internal_fetch(request, options, manifest, state) {
200
+ async function internal_fetch(request, manifest, state) {
202
201
  if (request.signal?.aborted) {
203
202
  throw new DOMException('The operation was aborted.', 'AbortError');
204
203
  }
@@ -206,7 +205,7 @@ async function internal_fetch(request, options, manifest, state) {
206
205
  const subrequest_state = fork_state_for_subrequest(state);
207
206
 
208
207
  if (!request.signal) {
209
- return await respond(request, options, manifest, subrequest_state);
208
+ return await respond(request, manifest, subrequest_state);
210
209
  }
211
210
 
212
211
  let remove_abort_listener = noop;
@@ -219,8 +218,7 @@ async function internal_fetch(request, options, manifest, state) {
219
218
  remove_abort_listener = () => request.signal.removeEventListener('abort', on_abort);
220
219
  });
221
220
 
222
- return Promise.race([
223
- respond(request, options, manifest, subrequest_state),
224
- abort_promise
225
- ]).finally(remove_abort_listener);
221
+ return Promise.race([respond(request, manifest, subrequest_state), abort_promise]).finally(
222
+ remove_abort_listener
223
+ );
226
224
  }
@@ -1,14 +1,24 @@
1
1
  import { noop } from '../../utils/functions.js';
2
- import { IN_WEBCONTAINER } from '../../constants.js';
2
+ import { stream_from_iterable } from '../utils.js';
3
+ import { IN_WEBCONTAINER, REROUTED_URL_HEADER } from '../../constants.js';
3
4
  import { respond } from './respond.js';
4
5
  import { create_request_state } from './state.js';
5
- import { options, get_hooks } from '__SERVER__/internal.js';
6
- import { set_read_implementation, set_manifest, fix_stack_trace } from './internal.js';
7
- import { set_env } from '__sveltekit/env';
6
+ import { options, get_hooks } from '<sveltekit:generated>/server.js';
7
+ import {
8
+ set_read_implementation,
9
+ set_manifest,
10
+ set_options,
11
+ set_hooks,
12
+ fix_stack_trace
13
+ } from './internal.js';
14
+ import { set_env } from '<sveltekit:generated>/env/config.js';
8
15
  import { init_tracing } from '@sveltejs/kit/internal/server';
9
16
  import { DEV } from 'esm-env';
10
17
  import { init_transport } from '#app/internal/transport';
11
18
 
19
+ // set at module scope because prerendering evaluates user modules before constructing a `Server`
20
+ set_options(options);
21
+
12
22
  /** @type {Promise<any>} */
13
23
  let init_promise;
14
24
 
@@ -48,16 +58,11 @@ if (DEV) {
48
58
  }
49
59
 
50
60
  export class Server {
51
- /** @type {import('types').SSROptions} */
52
- #options;
53
-
54
61
  /** @type {import('@sveltejs/kit').SSRManifest} */
55
62
  #manifest;
56
63
 
57
64
  /** @param {import('@sveltejs/kit').SSRManifest} manifest */
58
65
  constructor(manifest) {
59
- /** @type {import('types').SSROptions} */
60
- this.#options = options;
61
66
  this.#manifest = manifest;
62
67
 
63
68
  // Since AsyncLocalStorage is not working in webcontainers, we don't reset `sync_store`
@@ -105,14 +110,11 @@ export class Server {
105
110
  return result;
106
111
  }
107
112
 
108
- // TODO remove the cast once TypeScript's lib includes `ReadableStream.from`
109
- return /** @type {ReadableStream} */ (
110
- /** @type {any} */ (ReadableStream).from(
111
- (async function* () {
112
- const stream = await result;
113
- if (stream) yield* stream;
114
- })()
115
- )
113
+ return stream_from_iterable(
114
+ (async function* () {
115
+ const stream = await result;
116
+ if (stream) yield* stream;
117
+ })()
116
118
  );
117
119
  };
118
120
 
@@ -125,7 +127,7 @@ export class Server {
125
127
  try {
126
128
  const module = await get_hooks();
127
129
 
128
- this.#options.hooks = {
130
+ set_hooks({
129
131
  handle: module.handle || (({ event, resolve }) => resolve(event)),
130
132
  handleError:
131
133
  module.handleError ||
@@ -154,7 +156,7 @@ export class Server {
154
156
  }),
155
157
  handleFetch: module.handleFetch || (({ request, fetch }) => fetch(request)),
156
158
  reroute: module.reroute || noop
157
- };
159
+ });
158
160
 
159
161
  init_transport(module.transport ?? {});
160
162
 
@@ -163,14 +165,14 @@ export class Server {
163
165
  }
164
166
  } catch (e) {
165
167
  if (__SVELTEKIT_DEV__) {
166
- this.#options.hooks = {
168
+ set_hooks({
167
169
  handle: () => {
168
170
  throw e;
169
171
  },
170
172
  handleError: ({ error }) => console.error(error),
171
173
  handleFetch: ({ request, fetch }) => fetch(request),
172
174
  reroute: noop
173
- };
175
+ });
174
176
  } else {
175
177
  throw e;
176
178
  }
@@ -183,18 +185,19 @@ export class Server {
183
185
  * @param {import('types').InternalRequestOptions} options
184
186
  */
185
187
  async respond(request, options) {
186
- const response = await respond(
187
- request,
188
- this.#options,
189
- this.#manifest,
190
- create_request_state(options)
191
- );
188
+ const request_state = create_request_state(options);
189
+
190
+ const response = await respond(request, this.#manifest, request_state);
192
191
 
193
192
  if (DEV) {
194
193
  const error = decoded_responses.get(response);
195
194
  if (error) console.error(fix_stack_trace(error));
196
195
  }
197
196
 
197
+ if (request_state.rerouted_url) {
198
+ response.headers.set(REROUTED_URL_HEADER, request_state.rerouted_url);
199
+ }
200
+
198
201
  return response;
199
202
  }
200
203
  }
@@ -1,4 +1,5 @@
1
1
  /** @import { SSRManifest } from '@sveltejs/kit'; */
2
+ /** @import { ServerHooks, SSROptions } from 'types'; */
2
3
  import { restore, save } from './dev.js';
3
4
  import {
4
5
  has_data_suffix,
@@ -16,6 +17,8 @@ const styleText =
16
17
 
17
18
  const read_implementation_key = Symbol.for('sveltekit.read_implementation');
18
19
  const manifest_key = Symbol.for('sveltekit.manifest');
20
+ const options_key = Symbol.for('sveltekit.options');
21
+ const hooks_key = Symbol.for('sveltekit.hooks');
19
22
 
20
23
  export let read_implementation = /** @type {((path: string) => ReadableStream<any>) | null} */ (
21
24
  (__SVELTEKIT_DEV__ && restore(read_implementation_key)) ?? null
@@ -25,6 +28,12 @@ export let manifest = /** @type {SSRManifest} */ (
25
28
  (__SVELTEKIT_DEV__ && restore(manifest_key)) ?? null
26
29
  );
27
30
 
31
+ export let options = /** @type {SSROptions} */ (
32
+ (__SVELTEKIT_DEV__ && restore(options_key)) ?? null
33
+ );
34
+
35
+ export let hooks = /** @type {ServerHooks} */ ((__SVELTEKIT_DEV__ && restore(hooks_key)) ?? null);
36
+
28
37
  /**
29
38
  * @param {(path: string) => ReadableStream<any>} fn
30
39
  */
@@ -42,11 +51,28 @@ export function set_manifest(value) {
42
51
  if (__SVELTEKIT_DEV__) save(manifest_key, value);
43
52
  }
44
53
 
54
+ /**
55
+ * @param {SSROptions} value
56
+ */
57
+ export function set_options(value) {
58
+ options = value;
59
+ if (__SVELTEKIT_DEV__) save(options_key, value);
60
+ }
61
+
62
+ /**
63
+ * @param {ServerHooks} value
64
+ */
65
+ export function set_hooks(value) {
66
+ hooks = value;
67
+ if (__SVELTEKIT_DEV__) save(hooks_key, value);
68
+ }
69
+
45
70
  /**
46
71
  * @param {number} status
47
72
  * @param {Request} request
73
+ * @returns {string}
48
74
  */
49
- export function log_response(status, request) {
75
+ export function format_response(status, request) {
50
76
  const url = new URL(request.url);
51
77
  const requested = url.href.replace(url.origin, '');
52
78
 
@@ -59,13 +85,17 @@ export function log_response(status, request) {
59
85
  const pathname = strip_resolution_suffix(url.pathname) || '/';
60
86
  log += pathname + styleText('dim', requested.slice(pathname.length));
61
87
  } else if (has_remote_prefix(url)) {
62
- const id = /** @type {string} */ (strip_remote_prefix(url).split('/').pop());
63
- log += styleText('dim', url.pathname.slice(0, -id.length)) + id + styleText('dim', url.search);
88
+ const id = /** @type {string} */ (strip_remote_prefix(url));
89
+ const [file_hash, name, arg_hash] = id.split('/');
90
+
91
+ log += styleText('dim', `${url.pathname.slice(0, -id.length)}${file_hash}/`) + name;
92
+ if (arg_hash) log += styleText('dim', `/${arg_hash}`);
93
+ if (url.search) log += styleText('dim', url.search);
64
94
  } else {
65
95
  log += requested;
66
96
  }
67
97
 
68
- console.log(log);
98
+ return log;
69
99
  }
70
100
 
71
101
  export { fix_stack_trace, set_fix_stack_trace } from './sourcemaps.js';
@@ -1,8 +1,7 @@
1
1
  /** @import { RequestEvent, Actions } from '@sveltejs/kit' */
2
2
  /** @import { ActionResult } from '$app/forms' */
3
- /** @import { SSROptions, SSRNode, ServerNode } from 'types' */
3
+ /** @import { SSRNode, ServerNode, ServerActionResult } from 'types' */
4
4
  import { DEV } from 'esm-env';
5
- import { json } from '@sveltejs/kit';
6
5
  import { HttpError, Redirect, ActionFailure, SvelteKitError } from '@sveltejs/kit/internal';
7
6
  import { with_request_store, merge_tracing, record_span } from '@sveltejs/kit/internal/server';
8
7
  import { normalize_error } from '../../../utils/error.js';
@@ -24,102 +23,46 @@ export function is_action_json_request(event) {
24
23
  /**
25
24
  * @param {RequestEvent} event
26
25
  * @param {import('types').RequestState} state
27
- * @param {SSROptions} options
28
26
  * @param {SSRNode['server'] | undefined} server
29
27
  */
30
- export async function handle_action_json_request(event, state, options, server) {
31
- const actions = server?.actions;
32
- const location = get_action_location(event.url);
33
-
34
- if (!actions) {
35
- const no_actions_error = new SvelteKitError(
36
- 405,
37
- 'Method Not Allowed',
38
- `POST method not allowed. No form actions exist for ${DEV ? `the page at ${event.route.id}` : 'this page'}`
39
- );
28
+ export async function handle_action_json_request(event, state, server) {
29
+ const result = await handle_action_request(event, state, server);
30
+ return action_result_json(event, state, result);
31
+ }
40
32
 
41
- const error = await handle_error_and_jsonify(event, state, options, no_actions_error);
33
+ /**
34
+ * @param {RequestEvent} event
35
+ * @param {import('types').RequestState} state
36
+ * @param {ServerActionResult} result
37
+ * @returns {Promise<Response>}
38
+ */
39
+ async function action_result_json(event, state, result) {
40
+ if (result.type === 'redirect') {
41
+ return action_json(result);
42
+ }
42
43
 
43
- return action_json(
44
- {
45
- type: 'error',
46
- error,
47
- location
48
- },
49
- {
50
- status: error.status,
51
- headers: {
52
- // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
53
- // "The server must generate an Allow header field in a 405 status code response"
54
- allow: 'GET'
55
- }
56
- }
57
- );
44
+ if (result.type === 'error') {
45
+ const error = await handle_error_and_jsonify(event, state, result.error);
46
+ return action_json({ ...result, error }, { status: error.status });
58
47
  }
59
48
 
60
- check_named_default_separate(actions);
49
+ if (result.type === 'success' && !result.data) {
50
+ return action_json({ ...result, status: 204, data: undefined });
51
+ }
61
52
 
62
53
  try {
63
- const data = await call_action(event, state, actions);
64
-
65
- if (DEV) {
66
- validate_action_return(data);
67
- }
68
-
69
- if (data instanceof ActionFailure) {
70
- return action_json(
71
- {
72
- type: 'failure',
73
- status: data.status,
74
- location,
75
- // @ts-expect-error we assign a string to what is supposed to be an object. That's ok
76
- // because we don't use the object outside, and this way we have better code navigation
77
- // through knowing where the related interface is used.
78
- data: try_serialize(data.data, stringify, /** @type {string} */ (event.route.id))
79
- },
80
- {
81
- status: data.status
82
- }
83
- );
84
- } else if (data) {
85
- return action_json({
86
- type: 'success',
87
- status: 200,
88
- location,
89
- // @ts-expect-error see comment above
90
- data: try_serialize(data, stringify, /** @type {string} */ (event.route.id))
91
- });
92
- } else {
93
- return action_json({
94
- type: 'success',
95
- status: 204,
96
- location
97
- });
98
- }
99
- } catch (e) {
100
- const err = normalize_error(e);
101
-
102
- if (err instanceof Redirect) {
103
- return action_json_redirect(err);
104
- }
105
-
106
- const transformed = await handle_error_and_jsonify(
107
- event,
108
- state,
109
- options,
110
- check_incorrect_fail_use(err)
111
- );
112
-
113
54
  return action_json(
114
55
  {
115
- type: 'error',
116
- error: transformed,
117
- location
56
+ ...result,
57
+ // @ts-expect-error we assign a string to what is supposed to be an object. That's ok
58
+ // because we don't use the object outside, and this way we have better code navigation
59
+ // through knowing where the related interface is used.
60
+ data: try_serialize(result.data, stringify, /** @type {string} */ (event.route.id))
118
61
  },
119
- {
120
- status: transformed.status
121
- }
62
+ { status: result.status }
122
63
  );
64
+ } catch (e) {
65
+ return action_result_json(event, state, action_error_result(e, result.location));
123
66
  }
124
67
  }
125
68
 
@@ -140,12 +83,48 @@ export function get_action_location(url) {
140
83
  }
141
84
 
142
85
  /**
143
- * @param {HttpError | Error} error
86
+ * @param {RequestEvent} event
87
+ * @param {string} location
88
+ * @returns {Extract<ServerActionResult, { type: 'error' }>}
89
+ */
90
+ export function method_not_allowed_result(event, location) {
91
+ event.setHeaders({
92
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
93
+ // "The server must generate an Allow header field in a 405 status code response"
94
+ allow: 'GET'
95
+ });
96
+ return {
97
+ type: 'error',
98
+ location,
99
+ error: new SvelteKitError(
100
+ 405,
101
+ 'Method Not Allowed',
102
+ `POST method not allowed. No form actions exist for ${DEV ? `the page at ${event.route.id}` : 'this page'}`
103
+ )
104
+ };
105
+ }
106
+
107
+ /**
108
+ * @param {unknown} e
109
+ * @param {string} location
110
+ * @returns {Extract<ServerActionResult, { type: 'redirect' | 'error' }>}
144
111
  */
145
- export function check_incorrect_fail_use(error) {
146
- return error instanceof ActionFailure
147
- ? new Error('Cannot "throw fail()". Use "return fail()"')
148
- : error;
112
+ export function action_error_result(e, location) {
113
+ const err = normalize_error(e);
114
+
115
+ if (err instanceof Redirect) {
116
+ return {
117
+ type: 'redirect',
118
+ status: err.status,
119
+ location: err.location
120
+ };
121
+ }
122
+
123
+ return {
124
+ type: 'error',
125
+ location,
126
+ error: err
127
+ };
149
128
  }
150
129
 
151
130
  /**
@@ -164,7 +143,7 @@ export function action_json_redirect(redirect) {
164
143
  * @param {ResponseInit} [init]
165
144
  */
166
145
  function action_json(data, init) {
167
- return with_version_header(json(data, init));
146
+ return with_version_header(Response.json(data, init));
168
147
  }
169
148
 
170
149
  /**
@@ -178,7 +157,7 @@ export function is_action_request(event) {
178
157
  * @param {RequestEvent} event
179
158
  * @param {import('types').RequestState} state
180
159
  * @param {SSRNode['server'] | undefined} server
181
- * @returns {Promise<ActionResult>}
160
+ * @returns {Promise<ServerActionResult>}
182
161
  */
183
162
  export async function handle_action_request(event, state, server) {
184
163
  const actions = server?.actions;
@@ -186,21 +165,7 @@ export async function handle_action_request(event, state, server) {
186
165
 
187
166
  if (!actions) {
188
167
  // TODO should this be a different error altogether?
189
- event.setHeaders({
190
- // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
191
- // "The server must generate an Allow header field in a 405 status code response"
192
- allow: 'GET'
193
- });
194
- return {
195
- type: 'error',
196
- location,
197
- // We're lying a bit with the types here; this will be transformed into a proper App.Error object later
198
- error: new SvelteKitError(
199
- 405,
200
- 'Method Not Allowed',
201
- `POST method not allowed. No form actions exist for ${DEV ? `the page at ${event.route.id}` : 'this page'}`
202
- )
203
- };
168
+ return method_not_allowed_result(event, location);
204
169
  }
205
170
 
206
171
  check_named_default_separate(actions);
@@ -229,22 +194,10 @@ export async function handle_action_request(event, state, server) {
229
194
  };
230
195
  }
231
196
  } catch (e) {
232
- const err = normalize_error(e);
233
-
234
- if (err instanceof Redirect) {
235
- return {
236
- type: 'redirect',
237
- status: err.status,
238
- location: err.location
239
- };
240
- }
241
-
242
- return {
243
- type: 'error',
244
- location,
245
- // @ts-expect-error We're lying a bit with the types here; this will be transformed into a proper App.Error object later
246
- error: check_incorrect_fail_use(err)
247
- };
197
+ return action_error_result(
198
+ e instanceof ActionFailure ? new Error('Cannot "throw fail()". Use "return fail()"') : e,
199
+ location
200
+ );
248
201
  }
249
202
  }
250
203