@sveltejs/kit 1.0.0-next.44 → 1.0.0-next.442

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 (153) hide show
  1. package/README.md +12 -9
  2. package/package.json +94 -63
  3. package/src/cli.js +112 -0
  4. package/src/core/adapt/builder.js +223 -0
  5. package/src/core/adapt/index.js +19 -0
  6. package/src/core/config/index.js +86 -0
  7. package/src/core/config/options.js +488 -0
  8. package/src/core/config/types.d.ts +1 -0
  9. package/src/core/constants.js +5 -0
  10. package/src/core/env.js +97 -0
  11. package/src/core/generate_manifest/index.js +78 -0
  12. package/src/core/prerender/crawl.js +194 -0
  13. package/src/core/prerender/prerender.js +380 -0
  14. package/src/core/prerender/queue.js +80 -0
  15. package/src/core/sync/create_manifest_data/index.js +452 -0
  16. package/src/core/sync/create_manifest_data/types.d.ts +37 -0
  17. package/src/core/sync/sync.js +59 -0
  18. package/src/core/sync/utils.js +33 -0
  19. package/src/core/sync/write_ambient.js +27 -0
  20. package/src/core/sync/write_client_manifest.js +92 -0
  21. package/src/core/sync/write_matchers.js +25 -0
  22. package/src/core/sync/write_root.js +91 -0
  23. package/src/core/sync/write_tsconfig.js +195 -0
  24. package/src/core/sync/write_types/index.js +580 -0
  25. package/src/core/sync/write_types/test/layout/+layout.js +5 -0
  26. package/src/core/sync/write_types/test/layout/+layout.server.js +5 -0
  27. package/src/core/sync/write_types/test/layout/+layout.svelte +0 -0
  28. package/src/core/sync/write_types/test/layout/+page.js +5 -0
  29. package/src/core/sync/write_types/test/layout/+page.server.js +5 -0
  30. package/src/core/sync/write_types/test/layout/+page.svelte +0 -0
  31. package/src/core/sync/write_types/test/layout/_expected/$types.d.ts +67 -0
  32. package/src/core/sync/write_types/test/layout-advanced/(main)/+layout.server.js +5 -0
  33. package/src/core/sync/write_types/test/layout-advanced/(main)/+layout.svelte +0 -0
  34. package/src/core/sync/write_types/test/layout-advanced/(main)/+page.js +5 -0
  35. package/src/core/sync/write_types/test/layout-advanced/(main)/+page@.svelte +0 -0
  36. package/src/core/sync/write_types/test/layout-advanced/(main)/sub/+page.js +5 -0
  37. package/src/core/sync/write_types/test/layout-advanced/(main)/sub/+page.svelte +0 -0
  38. package/src/core/sync/write_types/test/layout-advanced/+layout.js +5 -0
  39. package/src/core/sync/write_types/test/layout-advanced/+layout.svelte +0 -0
  40. package/src/core/sync/write_types/test/layout-advanced/_expected/$types.d.ts +32 -0
  41. package/src/core/sync/write_types/test/layout-advanced/_expected/(main)/$types.d.ts +42 -0
  42. package/src/core/sync/write_types/test/layout-advanced/_expected/(main)/sub/$types.d.ts +33 -0
  43. package/src/core/sync/write_types/test/simple-page-server-and-shared/+page.js +5 -0
  44. package/src/core/sync/write_types/test/simple-page-server-and-shared/+page.server.js +5 -0
  45. package/src/core/sync/write_types/test/simple-page-server-and-shared/+page.svelte +0 -0
  46. package/src/core/sync/write_types/test/simple-page-server-and-shared/_expected/$types.d.ts +44 -0
  47. package/src/core/sync/write_types/test/simple-page-server-only/+page.server.js +5 -0
  48. package/src/core/sync/write_types/test/simple-page-server-only/+page.svelte +0 -0
  49. package/src/core/sync/write_types/test/simple-page-server-only/_expected/$types.d.ts +30 -0
  50. package/src/core/sync/write_types/test/simple-page-shared-only/+page.js +5 -0
  51. package/src/core/sync/write_types/test/simple-page-shared-only/+page.svelte +0 -0
  52. package/src/core/sync/write_types/test/simple-page-shared-only/_expected/$types.d.ts +33 -0
  53. package/src/core/sync/write_types/test/slugs/+layout.js +1 -0
  54. package/src/core/sync/write_types/test/slugs/+layout.svelte +1 -0
  55. package/src/core/sync/write_types/test/slugs/[...rest]/+page.js +3 -0
  56. package/src/core/sync/write_types/test/slugs/[...rest]/+page.svelte +0 -0
  57. package/src/core/sync/write_types/test/slugs/[slug]/+page.js +3 -0
  58. package/src/core/sync/write_types/test/slugs/[slug]/+page.svelte +0 -0
  59. package/src/core/sync/write_types/test/slugs/_expected/$types.d.ts +32 -0
  60. package/src/core/sync/write_types/test/slugs/_expected/[...rest]/$types.d.ts +29 -0
  61. package/src/core/sync/write_types/test/slugs/_expected/[slug]/$types.d.ts +29 -0
  62. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/+layout.js +1 -0
  63. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/+layout.svelte +1 -0
  64. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/_expected/$types.d.ts +30 -0
  65. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/_expected/nested/$types.d.ts +32 -0
  66. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/_expected/nested/[...rest]/$types.d.ts +37 -0
  67. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/_expected/nested/[slug]/$types.d.ts +17 -0
  68. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/nested/+layout.js +1 -0
  69. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/nested/+layout.svelte +1 -0
  70. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/nested/[...rest]/+page.js +3 -0
  71. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/nested/[...rest]/+page.svelte +0 -0
  72. package/src/core/sync/write_types/test/slugs-layout-not-all-pages-have-load/nested/[slug]/+page@.svelte +0 -0
  73. package/src/core/utils.js +70 -0
  74. package/src/hooks.js +26 -0
  75. package/src/index/index.js +45 -0
  76. package/src/index/private.js +33 -0
  77. package/src/node/index.js +145 -0
  78. package/src/node/polyfills.js +40 -0
  79. package/src/runtime/app/env.js +11 -0
  80. package/src/runtime/app/navigation.js +22 -0
  81. package/src/runtime/app/paths.js +1 -0
  82. package/src/runtime/app/stores.js +102 -0
  83. package/src/runtime/client/ambient.d.ts +18 -0
  84. package/src/runtime/client/client.js +1362 -0
  85. package/src/runtime/client/fetcher.js +60 -0
  86. package/src/runtime/client/parse.js +41 -0
  87. package/src/runtime/client/singletons.js +21 -0
  88. package/src/runtime/client/start.js +46 -0
  89. package/src/runtime/client/types.d.ts +86 -0
  90. package/src/runtime/client/utils.js +113 -0
  91. package/src/runtime/components/error.svelte +16 -0
  92. package/{assets → src/runtime}/components/layout.svelte +0 -0
  93. package/src/runtime/env/dynamic/private.js +1 -0
  94. package/src/runtime/env/dynamic/public.js +1 -0
  95. package/src/runtime/env-private.js +7 -0
  96. package/src/runtime/env-public.js +7 -0
  97. package/src/runtime/env.js +6 -0
  98. package/src/runtime/hash.js +16 -0
  99. package/src/runtime/paths.js +11 -0
  100. package/src/runtime/server/endpoint.js +50 -0
  101. package/src/runtime/server/index.js +492 -0
  102. package/src/runtime/server/page/cookie.js +25 -0
  103. package/src/runtime/server/page/crypto.js +239 -0
  104. package/src/runtime/server/page/csp.js +249 -0
  105. package/src/runtime/server/page/fetch.js +266 -0
  106. package/src/runtime/server/page/index.js +408 -0
  107. package/src/runtime/server/page/load_data.js +168 -0
  108. package/src/runtime/server/page/render.js +361 -0
  109. package/src/runtime/server/page/respond_with_error.js +95 -0
  110. package/src/runtime/server/page/types.d.ts +44 -0
  111. package/src/runtime/server/utils.js +116 -0
  112. package/src/utils/array.js +9 -0
  113. package/src/utils/error.js +22 -0
  114. package/src/utils/escape.js +104 -0
  115. package/src/utils/filesystem.js +108 -0
  116. package/src/utils/functions.js +16 -0
  117. package/src/utils/http.js +55 -0
  118. package/src/utils/misc.js +1 -0
  119. package/src/utils/routing.js +146 -0
  120. package/src/utils/url.js +142 -0
  121. package/src/vite/build/build_server.js +348 -0
  122. package/src/vite/build/build_service_worker.js +90 -0
  123. package/src/vite/build/utils.js +160 -0
  124. package/src/vite/dev/index.js +543 -0
  125. package/src/vite/index.js +578 -0
  126. package/src/vite/preview/index.js +186 -0
  127. package/src/vite/types.d.ts +3 -0
  128. package/src/vite/utils.js +345 -0
  129. package/svelte-kit.js +1 -1
  130. package/types/ambient.d.ts +366 -0
  131. package/types/index.d.ts +345 -0
  132. package/types/internal.d.ts +374 -0
  133. package/types/private.d.ts +209 -0
  134. package/CHANGELOG.md +0 -437
  135. package/assets/components/error.svelte +0 -13
  136. package/assets/runtime/app/env.js +0 -5
  137. package/assets/runtime/app/navigation.js +0 -41
  138. package/assets/runtime/app/paths.js +0 -1
  139. package/assets/runtime/app/stores.js +0 -93
  140. package/assets/runtime/chunks/utils.js +0 -19
  141. package/assets/runtime/internal/singletons.js +0 -23
  142. package/assets/runtime/internal/start.js +0 -770
  143. package/assets/runtime/paths.js +0 -12
  144. package/dist/chunks/index.js +0 -3521
  145. package/dist/chunks/index2.js +0 -587
  146. package/dist/chunks/index3.js +0 -246
  147. package/dist/chunks/index4.js +0 -545
  148. package/dist/chunks/index5.js +0 -761
  149. package/dist/chunks/index6.js +0 -322
  150. package/dist/chunks/standard.js +0 -99
  151. package/dist/chunks/utils.js +0 -83
  152. package/dist/cli.js +0 -546
  153. package/dist/ssr.js +0 -2580
