@sveltejs/kit 1.0.0-next.99 → 1.0.1

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 (141) hide show
  1. package/README.md +5 -1
  2. package/package.json +91 -77
  3. package/postinstall.js +47 -0
  4. package/src/cli.js +44 -0
  5. package/src/constants.js +5 -0
  6. package/src/core/adapt/builder.js +221 -0
  7. package/src/core/adapt/index.js +31 -0
  8. package/src/core/config/default-error.html +56 -0
  9. package/src/core/config/index.js +100 -0
  10. package/src/core/config/options.js +387 -0
  11. package/src/core/config/types.d.ts +1 -0
  12. package/src/core/env.js +138 -0
  13. package/src/core/generate_manifest/index.js +116 -0
  14. package/src/core/prerender/crawl.js +207 -0
  15. package/src/core/prerender/entities.js +2252 -0
  16. package/src/core/prerender/fallback.js +43 -0
  17. package/src/core/prerender/prerender.js +459 -0
  18. package/src/core/prerender/queue.js +80 -0
  19. package/src/core/sync/create_manifest_data/conflict.js +0 -0
  20. package/src/core/sync/create_manifest_data/index.js +523 -0
  21. package/src/core/sync/create_manifest_data/sort.js +161 -0
  22. package/src/core/sync/create_manifest_data/types.d.ts +37 -0
  23. package/src/core/sync/sync.js +59 -0
  24. package/src/core/sync/utils.js +33 -0
  25. package/src/core/sync/write_ambient.js +58 -0
  26. package/src/core/sync/write_client_manifest.js +107 -0
  27. package/src/core/sync/write_matchers.js +25 -0
  28. package/src/core/sync/write_root.js +91 -0
  29. package/src/core/sync/write_tsconfig.js +195 -0
  30. package/src/core/sync/write_types/index.js +809 -0
  31. package/src/core/utils.js +67 -0
  32. package/src/exports/hooks/index.js +1 -0
  33. package/src/exports/hooks/sequence.js +44 -0
  34. package/src/exports/index.js +55 -0
  35. package/src/exports/node/index.js +172 -0
  36. package/src/exports/node/polyfills.js +28 -0
  37. package/src/exports/vite/build/build_server.js +359 -0
  38. package/src/exports/vite/build/build_service_worker.js +85 -0
  39. package/src/exports/vite/build/utils.js +230 -0
  40. package/src/exports/vite/dev/index.js +597 -0
  41. package/src/exports/vite/graph_analysis/index.js +99 -0
  42. package/src/exports/vite/graph_analysis/types.d.ts +5 -0
  43. package/src/exports/vite/graph_analysis/utils.js +6 -0
  44. package/src/exports/vite/index.js +708 -0
  45. package/src/exports/vite/preview/index.js +194 -0
  46. package/src/exports/vite/types.d.ts +3 -0
  47. package/src/exports/vite/utils.js +184 -0
  48. package/src/runtime/app/env.js +1 -0
  49. package/src/runtime/app/environment.js +13 -0
  50. package/src/runtime/app/forms.js +135 -0
  51. package/src/runtime/app/navigation.js +22 -0
  52. package/src/runtime/app/paths.js +1 -0
  53. package/src/runtime/app/stores.js +57 -0
  54. package/src/runtime/client/ambient.d.ts +30 -0
  55. package/src/runtime/client/client.js +1725 -0
  56. package/src/runtime/client/constants.js +10 -0
  57. package/src/runtime/client/fetcher.js +127 -0
  58. package/src/runtime/client/parse.js +60 -0
  59. package/src/runtime/client/singletons.js +21 -0
  60. package/src/runtime/client/start.js +45 -0
  61. package/src/runtime/client/types.d.ts +86 -0
  62. package/src/runtime/client/utils.js +257 -0
  63. package/src/runtime/components/error.svelte +6 -0
  64. package/{assets → src/runtime}/components/layout.svelte +0 -0
  65. package/src/runtime/control.js +45 -0
  66. package/src/runtime/env/dynamic/private.js +1 -0
  67. package/src/runtime/env/dynamic/public.js +1 -0
  68. package/src/runtime/env-private.js +6 -0
  69. package/src/runtime/env-public.js +6 -0
  70. package/src/runtime/env.js +12 -0
  71. package/src/runtime/hash.js +20 -0
  72. package/src/runtime/paths.js +11 -0
  73. package/src/runtime/server/cookie.js +228 -0
  74. package/src/runtime/server/data/index.js +158 -0
  75. package/src/runtime/server/endpoint.js +86 -0
  76. package/src/runtime/server/fetch.js +175 -0
  77. package/src/runtime/server/index.js +405 -0
  78. package/src/runtime/server/page/actions.js +267 -0
  79. package/src/runtime/server/page/crypto.js +239 -0
  80. package/src/runtime/server/page/csp.js +250 -0
  81. package/src/runtime/server/page/index.js +326 -0
  82. package/src/runtime/server/page/load_data.js +270 -0
  83. package/src/runtime/server/page/render.js +393 -0
  84. package/src/runtime/server/page/respond_with_error.js +103 -0
  85. package/src/runtime/server/page/serialize_data.js +87 -0
  86. package/src/runtime/server/page/types.d.ts +35 -0
  87. package/src/runtime/server/utils.js +179 -0
  88. package/src/utils/array.js +9 -0
  89. package/src/utils/error.js +22 -0
  90. package/src/utils/escape.js +46 -0
  91. package/src/utils/exports.js +54 -0
  92. package/src/utils/filesystem.js +178 -0
  93. package/src/utils/functions.js +16 -0
  94. package/src/utils/http.js +72 -0
  95. package/src/utils/misc.js +1 -0
  96. package/src/utils/promises.js +17 -0
  97. package/src/utils/routing.js +201 -0
  98. package/src/utils/unit_test.js +11 -0
  99. package/src/utils/url.js +161 -0
  100. package/svelte-kit.js +1 -1
  101. package/types/ambient.d.ts +451 -0
  102. package/types/index.d.ts +1168 -5
  103. package/types/internal.d.ts +348 -159
  104. package/types/private.d.ts +237 -0
  105. package/types/synthetic/$env+dynamic+private.md +10 -0
  106. package/types/synthetic/$env+dynamic+public.md +8 -0
  107. package/types/synthetic/$env+static+private.md +19 -0
  108. package/types/synthetic/$env+static+public.md +7 -0
  109. package/types/synthetic/$lib.md +5 -0
  110. package/CHANGELOG.md +0 -825
  111. package/assets/components/error.svelte +0 -21
  112. package/assets/runtime/app/env.js +0 -16
  113. package/assets/runtime/app/navigation.js +0 -53
  114. package/assets/runtime/app/paths.js +0 -1
  115. package/assets/runtime/app/stores.js +0 -87
  116. package/assets/runtime/chunks/utils.js +0 -13
  117. package/assets/runtime/env.js +0 -8
  118. package/assets/runtime/internal/singletons.js +0 -20
  119. package/assets/runtime/internal/start.js +0 -1061
  120. package/assets/runtime/paths.js +0 -12
  121. package/dist/chunks/_commonjsHelpers.js +0 -8
  122. package/dist/chunks/cert.js +0 -29079
  123. package/dist/chunks/constants.js +0 -3
  124. package/dist/chunks/index.js +0 -3532
  125. package/dist/chunks/index2.js +0 -583
  126. package/dist/chunks/index3.js +0 -31
  127. package/dist/chunks/index4.js +0 -1004
  128. package/dist/chunks/index5.js +0 -327
  129. package/dist/chunks/index6.js +0 -325
  130. package/dist/chunks/standard.js +0 -99
  131. package/dist/chunks/utils.js +0 -149
  132. package/dist/cli.js +0 -711
  133. package/dist/http.js +0 -66
  134. package/dist/install-fetch.js +0 -1699
  135. package/dist/ssr.js +0 -1523
  136. package/types/ambient-modules.d.ts +0 -115
  137. package/types/config.d.ts +0 -101
  138. package/types/endpoint.d.ts +0 -23
  139. package/types/helper.d.ts +0 -19
  140. package/types/hooks.d.ts +0 -21
  141. package/types/page.d.ts +0 -30
