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