@@ -0,0 +1,266 @@
1
+ import * as cookie from 'cookie';
2
+ import * as set_cookie_parser from 'set-cookie-parser';
3
+ import { respond } from '../index.js';
4
+ import { is_root_relative, resolve } from '../../../utils/url.js';
5
+ import { domain_matches, path_matches } from './cookie.js';
6
+
7
+ /**
8
+ * @param {{
9
+ * event: import('types').RequestEvent;
10
+ * options: import('types').SSROptions;
11
+ * state: import('types').SSRState;
12
+ * route: import('types').SSRRoute | import('types').SSRErrorPage;
13
+ * }} opts
14
+ */
15
+ export function create_fetch({ event, options, state, route }) {
16
+ /** @type {import('./types').Fetched[]} */
17
+ const fetched = [];
18
+
19
+ const initial_cookies = cookie.parse(event.request.headers.get('cookie') || '');
20
+
21
+ /** @type {import('set-cookie-parser').Cookie[]} */
22
+ const cookies = [];
23
+
24
+ /** @type {typeof fetch} */
25
+ const fetcher = async (resource, opts = {}) => {
26
+ /** @type {string} */
27
+ let requested;
28
+
29
+ if (typeof resource === 'string' || resource instanceof URL) {
30
+ requested = resource.toString();
31
+ } else {
32
+ requested = resource.url;
33
+
34
+ opts = {
35
+ method: resource.method,
36
+ headers: resource.headers,
37
+ body: resource.body,
38
+ mode: resource.mode,
39
+ credentials: resource.credentials,
40
+ cache: resource.cache,
41
+ redirect: resource.redirect,
42
+ referrer: resource.referrer,
43
+ integrity: resource.integrity,
44
+ ...opts
45
+ };
46
+ }
47
+
48
+ opts.headers = new Headers(opts.headers);
49
+
50
+ // merge headers from request
51
+ for (const [key, value] of event.request.headers) {
52
+ if (
53
+ key !== 'authorization' &&
54
+ key !== 'connection' &&
55
+ key !== 'content-length' &&
56
+ key !== 'cookie' &&
57
+ key !== 'host' &&
58
+ key !== 'if-none-match' &&
59
+ !opts.headers.has(key)
60
+ ) {
61
+ opts.headers.set(key, value);
62
+ }
63
+ }
64
+
65
+ const resolved = resolve(event.url.pathname, requested.split('?')[0]);
66
+
67
+ /** @type {Response} */
68
+ let response;
69
+
70
+ /** @type {import('types').PrerenderDependency} */
71
+ let dependency;
72
+
73
+ // handle fetch requests for static assets. e.g. prebaked data, etc.
74
+ // we need to support everything the browser's fetch supports
75
+ const prefix = options.paths.assets || options.paths.base;
76
+ const filename = decodeURIComponent(
77
+ resolved.startsWith(prefix) ? resolved.slice(prefix.length) : resolved
78
+ ).slice(1);
79
+ const filename_html = `${filename}/index.html`; // path may also match path/index.html
80
+
81
+ const is_asset = options.manifest.assets.has(filename);
82
+ const is_asset_html = options.manifest.assets.has(filename_html);
83
+
84
+ if (is_asset || is_asset_html) {
85
+ const file = is_asset ? filename : filename_html;
86
+
87
+ if (options.read) {
88
+ const type = is_asset
89
+ ? options.manifest.mimeTypes[filename.slice(filename.lastIndexOf('.'))]
90
+ : 'text/html';
91
+
92
+ response = new Response(options.read(file), {
93
+ headers: type ? { 'content-type': type } : {}
94
+ });
95
+ } else {
96
+ response = await fetch(`${event.url.origin}/${file}`, /** @type {RequestInit} */ (opts));
97
+ }
98
+ } else if (is_root_relative(resolved)) {
99
+ if (opts.credentials !== 'omit') {
100
+ const authorization = event.request.headers.get('authorization');
101
+
102
+ // combine cookies from the initiating request with any that were
103
+ // added via set-cookie
104
+ const combined_cookies = { ...initial_cookies };
105
+
106
+ for (const cookie of cookies) {
107
+ if (!domain_matches(event.url.hostname, cookie.domain)) continue;
108
+ if (!path_matches(resolved, cookie.path)) continue;
109
+
110
+ combined_cookies[cookie.name] = cookie.value;
111
+ }
112
+
113
+ const cookie = Object.entries(combined_cookies)
114
+ .map(([name, value]) => `${name}=${value}`)
115
+ .join('; ');
116
+
117
+ if (cookie) {
118
+ opts.headers.set('cookie', cookie);
119
+ }
120
+
121
+ if (authorization && !opts.headers.has('authorization')) {
122
+ opts.headers.set('authorization', authorization);
123
+ }
124
+ }
125
+
126
+ if (opts.body && typeof opts.body !== 'string') {
127
+ // per https://developer.mozilla.org/en-US/docs/Web/API/Request/Request, this can be a
128
+ // Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object.
129
+ // non-string bodies are irksome to deal with, but luckily aren't particularly useful
130
+ // in this context anyway, so we take the easy route and ban them
131
+ throw new Error('Request body must be a string');
132
+ }
133
+
134
+ response = await respond(
135
+ new Request(new URL(requested, event.url).href, { ...opts }),
136
+ options,
137
+ {
138
+ ...state,
139
+ initiator: route
140
+ }
141
+ );
142
+
143
+ if (state.prerendering) {
144
+ dependency = { response, body: null };
145
+ state.prerendering.dependencies.set(resolved, dependency);
146
+ }
147
+ } else {
148
+ // external
149
+ if (resolved.startsWith('//')) {
150
+ requested = event.url.protocol + requested;
151
+ }
152
+
153
+ // external fetch
154
+ // allow cookie passthrough for "same-origin"
155
+ // if SvelteKit is serving my.domain.com:
156
+ // - domain.com WILL NOT receive cookies
157
+ // - my.domain.com WILL receive cookies
158
+ // - api.domain.dom WILL NOT receive cookies
159
+ // - sub.my.domain.com WILL receive cookies
160
+ // ports do not affect the resolution
161
+ // leading dot prevents mydomain.com matching domain.com
162
+ if (
163
+ `.${new URL(requested).hostname}`.endsWith(`.${event.url.hostname}`) &&
164
+ opts.credentials !== 'omit'
165
+ ) {
166
+ const cookie = event.request.headers.get('cookie');
167
+ if (cookie) opts.headers.set('cookie', cookie);
168
+ }
169
+
170
+ // we need to delete the connection header, as explained here:
171
+ // https://github.com/nodejs/undici/issues/1470#issuecomment-1140798467
172
+ // TODO this may be a case for being selective about which headers we let through
173
+ opts.headers.delete('connection');
174
+
175
+ const external_request = new Request(requested, /** @type {RequestInit} */ (opts));
176
+ response = await options.hooks.externalFetch.call(null, external_request);
177
+ }
178
+
179
+ const set_cookie = response.headers.get('set-cookie');
180
+ if (set_cookie) {
181
+ cookies.push(
182
+ ...set_cookie_parser
183
+ .splitCookiesString(set_cookie)
184
+ .map((str) => set_cookie_parser.parseString(str))
185
+ );
186
+ }
187
+
188
+ const proxy = new Proxy(response, {
189
+ get(response, key, _receiver) {
190
+ async function text() {
191
+ const body = await response.text();
192
+
193
+ /** @type {import('types').ResponseHeaders} */
194
+ const headers = {};
195
+ for (const [key, value] of response.headers) {
196
+ // TODO skip others besides set-cookie and etag?
197
+ if (key !== 'set-cookie' && key !== 'etag') {
198
+ headers[key] = value;
199
+ }
200
+ }
201
+
202
+ if (!opts.body || typeof opts.body === 'string') {
203
+ const status_number = Number(response.status);
204
+ if (isNaN(status_number)) {
205
+ throw new Error(
206
+ `response.status is not a number. value: "${
207
+ response.status
208
+ }" type: ${typeof response.status}`
209
+ );
210
+ }
211
+
212
+ fetched.push({
213
+ url: requested,
214
+ body: opts.body,
215
+ response: {
216
+ status: status_number,
217
+ statusText: response.statusText,
218
+ headers,
219
+ body
220
+ }
221
+ });
222
+ }
223
+
224
+ if (dependency) {
225
+ dependency.body = body;
226
+ }
227
+
228
+ return body;
229
+ }
230
+
231
+ if (key === 'arrayBuffer') {
232
+ return async () => {
233
+ const buffer = await response.arrayBuffer();
234
+
235
+ if (dependency) {
236
+ dependency.body = new Uint8Array(buffer);
237
+ }
238
+
239
+ // TODO should buffer be inlined into the page (albeit base64'd)?
240
+ // any conditions in which it shouldn't be?
241
+
242
+ return buffer;
243
+ };
244
+ }
245
+
246
+ if (key === 'text') {
247
+ return text;
248
+ }
249
+
250
+ if (key === 'json') {
251
+ return async () => {
252
+ return JSON.parse(await text());
253
+ };
254
+ }
255
+
256
+ // TODO arrayBuffer?
257
+
258
+ return Reflect.get(response, key, response);
259
+ }
260
+ });
261
+
262
+ return proxy;
263
+ };
264
+
265
+ return { fetcher, fetched, cookies };
266
+ }
@@ -0,0 +1,408 @@
1
+ import { negotiate } from '../../../utils/http.js';
2
+ import { render_response } from './render.js';
3
+ import { respond_with_error } from './respond_with_error.js';
4
+ import { method_not_allowed, error_to_pojo, allowed_methods } from '../utils.js';
5
+ import { create_fetch } from './fetch.js';
6
+ import { HttpError, Redirect } from '../../../index/private.js';
7
+ import { error, json } from '../../../index/index.js';
8
+ import { compact } from '../../../utils/array.js';
9
+ import { normalize_error } from '../../../utils/error.js';
10
+ import { load_data, load_server_data } from './load_data.js';
11
+
12
+ /**
13
+ * @typedef {import('./types.js').Loaded} Loaded
14
+ * @typedef {import('types').SSRNode} SSRNode
15
+ * @typedef {import('types').SSROptions} SSROptions
16
+ * @typedef {import('types').SSRState} SSRState
17
+ */
18
+
19
+ /**
20
+ * @param {import('types').RequestEvent} event
21
+ * @param {import('types').SSRRoute} route
22
+ * @param {import('types').PageNodeIndexes} page
23
+ * @param {import('types').SSROptions} options
24
+ * @param {import('types').SSRState} state
25
+ * @param {import('types').RequiredResolveOptions} resolve_opts
26
+ * @returns {Promise<Response>}
27
+ */
28
+ export async function render_page(event, route, page, options, state, resolve_opts) {
29
+ if (state.initiator === route) {
30
+ // infinite request cycle detected
31
+ return new Response(`Not found: ${event.url.pathname}`, {
32
+ status: 404
33
+ });
34
+ }
35
+
36
+ const accept = negotiate(event.request.headers.get('accept') || 'text/html', [
37
+ 'text/html',
38
+ 'application/json'
39
+ ]);
40
+
41
+ if (
42
+ accept === 'application/json' &&
43
+ event.request.method !== 'GET' &&
44
+ event.request.method !== 'HEAD'
45
+ ) {
46
+ const node = await options.manifest._.nodes[page.leaf]();
47
+ if (node.server) {
48
+ return handle_json_request(event, options, node.server);
49
+ }
50
+ }
51
+
52
+ const { fetcher, fetched, cookies } = create_fetch({ event, options, state, route });
53
+
54
+ try {
55
+ const nodes = await Promise.all([
56
+ // we use == here rather than === because [undefined] serializes as "[null]"
57
+ ...page.layouts.map((n) => (n == undefined ? n : options.manifest._.nodes[n]())),
58
+ options.manifest._.nodes[page.leaf]()
59
+ ]);
60
+
61
+ const leaf_node = /** @type {import('types').SSRNode} */ (nodes.at(-1));
62
+
63
+ let status = 200;
64
+
65
+ /** @type {HttpError | Error} */
66
+ let mutation_error;
67
+
68
+ /** @type {Record<string, string> | undefined} */
69
+ let validation_errors;
70
+
71
+ if (leaf_node.server && event.request.method !== 'GET' && event.request.method !== 'HEAD') {
72
+ // for non-GET requests, first call handler in +page.server.js
73
+ // (this also determines status code)
74
+ try {
75
+ const method = /** @type {'POST' | 'PATCH' | 'PUT' | 'DELETE'} */ (event.request.method);
76
+ const handler = leaf_node.server[method];
77
+ if (handler) {
78
+ const result = await handler.call(null, event);
79
+
80
+ if (result?.errors) {
81
+ validation_errors = result.errors;
82
+ status = result.status ?? 400;
83
+ }
84
+
85
+ if (event.request.method === 'POST' && result?.location) {
86
+ return redirect_response(303, result.location);
87
+ }
88
+ } else {
89
+ event.setHeaders({
90
+ allow: allowed_methods(leaf_node.server).join(', ')
91
+ });
92
+
93
+ mutation_error = error(405, 'Method not allowed');
94
+ }
95
+ } catch (e) {
96
+ if (e instanceof Redirect) {
97
+ return redirect_response(e.status, e.location);
98
+ }
99
+
100
+ mutation_error = /** @type {HttpError | Error} */ (e);
101
+ }
102
+ }
103
+
104
+ const should_prerender_data = nodes.some((node) => node?.server);
105
+ const data_pathname = `${event.url.pathname.replace(/\/$/, '')}/__data.json`;
106
+
107
+ // it's crucial that we do this before returning the non-SSR response, otherwise
108
+ // SvelteKit will erroneously believe that the path has been prerendered,
109
+ // causing functions to be omitted from the manifesst generated later
110
+ const should_prerender =
111
+ leaf_node.shared?.prerender ?? leaf_node.server?.prerender ?? options.prerender.default;
112
+ if (should_prerender) {
113
+ const mod = leaf_node.server;
114
+ if (mod && (mod.POST || mod.PUT || mod.DELETE || mod.PATCH)) {
115
+ throw new Error('Cannot prerender pages that have endpoints with mutative methods');
116
+ }
117
+ } else if (state.prerendering) {
118
+ // if the page isn't marked as prerenderable (or is explicitly
119
+ // marked NOT prerenderable, if `prerender.default` is `true`),
120
+ // then bail out at this point
121
+ if (!should_prerender) {
122
+ return new Response(undefined, {
123
+ status: 204
124
+ });
125
+ }
126
+ }
127
+
128
+ if (!resolve_opts.ssr) {
129
+ return await render_response({
130
+ branch: [],
131
+ validation_errors: undefined,
132
+ fetched,
133
+ cookies,
134
+ page_config: {
135
+ hydrate: true,
136
+ router: true
137
+ },
138
+ status,
139
+ error: null,
140
+ event,
141
+ options,
142
+ state,
143
+ resolve_opts
144
+ });
145
+ }
146
+
147
+ /** @type {Array<Loaded | null>} */
148
+ let branch = [];
149
+
150
+ /** @type {Error | null} */
151
+ let load_error = null;
152
+
153
+ /** @type {Array<Promise<import('types').ServerDataNode | null>>} */
154
+ const server_promises = nodes.map((node, i) => {
155
+ if (load_error) {
156
+ // if an error happens immediately, don't bother with the rest of the nodes
157
+ throw load_error;
158
+ }
159
+
160
+ return Promise.resolve().then(async () => {
161
+ try {
162
+ if (node === leaf_node && mutation_error) {
163
+ // we wait until here to throw the error so that we can use
164
+ // any nested +error.svelte components that were defined
165
+ throw mutation_error;
166
+ }
167
+
168
+ return await load_server_data({
169
+ dev: options.dev,
170
+ event,
171
+ state,
172
+ node,
173
+ parent: async () => {
174
+ /** @type {Record<string, any>} */
175
+ const data = {};
176
+ for (let j = 0; j < i; j += 1) {
177
+ const parent = await server_promises[j];
178
+ if (parent) Object.assign(data, await parent.data);
179
+ }
180
+ return data;
181
+ }
182
+ });
183
+ } catch (e) {
184
+ load_error = /** @type {Error} */ (e);
185
+ throw load_error;
186
+ }
187
+ });
188
+ });
189
+
190
+ /** @type {Array<Promise<Record<string, any> | null>>} */
191
+ const load_promises = nodes.map((node, i) => {
192
+ if (load_error) throw load_error;
193
+ return Promise.resolve().then(async () => {
194
+ try {
195
+ return await load_data({
196
+ event,
197
+ fetcher,
198
+ node,
199
+ parent: async () => {
200
+ const data = {};
201
+ for (let j = 0; j < i; j += 1) {
202
+ Object.assign(data, await load_promises[j]);
203
+ }
204
+ return data;
205
+ },
206
+ server_data_promise: server_promises[i],
207
+ state
208
+ });
209
+ } catch (e) {
210
+ load_error = /** @type {Error} */ (e);
211
+ throw load_error;
212
+ }
213
+ });
214
+ });
215
+
216
+ // if we don't do this, rejections will be unhandled
217
+ for (const p of server_promises) p.catch(() => {});
218
+ for (const p of load_promises) p.catch(() => {});
219
+
220
+ for (let i = 0; i < nodes.length; i += 1) {
221
+ const node = nodes[i];
222
+
223
+ if (node) {
224
+ try {
225
+ const server_data = await server_promises[i];
226
+ const data = await load_promises[i];
227
+
228
+ branch.push({ node, server_data, data });
229
+ } catch (e) {
230
+ const error = normalize_error(e);
231
+
232
+ if (error instanceof Redirect) {
233
+ if (state.prerendering && should_prerender_data) {
234
+ state.prerendering.dependencies.set(data_pathname, {
235
+ response: new Response(undefined),
236
+ body: JSON.stringify({
237
+ type: 'redirect',
238
+ location: error.location
239
+ })
240
+ });
241
+ }
242
+
243
+ return redirect_response(error.status, error.location);
244
+ }
245
+
246
+ if (!(error instanceof HttpError)) {
247
+ options.handle_error(/** @type {Error} */ (error), event);
248
+ }
249
+
250
+ const status = error instanceof HttpError ? error.status : 500;
251
+
252
+ while (i--) {
253
+ if (page.errors[i]) {
254
+ const index = /** @type {number} */ (page.errors[i]);
255
+ const node = await options.manifest._.nodes[index]();
256
+
257
+ let j = i;
258
+ while (!branch[j]) j -= 1;
259
+
260
+ return await render_response({
261
+ event,
262
+ options,
263
+ state,
264
+ resolve_opts,
265
+ page_config: { router: true, hydrate: true },
266
+ status,
267
+ error,
268
+ branch: compact(branch.slice(0, j + 1)).concat({
269
+ node,
270
+ data: null,
271
+ server_data: null
272
+ }),
273
+ fetched,
274
+ cookies,
275
+ validation_errors: undefined
276
+ });
277
+ }
278
+ }
279
+
280
+ // if we're still here, it means the error happened in the root layout,
281
+ // which means we have to fall back to a plain text response
282
+ // TODO since the requester is expecting HTML, maybe it makes sense to
283
+ // doll this up a bit
284
+ return new Response(
285
+ error instanceof HttpError ? error.message : options.get_stack(error),
286
+ { status }
287
+ );
288
+ }
289
+ } else {
290
+ // push an empty slot so we can rewind past gaps to the
291
+ // layout that corresponds with an +error.svelte page
292
+ branch.push(null);
293
+ }
294
+ }
295
+
296
+ if (state.prerendering && should_prerender_data) {
297
+ state.prerendering.dependencies.set(data_pathname, {
298
+ response: new Response(undefined),
299
+ body: JSON.stringify({
300
+ type: 'data',
301
+ nodes: branch.map((branch_node) => branch_node?.server_data)
302
+ })
303
+ });
304
+ }
305
+
306
+ // TODO use validation_errors
307
+
308
+ return await render_response({
309
+ event,
310
+ options,
311
+ state,
312
+ resolve_opts,
313
+ page_config: get_page_config(leaf_node, options),
314
+ status,
315
+ error: null,
316
+ branch: compact(branch),
317
+ validation_errors,
318
+ fetched,
319
+ cookies
320
+ });
321
+ } catch (error) {
322
+ // if we end up here, it means the data loaded successfull
323
+ // but the page failed to render
324
+ options.handle_error(/** @type {Error} */ (error), event);
325
+
326
+ return await respond_with_error({
327
+ event,
328
+ options,
329
+ state,
330
+ status: 500,
331
+ error: /** @type {Error} */ (error),
332
+ resolve_opts
333
+ });
334
+ }
335
+ }
336
+
337
+ /**
338
+ * @param {import('types').SSRNode} leaf
339
+ * @param {SSROptions} options
340
+ */
341
+ function get_page_config(leaf, options) {
342
+ // TODO we can reinstate this now that it's in the module
343
+ if (leaf.shared && 'ssr' in leaf.shared) {
344
+ throw new Error(
345
+ '`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs/hooks#handle'
346
+ );
347
+ }
348
+
349
+ return {
350
+ router: leaf.shared?.router ?? options.router,
351
+ hydrate: leaf.shared?.hydrate ?? options.hydrate
352
+ };
353
+ }
354
+
355
+ /**
356
+ * @param {import('types').RequestEvent} event
357
+ * @param {import('types').SSROptions} options
358
+ * @param {import('types').SSRNode['server']} mod
359
+ */
360
+ export async function handle_json_request(event, options, mod) {
361
+ const method = /** @type {'POST' | 'PUT' | 'PATCH' | 'DELETE'} */ (event.request.method);
362
+ const handler = mod[method];
363
+
364
+ if (!handler) {
365
+ return method_not_allowed(mod, method);
366
+ }
367
+
368
+ try {
369
+ // @ts-ignore
370
+ const result = await handler.call(null, event);
371
+
372
+ if (result?.errors) {
373
+ // @ts-ignore
374
+ return json({ errors: result.errors }, { status: result.status || 400 });
375
+ }
376
+
377
+ return new Response(undefined, {
378
+ status: 204,
379
+ // @ts-ignore
380
+ headers: result?.location ? { location: result.location } : undefined
381
+ });
382
+ } catch (e) {
383
+ const error = normalize_error(e);
384
+
385
+ if (error instanceof Redirect) {
386
+ return redirect_response(error.status, error.location);
387
+ }
388
+
389
+ if (!(error instanceof HttpError)) {
390
+ options.handle_error(error, event);
391
+ }
392
+
393
+ return json(error_to_pojo(error, options.get_stack), {
394
+ status: error instanceof HttpError ? error.status : 500
395
+ });
396
+ }
397
+ }
398
+
399
+ /**
400
+ * @param {number} status
401
+ * @param {string} location
402
+ */
403
+ function redirect_response(status, location) {
404
+ return new Response(undefined, {
405
+ status,
406
+ headers: { location }
407
+ });
408
+ }