@@ -0,0 +1,405 @@
1
+ import { DEV } from 'esm-env';
2
+ import { is_endpoint_request, render_endpoint } from './endpoint.js';
3
+ import { render_page } from './page/index.js';
4
+ import { render_response } from './page/render.js';
5
+ import { respond_with_error } from './page/respond_with_error.js';
6
+ import { is_form_content_type } from '../../utils/http.js';
7
+ import { GENERIC_ERROR, get_option, handle_fatal_error, redirect_response } from './utils.js';
8
+ import {
9
+ decode_pathname,
10
+ decode_params,
11
+ disable_search,
12
+ has_data_suffix,
13
+ normalize_path,
14
+ strip_data_suffix
15
+ } from '../../utils/url.js';
16
+ import { exec } from '../../utils/routing.js';
17
+ import { INVALIDATED_PARAM, redirect_json_response, render_data } from './data/index.js';
18
+ import { add_cookies_to_headers, get_cookies } from './cookie.js';
19
+ import { create_fetch } from './fetch.js';
20
+ import { Redirect } from '../control.js';
21
+ import {
22
+ validate_common_exports,
23
+ validate_page_server_exports,
24
+ validate_server_exports
25
+ } from '../../utils/exports.js';
26
+ import { error, json } from '../../exports/index.js';
27
+
28
+ /* global __SVELTEKIT_ADAPTER_NAME__ */
29
+
30
+ /** @type {import('types').RequiredResolveOptions['transformPageChunk']} */
31
+ const default_transform = ({ html }) => html;
32
+
33
+ /** @type {import('types').RequiredResolveOptions['filterSerializedResponseHeaders']} */
34
+ const default_filter = () => false;
35
+
36
+ /** @type {import('types').RequiredResolveOptions['preload']} */
37
+ const default_preload = ({ type }) => type === 'js' || type === 'css';
38
+
39
+ /** @type {import('types').Respond} */
40
+ export async function respond(request, options, state) {
41
+ /** URL but stripped from the potential `/__data.json` suffix and its search param */
42
+ let url = new URL(request.url);
43
+
44
+ if (options.csrf.check_origin) {
45
+ const forbidden =
46
+ request.method === 'POST' &&
47
+ request.headers.get('origin') !== url.origin &&
48
+ is_form_content_type(request);
49
+
50
+ if (forbidden) {
51
+ const csrf_error = error(403, `Cross-site ${request.method} form submissions are forbidden`);
52
+ if (request.headers.get('accept') === 'application/json') {
53
+ return json(csrf_error.body, { status: csrf_error.status });
54
+ }
55
+ return new Response(csrf_error.body.message, { status: csrf_error.status });
56
+ }
57
+ }
58
+
59
+ let decoded;
60
+ try {
61
+ decoded = decode_pathname(url.pathname);
62
+ } catch {
63
+ return new Response('Malformed URI', { status: 400 });
64
+ }
65
+
66
+ /** @type {import('types').SSRRoute | null} */
67
+ let route = null;
68
+
69
+ /** @type {Record<string, string>} */
70
+ let params = {};
71
+
72
+ if (options.paths.base && !state.prerendering?.fallback) {
73
+ if (!decoded.startsWith(options.paths.base)) {
74
+ return new Response('Not found', { status: 404 });
75
+ }
76
+ decoded = decoded.slice(options.paths.base.length) || '/';
77
+ }
78
+
79
+ const is_data_request = has_data_suffix(decoded);
80
+ /** @type {boolean[] | undefined} */
81
+ let invalidated_data_nodes;
82
+ if (is_data_request) {
83
+ decoded = strip_data_suffix(decoded) || '/';
84
+ url.pathname = strip_data_suffix(url.pathname) || '/';
85
+ invalidated_data_nodes = url.searchParams.get(INVALIDATED_PARAM)?.split('_').map(Boolean);
86
+ url.searchParams.delete(INVALIDATED_PARAM);
87
+ }
88
+
89
+ if (!state.prerendering?.fallback) {
90
+ // TODO this could theoretically break — should probably be inside a try-catch
91
+ const matchers = await options.manifest._.matchers();
92
+
93
+ for (const candidate of options.manifest._.routes) {
94
+ const match = candidate.pattern.exec(decoded);
95
+ if (!match) continue;
96
+
97
+ const matched = exec(match, candidate.params, matchers);
98
+ if (matched) {
99
+ route = candidate;
100
+ params = decode_params(matched);
101
+ break;
102
+ }
103
+ }
104
+ }
105
+
106
+ /** @type {import('types').TrailingSlash | void} */
107
+ let trailing_slash = undefined;
108
+
109
+ /** @type {Record<string, string>} */
110
+ const headers = {};
111
+
112
+ /** @type {import('types').RequestEvent} */
113
+ const event = {
114
+ // @ts-expect-error `cookies` and `fetch` need to be created after the `event` itself
115
+ cookies: null,
116
+ // @ts-expect-error
117
+ fetch: null,
118
+ getClientAddress:
119
+ state.getClientAddress ||
120
+ (() => {
121
+ throw new Error(
122
+ `${__SVELTEKIT_ADAPTER_NAME__} does not specify getClientAddress. Please raise an issue`
123
+ );
124
+ }),
125
+ locals: {},
126
+ params,
127
+ platform: state.platform,
128
+ request,
129
+ route: { id: route?.id ?? null },
130
+ setHeaders: (new_headers) => {
131
+ for (const key in new_headers) {
132
+ const lower = key.toLowerCase();
133
+ const value = new_headers[key];
134
+
135
+ if (lower === 'set-cookie') {
136
+ throw new Error(
137
+ `Use \`event.cookies.set(name, value, options)\` instead of \`event.setHeaders\` to set cookies`
138
+ );
139
+ } else if (lower in headers) {
140
+ throw new Error(`"${key}" header is already set`);
141
+ } else {
142
+ headers[lower] = value;
143
+
144
+ if (state.prerendering && lower === 'cache-control') {
145
+ state.prerendering.cache = /** @type {string} */ (value);
146
+ }
147
+ }
148
+ }
149
+ },
150
+ url,
151
+ isDataRequest: is_data_request
152
+ };
153
+
154
+ /** @type {import('types').RequiredResolveOptions} */
155
+ let resolve_opts = {
156
+ transformPageChunk: default_transform,
157
+ filterSerializedResponseHeaders: default_filter,
158
+ preload: default_preload
159
+ };
160
+
161
+ try {
162
+ // determine whether we need to redirect to add/remove a trailing slash
163
+ if (route && !is_data_request) {
164
+ if (route.page) {
165
+ const nodes = await Promise.all([
166
+ // we use == here rather than === because [undefined] serializes as "[null]"
167
+ ...route.page.layouts.map((n) => (n == undefined ? n : options.manifest._.nodes[n]())),
168
+ options.manifest._.nodes[route.page.leaf]()
169
+ ]);
170
+
171
+ if (DEV) {
172
+ const layouts = nodes.slice(0, -1);
173
+ const page = nodes.at(-1);
174
+
175
+ for (const layout of layouts) {
176
+ if (layout) {
177
+ validate_common_exports(layout.server, /** @type {string} */ (layout.server_id));
178
+ validate_common_exports(
179
+ layout.universal,
180
+ /** @type {string} */ (layout.universal_id)
181
+ );
182
+ }
183
+ }
184
+
185
+ if (page) {
186
+ validate_page_server_exports(page.server, /** @type {string} */ (page.server_id));
187
+ validate_common_exports(page.universal, /** @type {string} */ (page.universal_id));
188
+ }
189
+ }
190
+
191
+ trailing_slash = get_option(nodes, 'trailingSlash');
192
+ } else if (route.endpoint) {
193
+ const node = await route.endpoint();
194
+ trailing_slash = node.trailingSlash;
195
+
196
+ if (DEV) {
197
+ validate_server_exports(node, /** @type {string} */ (route.endpoint_id));
198
+ }
199
+ }
200
+
201
+ const normalized = normalize_path(url.pathname, trailing_slash ?? 'never');
202
+
203
+ if (normalized !== url.pathname && !state.prerendering?.fallback) {
204
+ return new Response(undefined, {
205
+ status: 301,
206
+ headers: {
207
+ 'x-sveltekit-normalize': '1',
208
+ location:
209
+ // ensure paths starting with '//' are not treated as protocol-relative
210
+ (normalized.startsWith('//') ? url.origin + normalized : normalized) +
211
+ (url.search === '?' ? '' : url.search)
212
+ }
213
+ });
214
+ }
215
+ }
216
+
217
+ const { cookies, new_cookies, get_cookie_header } = get_cookies(
218
+ request,
219
+ url,
220
+ options.dev,
221
+ trailing_slash ?? 'never'
222
+ );
223
+
224
+ event.cookies = cookies;
225
+ event.fetch = create_fetch({ event, options, state, get_cookie_header });
226
+
227
+ if (state.prerendering && !state.prerendering.fallback) disable_search(url);
228
+
229
+ const response = await options.hooks.handle({
230
+ event,
231
+ resolve: (event, opts) =>
232
+ resolve(event, opts).then((response) => {
233
+ // add headers/cookies here, rather than inside `resolve`, so that we
234
+ // can do it once for all responses instead of once per `return`
235
+ for (const key in headers) {
236
+ const value = headers[key];
237
+ response.headers.set(key, /** @type {string} */ (value));
238
+ }
239
+
240
+ add_cookies_to_headers(response.headers, Object.values(new_cookies));
241
+
242
+ if (state.prerendering && event.route.id !== null) {
243
+ response.headers.set('x-sveltekit-routeid', encodeURI(event.route.id));
244
+ }
245
+
246
+ return response;
247
+ })
248
+ });
249
+
250
+ // respond with 304 if etag matches
251
+ if (response.status === 200 && response.headers.has('etag')) {
252
+ let if_none_match_value = request.headers.get('if-none-match');
253
+
254
+ // ignore W/ prefix https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match#directives
255
+ if (if_none_match_value?.startsWith('W/"')) {
256
+ if_none_match_value = if_none_match_value.substring(2);
257
+ }
258
+
259
+ const etag = /** @type {string} */ (response.headers.get('etag'));
260
+
261
+ if (if_none_match_value === etag) {
262
+ const headers = new Headers({ etag });
263
+
264
+ // https://datatracker.ietf.org/doc/html/rfc7232#section-4.1 + set-cookie
265
+ for (const key of [
266
+ 'cache-control',
267
+ 'content-location',
268
+ 'date',
269
+ 'expires',
270
+ 'vary',
271
+ 'set-cookie'
272
+ ]) {
273
+ const value = response.headers.get(key);
274
+ if (value) headers.set(key, value);
275
+ }
276
+
277
+ return new Response(undefined, {
278
+ status: 304,
279
+ headers
280
+ });
281
+ }
282
+ }
283
+
284
+ // Edge case: If user does `return Response(30x)` in handle hook while processing a data request,
285
+ // we need to transform the redirect response to a corresponding JSON response.
286
+ if (is_data_request && response.status >= 300 && response.status <= 308) {
287
+ const location = response.headers.get('location');
288
+ if (location) {
289
+ return redirect_json_response(new Redirect(/** @type {any} */ (response.status), location));
290
+ }
291
+ }
292
+
293
+ return response;
294
+ } catch (error) {
295
+ if (error instanceof Redirect) {
296
+ if (is_data_request) {
297
+ return redirect_json_response(error);
298
+ } else {
299
+ return redirect_response(error.status, error.location);
300
+ }
301
+ }
302
+ return await handle_fatal_error(event, options, error);
303
+ }
304
+
305
+ /**
306
+ *
307
+ * @param {import('types').RequestEvent} event
308
+ * @param {import('types').ResolveOptions} [opts]
309
+ */
310
+ async function resolve(event, opts) {
311
+ try {
312
+ if (opts) {
313
+ if ('ssr' in opts) {
314
+ throw new Error(
315
+ 'ssr has been removed, set it in the appropriate +layout.js instead. See the PR for more information: https://github.com/sveltejs/kit/pull/6197'
316
+ );
317
+ }
318
+
319
+ resolve_opts = {
320
+ transformPageChunk: opts.transformPageChunk || default_transform,
321
+ filterSerializedResponseHeaders: opts.filterSerializedResponseHeaders || default_filter,
322
+ preload: opts.preload || default_preload
323
+ };
324
+ }
325
+
326
+ if (state.prerendering?.fallback) {
327
+ return await render_response({
328
+ event,
329
+ options,
330
+ state,
331
+ page_config: { ssr: false, csr: true },
332
+ status: 200,
333
+ error: null,
334
+ branch: [],
335
+ fetched: [],
336
+ resolve_opts
337
+ });
338
+ }
339
+
340
+ if (route) {
341
+ /** @type {Response} */
342
+ let response;
343
+
344
+ if (is_data_request) {
345
+ response = await render_data(
346
+ event,
347
+ route,
348
+ options,
349
+ state,
350
+ invalidated_data_nodes,
351
+ trailing_slash ?? 'never'
352
+ );
353
+ } else if (route.endpoint && (!route.page || is_endpoint_request(event))) {
354
+ response = await render_endpoint(event, await route.endpoint(), state);
355
+ } else if (route.page) {
356
+ response = await render_page(event, route, route.page, options, state, resolve_opts);
357
+ } else {
358
+ // a route will always have a page or an endpoint, but TypeScript
359
+ // doesn't know that
360
+ throw new Error('This should never happen');
361
+ }
362
+
363
+ return response;
364
+ }
365
+
366
+ if (state.initiator === GENERIC_ERROR) {
367
+ return new Response('Internal Server Error', {
368
+ status: 500
369
+ });
370
+ }
371
+
372
+ // if this request came direct from the user, rather than
373
+ // via a `fetch` in a `load`, render a 404 page
374
+ if (!state.initiator) {
375
+ return await respond_with_error({
376
+ event,
377
+ options,
378
+ state,
379
+ status: 404,
380
+ error: new Error(`Not found: ${event.url.pathname}`),
381
+ resolve_opts
382
+ });
383
+ }
384
+
385
+ if (state.prerendering) {
386
+ return new Response('not found', { status: 404 });
387
+ }
388
+
389
+ // we can't load the endpoint from our own manifest,
390
+ // so we need to make an actual HTTP request
391
+ return await fetch(request);
392
+ } catch (error) {
393
+ // HttpError from endpoint can end up here - TODO should it be handled there instead?
394
+ return await handle_fatal_error(event, options, error);
395
+ } finally {
396
+ event.cookies.set = () => {
397
+ throw new Error('Cannot use `cookies.set(...)` after the response has been generated');
398
+ };
399
+
400
+ event.setHeaders = () => {
401
+ throw new Error('Cannot use `setHeaders(...)` after the response has been generated');
402
+ };
403
+ }
404
+ }
405
+ }
@@ -0,0 +1,267 @@
1
+ import * as devalue from 'devalue';
2
+ import { error, json } from '../../../exports/index.js';
3
+ import { normalize_error } from '../../../utils/error.js';
4
+ import { is_form_content_type, negotiate } from '../../../utils/http.js';
5
+ import { HttpError, Redirect, ActionFailure } from '../../control.js';
6
+ import { handle_error_and_jsonify } from '../utils.js';
7
+
8
+ /** @param {import('types').RequestEvent} event */
9
+ export function is_action_json_request(event) {
10
+ const accept = negotiate(event.request.headers.get('accept') ?? '*/*', [
11
+ 'application/json',
12
+ 'text/html'
13
+ ]);
14
+
15
+ return accept === 'application/json' && event.request.method === 'POST';
16
+ }
17
+
18
+ /**
19
+ * @param {import('types').RequestEvent} event
20
+ * @param {import('types').SSROptions} options
21
+ * @param {import('types').SSRNode['server'] | undefined} server
22
+ */
23
+ export async function handle_action_json_request(event, options, server) {
24
+ const actions = server?.actions;
25
+
26
+ if (!actions) {
27
+ if (server) {
28
+ maybe_throw_migration_error(server);
29
+ }
30
+ // TODO should this be a different error altogether?
31
+ const no_actions_error = error(405, 'POST method not allowed. No actions exist for this page');
32
+ return action_json(
33
+ {
34
+ type: 'error',
35
+ error: await handle_error_and_jsonify(event, options, no_actions_error)
36
+ },
37
+ {
38
+ status: no_actions_error.status,
39
+ headers: {
40
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
41
+ // "The server must generate an Allow header field in a 405 status code response"
42
+ allow: 'GET'
43
+ }
44
+ }
45
+ );
46
+ }
47
+
48
+ check_named_default_separate(actions);
49
+
50
+ try {
51
+ const data = await call_action(event, actions);
52
+
53
+ if (data instanceof ActionFailure) {
54
+ return action_json({
55
+ type: 'failure',
56
+ status: data.status,
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: stringify_action_response(data.data, /** @type {string} */ (event.route.id))
61
+ });
62
+ } else {
63
+ return action_json({
64
+ type: 'success',
65
+ status: data ? 200 : 204,
66
+ // @ts-expect-error see comment above
67
+ data: stringify_action_response(data, /** @type {string} */ (event.route.id))
68
+ });
69
+ }
70
+ } catch (e) {
71
+ const error = normalize_error(e);
72
+
73
+ if (error instanceof Redirect) {
74
+ return action_json({
75
+ type: 'redirect',
76
+ status: error.status,
77
+ location: error.location
78
+ });
79
+ }
80
+
81
+ return action_json(
82
+ {
83
+ type: 'error',
84
+ error: await handle_error_and_jsonify(event, options, check_incorrect_fail_use(error))
85
+ },
86
+ {
87
+ status: error instanceof HttpError ? error.status : 500
88
+ }
89
+ );
90
+ }
91
+ }
92
+
93
+ /**
94
+ * @param {HttpError | Error} error
95
+ */
96
+ function check_incorrect_fail_use(error) {
97
+ return error instanceof ActionFailure
98
+ ? new Error(`Cannot "throw fail()". Use "return fail()"`)
99
+ : error;
100
+ }
101
+
102
+ /**
103
+ * @param {import('types').ActionResult} data
104
+ * @param {ResponseInit} [init]
105
+ */
106
+ function action_json(data, init) {
107
+ return json(data, init);
108
+ }
109
+
110
+ /**
111
+ * @param {import('types').RequestEvent} event
112
+ * @param {import('types').SSRNode} leaf_node
113
+ */
114
+ export function is_action_request(event, leaf_node) {
115
+ return leaf_node.server && event.request.method !== 'GET' && event.request.method !== 'HEAD';
116
+ }
117
+
118
+ /**
119
+ * @param {import('types').RequestEvent} event
120
+ * @param {import('types').SSRNode['server']} server
121
+ * @returns {Promise<import('types').ActionResult>}
122
+ */
123
+ export async function handle_action_request(event, server) {
124
+ const actions = server.actions;
125
+
126
+ if (!actions) {
127
+ maybe_throw_migration_error(server);
128
+ // TODO should this be a different error altogether?
129
+ event.setHeaders({
130
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
131
+ // "The server must generate an Allow header field in a 405 status code response"
132
+ allow: 'GET'
133
+ });
134
+ return {
135
+ type: 'error',
136
+ error: error(405, 'POST method not allowed. No actions exist for this page')
137
+ };
138
+ }
139
+
140
+ check_named_default_separate(actions);
141
+
142
+ try {
143
+ const data = await call_action(event, actions);
144
+
145
+ if (data instanceof ActionFailure) {
146
+ return { type: 'failure', status: data.status, data: data.data };
147
+ } else {
148
+ return {
149
+ type: 'success',
150
+ status: 200,
151
+ data: /** @type {Record<string, any> | undefined} */ (data)
152
+ };
153
+ }
154
+ } catch (e) {
155
+ const error = normalize_error(e);
156
+
157
+ if (error instanceof Redirect) {
158
+ return {
159
+ type: 'redirect',
160
+ status: error.status,
161
+ location: error.location
162
+ };
163
+ }
164
+
165
+ return {
166
+ type: 'error',
167
+ error: check_incorrect_fail_use(error)
168
+ };
169
+ }
170
+ }
171
+
172
+ /**
173
+ * @param {import('types').Actions} actions
174
+ */
175
+ function check_named_default_separate(actions) {
176
+ if (actions.default && Object.keys(actions).length > 1) {
177
+ throw new Error(
178
+ `When using named actions, the default action cannot be used. See the docs for more info: https://kit.svelte.dev/docs/form-actions#named-actions`
179
+ );
180
+ }
181
+ }
182
+
183
+ /**
184
+ * @param {import('types').RequestEvent} event
185
+ * @param {NonNullable<import('types').SSRNode['server']['actions']>} actions
186
+ * @throws {Redirect | ActionFailure | HttpError | Error}
187
+ */
188
+ export async function call_action(event, actions) {
189
+ const url = new URL(event.request.url);
190
+
191
+ let name = 'default';
192
+ for (const param of url.searchParams) {
193
+ if (param[0].startsWith('/')) {
194
+ name = param[0].slice(1);
195
+ if (name === 'default') {
196
+ throw new Error('Cannot use reserved action name "default"');
197
+ }
198
+ break;
199
+ }
200
+ }
201
+
202
+ const action = actions[name];
203
+ if (!action) {
204
+ throw new Error(`No action with name '${name}' found`);
205
+ }
206
+
207
+ if (!is_form_content_type(event.request)) {
208
+ throw new Error(
209
+ `Actions expect form-encoded data (received ${event.request.headers.get('content-type')}`
210
+ );
211
+ }
212
+
213
+ return action(event);
214
+ }
215
+
216
+ /**
217
+ * @param {import('types').SSRNode['server']} server
218
+ */
219
+ function maybe_throw_migration_error(server) {
220
+ for (const method of ['POST', 'PUT', 'PATCH', 'DELETE']) {
221
+ if (/** @type {any} */ (server)[method]) {
222
+ throw new Error(
223
+ `${method} method no longer allowed in +page.server, use actions instead. See the PR for more info: https://github.com/sveltejs/kit/pull/6469`
224
+ );
225
+ }
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Try to `devalue.uneval` the data object, and if it fails, return a proper Error with context
231
+ * @param {any} data
232
+ * @param {string} route_id
233
+ */
234
+ export function uneval_action_response(data, route_id) {
235
+ return try_deserialize(data, devalue.uneval, route_id);
236
+ }
237
+
238
+ /**
239
+ * Try to `devalue.stringify` the data object, and if it fails, return a proper Error with context
240
+ * @param {any} data
241
+ * @param {string} route_id
242
+ */
243
+ function stringify_action_response(data, route_id) {
244
+ return try_deserialize(data, devalue.stringify, route_id);
245
+ }
246
+
247
+ /**
248
+ * @param {any} data
249
+ * @param {(data: any) => string} fn
250
+ * @param {string} route_id
251
+ */
252
+ function try_deserialize(data, fn, route_id) {
253
+ try {
254
+ return fn(data);
255
+ } catch (e) {
256
+ // If we're here, the data could not be serialized with devalue
257
+ const error = /** @type {any} */ (e);
258
+
259
+ if ('path' in error) {
260
+ let message = `Data returned from action inside ${route_id} is not serializable: ${error.message}`;
261
+ if (error.path !== '') message += ` (data.${error.path})`;
262
+ throw new Error(message);
263
+ }
264
+
265
+ throw error;
266
+ }
267
+ }