@sveltejs/kit 1.0.0-next.43 → 1.0.0-next.430
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 +95 -63
- package/src/cli.js +112 -0
- package/src/core/adapt/builder.js +207 -0
- package/src/core/adapt/index.js +19 -0
- package/src/core/config/index.js +86 -0
- package/src/core/config/options.js +488 -0
- package/src/core/config/types.d.ts +1 -0
- package/src/core/constants.js +5 -0
- package/src/core/env.js +97 -0
- package/src/core/generate_manifest/index.js +99 -0
- package/src/core/prerender/crawl.js +194 -0
- package/src/core/prerender/prerender.js +378 -0
- package/src/core/prerender/queue.js +80 -0
- package/src/core/sync/create_manifest_data/index.js +506 -0
- package/src/core/sync/create_manifest_data/types.d.ts +40 -0
- package/src/core/sync/sync.js +59 -0
- package/src/core/sync/utils.js +44 -0
- package/src/core/sync/write_ambient.js +27 -0
- package/src/core/sync/write_client_manifest.js +82 -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.js +775 -0
- package/src/core/utils.js +70 -0
- package/src/hooks.js +26 -0
- package/src/index/index.js +45 -0
- package/src/index/private.js +33 -0
- package/src/node/index.js +145 -0
- package/src/node/polyfills.js +40 -0
- package/src/runtime/app/env.js +11 -0
- package/src/runtime/app/navigation.js +22 -0
- package/src/runtime/app/paths.js +1 -0
- package/src/runtime/app/stores.js +102 -0
- package/src/runtime/client/ambient.d.ts +17 -0
- package/src/runtime/client/client.js +1289 -0
- package/src/runtime/client/fetcher.js +60 -0
- package/src/runtime/client/parse.js +36 -0
- package/src/runtime/client/singletons.js +21 -0
- package/src/runtime/client/start.js +46 -0
- package/src/runtime/client/types.d.ts +105 -0
- package/src/runtime/client/utils.js +113 -0
- package/src/runtime/components/error.svelte +16 -0
- package/{assets → src/runtime}/components/layout.svelte +0 -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 +7 -0
- package/src/runtime/env-public.js +7 -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/endpoint.js +58 -0
- package/src/runtime/server/index.js +448 -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 +266 -0
- package/src/runtime/server/page/index.js +416 -0
- package/src/runtime/server/page/load_data.js +135 -0
- package/src/runtime/server/page/render.js +362 -0
- package/src/runtime/server/page/respond_with_error.js +94 -0
- package/src/runtime/server/page/types.d.ts +44 -0
- package/src/runtime/server/utils.js +116 -0
- package/src/utils/error.js +22 -0
- package/src/utils/escape.js +104 -0
- package/src/utils/filesystem.js +108 -0
- package/src/utils/http.js +55 -0
- package/src/utils/misc.js +1 -0
- package/src/utils/routing.js +108 -0
- package/src/utils/url.js +97 -0
- package/src/vite/build/build_server.js +337 -0
- package/src/vite/build/build_service_worker.js +90 -0
- package/src/vite/build/utils.js +160 -0
- package/src/vite/dev/index.js +551 -0
- package/src/vite/index.js +574 -0
- package/src/vite/preview/index.js +186 -0
- package/src/vite/types.d.ts +3 -0
- package/src/vite/utils.js +345 -0
- package/svelte-kit.js +1 -1
- package/types/ambient.d.ts +357 -0
- package/types/index.d.ts +343 -0
- package/types/internal.d.ts +308 -0
- package/types/private.d.ts +209 -0
- package/CHANGELOG.md +0 -431
- package/assets/components/error.svelte +0 -13
- package/assets/runtime/app/env.js +0 -5
- package/assets/runtime/app/navigation.js +0 -41
- package/assets/runtime/app/paths.js +0 -1
- package/assets/runtime/app/stores.js +0 -93
- package/assets/runtime/chunks/utils.js +0 -19
- package/assets/runtime/internal/singletons.js +0 -23
- package/assets/runtime/internal/start.js +0 -770
- package/assets/runtime/paths.js +0 -12
- package/dist/.DS_Store +0 -0
- package/dist/chunks/index.js +0 -3521
- package/dist/chunks/index2.js +0 -587
- package/dist/chunks/index3.js +0 -246
- package/dist/chunks/index4.js +0 -538
- package/dist/chunks/index5.js +0 -761
- 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 -546
- package/dist/ssr.js +0 -2581
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import devalue from 'devalue';
|
|
2
|
+
import { readable, writable } from 'svelte/store';
|
|
3
|
+
import * as cookie from 'cookie';
|
|
4
|
+
import { hash } from '../../hash.js';
|
|
5
|
+
import { render_json_payload_script } from '../../../utils/escape.js';
|
|
6
|
+
import { s } from '../../../utils/misc.js';
|
|
7
|
+
import { Csp } from './csp.js';
|
|
8
|
+
import { PrerenderingURL } from '../../../utils/url.js';
|
|
9
|
+
import { serialize_error } from '../utils.js';
|
|
10
|
+
import { HttpError } from '../../../index/private.js';
|
|
11
|
+
|
|
12
|
+
// TODO rename this function/module
|
|
13
|
+
|
|
14
|
+
const updated = {
|
|
15
|
+
...readable(false),
|
|
16
|
+
check: () => false
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates the HTML response.
|
|
21
|
+
* @param {{
|
|
22
|
+
* branch: Array<import('./types').Loaded>;
|
|
23
|
+
* fetched: Array<import('./types').Fetched>;
|
|
24
|
+
* cookies: import('set-cookie-parser').Cookie[];
|
|
25
|
+
* options: import('types').SSROptions;
|
|
26
|
+
* state: import('types').SSRState;
|
|
27
|
+
* page_config: { hydrate: boolean, router: boolean };
|
|
28
|
+
* status: number;
|
|
29
|
+
* error: HttpError | Error | null;
|
|
30
|
+
* event: import('types').RequestEvent;
|
|
31
|
+
* resolve_opts: import('types').RequiredResolveOptions;
|
|
32
|
+
* validation_errors: Record<string, string> | undefined;
|
|
33
|
+
* }} opts
|
|
34
|
+
*/
|
|
35
|
+
export async function render_response({
|
|
36
|
+
branch,
|
|
37
|
+
fetched,
|
|
38
|
+
cookies,
|
|
39
|
+
options,
|
|
40
|
+
state,
|
|
41
|
+
page_config,
|
|
42
|
+
status,
|
|
43
|
+
error = null,
|
|
44
|
+
event,
|
|
45
|
+
resolve_opts,
|
|
46
|
+
validation_errors
|
|
47
|
+
}) {
|
|
48
|
+
if (state.prerendering) {
|
|
49
|
+
if (options.csp.mode === 'nonce') {
|
|
50
|
+
throw new Error('Cannot use prerendering if config.kit.csp.mode === "nonce"');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (options.template_contains_nonce) {
|
|
54
|
+
throw new Error('Cannot use prerendering if page template contains %sveltekit.nonce%');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const { entry } = options.manifest._;
|
|
59
|
+
|
|
60
|
+
const stylesheets = new Set(entry.stylesheets);
|
|
61
|
+
const modulepreloads = new Set(entry.imports);
|
|
62
|
+
|
|
63
|
+
/** @type {Set<string>} */
|
|
64
|
+
const link_header_preloads = new Set();
|
|
65
|
+
|
|
66
|
+
/** @type {Map<string, string>} */
|
|
67
|
+
// TODO if we add a client entry point one day, we will need to include inline_styles with the entry, otherwise stylesheets will be linked even if they are below inlineStyleThreshold
|
|
68
|
+
const inline_styles = new Map();
|
|
69
|
+
|
|
70
|
+
let rendered;
|
|
71
|
+
|
|
72
|
+
const stack = error instanceof HttpError ? undefined : error?.stack;
|
|
73
|
+
|
|
74
|
+
if (error && options.dev && !(error instanceof HttpError)) {
|
|
75
|
+
error.stack = options.get_stack(error);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (resolve_opts.ssr) {
|
|
79
|
+
/** @type {Record<string, any>} */
|
|
80
|
+
const props = {
|
|
81
|
+
stores: {
|
|
82
|
+
page: writable(null),
|
|
83
|
+
navigating: writable(null),
|
|
84
|
+
updated
|
|
85
|
+
},
|
|
86
|
+
components: await Promise.all(branch.map(({ node }) => node.component()))
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
let data = {};
|
|
90
|
+
|
|
91
|
+
// props_n (instead of props[n]) makes it easy to avoid
|
|
92
|
+
// unnecessary updates for layout components
|
|
93
|
+
for (let i = 0; i < branch.length; i += 1) {
|
|
94
|
+
data = { ...data, ...branch[i].data };
|
|
95
|
+
props[`data_${i}`] = data;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
props.page = {
|
|
99
|
+
error,
|
|
100
|
+
params: /** @type {Record<string, any>} */ (event.params),
|
|
101
|
+
routeId: event.routeId,
|
|
102
|
+
status,
|
|
103
|
+
url: state.prerendering ? new PrerenderingURL(event.url) : event.url,
|
|
104
|
+
data
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
if (validation_errors) {
|
|
108
|
+
props.errors = validation_errors;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// TODO remove this for 1.0
|
|
112
|
+
/**
|
|
113
|
+
* @param {string} property
|
|
114
|
+
* @param {string} replacement
|
|
115
|
+
*/
|
|
116
|
+
const print_error = (property, replacement) => {
|
|
117
|
+
Object.defineProperty(props.page, property, {
|
|
118
|
+
get: () => {
|
|
119
|
+
throw new Error(`$page.${property} has been replaced by $page.url.${replacement}`);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
print_error('origin', 'origin');
|
|
125
|
+
print_error('path', 'pathname');
|
|
126
|
+
print_error('query', 'searchParams');
|
|
127
|
+
|
|
128
|
+
rendered = options.root.render(props);
|
|
129
|
+
|
|
130
|
+
for (const { node } of branch) {
|
|
131
|
+
if (node.imports) {
|
|
132
|
+
node.imports.forEach((url) => modulepreloads.add(url));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (node.stylesheets) {
|
|
136
|
+
node.stylesheets.forEach((url) => stylesheets.add(url));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (node.inline_styles) {
|
|
140
|
+
Object.entries(await node.inline_styles()).forEach(([k, v]) => inline_styles.set(k, v));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
rendered = { head: '', html: '', css: { code: '', map: null } };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let { head, html: body } = rendered;
|
|
148
|
+
|
|
149
|
+
const csp = new Csp(options.csp, {
|
|
150
|
+
dev: options.dev,
|
|
151
|
+
prerender: !!state.prerendering
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const target = hash(body);
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* The prefix to use for static assets. Replaces `%sveltekit.assets%` in the template
|
|
158
|
+
* @type {string}
|
|
159
|
+
*/
|
|
160
|
+
let assets;
|
|
161
|
+
|
|
162
|
+
if (options.paths.assets) {
|
|
163
|
+
// if an asset path is specified, use it
|
|
164
|
+
assets = options.paths.assets;
|
|
165
|
+
} else if (state.prerendering?.fallback) {
|
|
166
|
+
// if we're creating a fallback page, asset paths need to be root-relative
|
|
167
|
+
assets = options.paths.base;
|
|
168
|
+
} else {
|
|
169
|
+
// otherwise we want asset paths to be relative to the page, so that they
|
|
170
|
+
// will work in odd contexts like IPFS, the internet archive, and so on
|
|
171
|
+
const segments = event.url.pathname.slice(options.paths.base.length).split('/').slice(2);
|
|
172
|
+
assets = segments.length > 0 ? segments.map(() => '..').join('/') : '.';
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** @param {string} path */
|
|
176
|
+
const prefixed = (path) => (path.startsWith('/') ? path : `${assets}/${path}`);
|
|
177
|
+
|
|
178
|
+
// prettier-ignore
|
|
179
|
+
const init_app = `
|
|
180
|
+
import { set_public_env, start } from ${s(prefixed(entry.file))};
|
|
181
|
+
|
|
182
|
+
set_public_env(${s(options.public_env)});
|
|
183
|
+
|
|
184
|
+
start({
|
|
185
|
+
target: document.querySelector('[data-sveltekit-hydrate="${target}"]').parentNode,
|
|
186
|
+
paths: ${s(options.paths)},
|
|
187
|
+
route: ${!!page_config.router},
|
|
188
|
+
spa: ${!resolve_opts.ssr},
|
|
189
|
+
trailing_slash: ${s(options.trailing_slash)},
|
|
190
|
+
hydrate: ${resolve_opts.ssr && page_config.hydrate ? `{
|
|
191
|
+
status: ${status},
|
|
192
|
+
error: ${error && serialize_error(error, e => e.stack)},
|
|
193
|
+
node_ids: [${branch.map(({ node }) => node.index).join(', ')}],
|
|
194
|
+
params: ${devalue(event.params)},
|
|
195
|
+
routeId: ${s(event.routeId)}
|
|
196
|
+
}` : 'null'}
|
|
197
|
+
});
|
|
198
|
+
`;
|
|
199
|
+
|
|
200
|
+
// we use an anonymous function instead of an arrow function to support
|
|
201
|
+
// older browsers (https://github.com/sveltejs/kit/pull/5417)
|
|
202
|
+
const init_service_worker = `
|
|
203
|
+
if ('serviceWorker' in navigator) {
|
|
204
|
+
addEventListener('load', function () {
|
|
205
|
+
navigator.serviceWorker.register('${options.service_worker}');
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
`;
|
|
209
|
+
|
|
210
|
+
if (inline_styles.size > 0) {
|
|
211
|
+
const content = Array.from(inline_styles.values()).join('\n');
|
|
212
|
+
|
|
213
|
+
const attributes = [];
|
|
214
|
+
if (options.dev) attributes.push(' data-sveltekit');
|
|
215
|
+
if (csp.style_needs_nonce) attributes.push(` nonce="${csp.nonce}"`);
|
|
216
|
+
|
|
217
|
+
csp.add_style(content);
|
|
218
|
+
|
|
219
|
+
head += `\n\t<style${attributes.join('')}>${content}</style>`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
for (const dep of stylesheets) {
|
|
223
|
+
const path = prefixed(dep);
|
|
224
|
+
const attributes = [];
|
|
225
|
+
|
|
226
|
+
if (csp.style_needs_nonce) {
|
|
227
|
+
attributes.push(`nonce="${csp.nonce}"`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (inline_styles.has(dep)) {
|
|
231
|
+
// don't load stylesheets that are already inlined
|
|
232
|
+
// include them in disabled state so that Vite can detect them and doesn't try to add them
|
|
233
|
+
attributes.push('disabled', 'media="(max-width: 0)"');
|
|
234
|
+
} else {
|
|
235
|
+
const preload_atts = ['rel="preload"', 'as="style"'].concat(attributes);
|
|
236
|
+
link_header_preloads.add(`<${encodeURI(path)}>; ${preload_atts.join(';')}; nopush`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
attributes.unshift('rel="stylesheet"');
|
|
240
|
+
head += `\n\t<link href="${path}" ${attributes.join(' ')}>`;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (page_config.router || page_config.hydrate) {
|
|
244
|
+
for (const dep of modulepreloads) {
|
|
245
|
+
const path = prefixed(dep);
|
|
246
|
+
link_header_preloads.add(`<${encodeURI(path)}>; rel="modulepreload"; nopush`);
|
|
247
|
+
if (state.prerendering) {
|
|
248
|
+
head += `\n\t<link rel="modulepreload" href="${path}">`;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const attributes = ['type="module"', `data-sveltekit-hydrate="${target}"`];
|
|
253
|
+
|
|
254
|
+
csp.add_script(init_app);
|
|
255
|
+
|
|
256
|
+
if (csp.script_needs_nonce) {
|
|
257
|
+
attributes.push(`nonce="${csp.nonce}"`);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
body += `\n\t\t<script ${attributes.join(' ')}>${init_app}</script>`;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (resolve_opts.ssr && page_config.hydrate) {
|
|
264
|
+
/** @type {string[]} */
|
|
265
|
+
const serialized_data = [];
|
|
266
|
+
|
|
267
|
+
for (const { url, body, response } of fetched) {
|
|
268
|
+
serialized_data.push(
|
|
269
|
+
render_json_payload_script(
|
|
270
|
+
{ type: 'data', url, body: typeof body === 'string' ? hash(body) : undefined },
|
|
271
|
+
response
|
|
272
|
+
)
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (branch.some((node) => node.server_data)) {
|
|
277
|
+
serialized_data.push(
|
|
278
|
+
render_json_payload_script(
|
|
279
|
+
{ type: 'server_data' },
|
|
280
|
+
branch.map(({ server_data }) => server_data)
|
|
281
|
+
)
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (validation_errors) {
|
|
286
|
+
serialized_data.push(
|
|
287
|
+
render_json_payload_script({ type: 'validation_errors' }, validation_errors)
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
body += `\n\t${serialized_data.join('\n\t')}`;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (options.service_worker) {
|
|
295
|
+
// always include service worker unless it's turned off explicitly
|
|
296
|
+
csp.add_script(init_service_worker);
|
|
297
|
+
|
|
298
|
+
head += `
|
|
299
|
+
<script${csp.script_needs_nonce ? ` nonce="${csp.nonce}"` : ''}>${init_service_worker}</script>`;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (state.prerendering) {
|
|
303
|
+
// TODO read headers set with setHeaders and convert into http-equiv where possible
|
|
304
|
+
const http_equiv = [];
|
|
305
|
+
|
|
306
|
+
const csp_headers = csp.csp_provider.get_meta();
|
|
307
|
+
if (csp_headers) {
|
|
308
|
+
http_equiv.push(csp_headers);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (state.prerendering.cache) {
|
|
312
|
+
http_equiv.push(`<meta http-equiv="cache-control" content="${state.prerendering.cache}">`);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (http_equiv.length > 0) {
|
|
316
|
+
head = http_equiv.join('\n') + head;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// TODO flush chunks as early as we can
|
|
321
|
+
const html =
|
|
322
|
+
(await resolve_opts.transformPageChunk({
|
|
323
|
+
html: options.template({ head, body, assets, nonce: /** @type {string} */ (csp.nonce) }),
|
|
324
|
+
done: true
|
|
325
|
+
})) || '';
|
|
326
|
+
|
|
327
|
+
const headers = new Headers({
|
|
328
|
+
'content-type': 'text/html',
|
|
329
|
+
etag: `"${hash(html)}"`
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
if (!state.prerendering) {
|
|
333
|
+
const csp_header = csp.csp_provider.get_header();
|
|
334
|
+
if (csp_header) {
|
|
335
|
+
headers.set('content-security-policy', csp_header);
|
|
336
|
+
}
|
|
337
|
+
const report_only_header = csp.report_only_provider.get_header();
|
|
338
|
+
if (report_only_header) {
|
|
339
|
+
headers.set('content-security-policy-report-only', report_only_header);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
for (const new_cookie of cookies) {
|
|
343
|
+
const { name, value, ...options } = new_cookie;
|
|
344
|
+
// @ts-expect-error
|
|
345
|
+
headers.append('set-cookie', cookie.serialize(name, value, options));
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (link_header_preloads.size) {
|
|
349
|
+
headers.set('link', Array.from(link_header_preloads).join(', '));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (error && options.dev && !(error instanceof HttpError)) {
|
|
354
|
+
// reset stack, otherwise it may be 'fixed' a second time
|
|
355
|
+
error.stack = stack;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return new Response(html, {
|
|
359
|
+
status,
|
|
360
|
+
headers
|
|
361
|
+
});
|
|
362
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { render_response } from './render.js';
|
|
2
|
+
import { load_data, load_server_data } from './load_data.js';
|
|
3
|
+
import { coalesce_to_error } from '../../../utils/error.js';
|
|
4
|
+
import { GENERIC_ERROR } from '../utils.js';
|
|
5
|
+
import { create_fetch } from './fetch.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('./types.js').Loaded} Loaded
|
|
9
|
+
* @typedef {import('types').SSROptions} SSROptions
|
|
10
|
+
* @typedef {import('types').SSRState} SSRState
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {{
|
|
15
|
+
* event: import('types').RequestEvent;
|
|
16
|
+
* options: SSROptions;
|
|
17
|
+
* state: SSRState;
|
|
18
|
+
* status: number;
|
|
19
|
+
* error: Error;
|
|
20
|
+
* resolve_opts: import('types').RequiredResolveOptions;
|
|
21
|
+
* }} opts
|
|
22
|
+
*/
|
|
23
|
+
export async function respond_with_error({ event, options, state, status, error, resolve_opts }) {
|
|
24
|
+
const { fetcher, fetched, cookies } = create_fetch({
|
|
25
|
+
event,
|
|
26
|
+
options,
|
|
27
|
+
state,
|
|
28
|
+
route: GENERIC_ERROR
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const branch = [];
|
|
33
|
+
|
|
34
|
+
if (resolve_opts.ssr) {
|
|
35
|
+
const default_layout = await options.manifest._.nodes[0](); // 0 is always the root layout
|
|
36
|
+
|
|
37
|
+
const server_data_promise = load_server_data({
|
|
38
|
+
dev: options.dev,
|
|
39
|
+
event,
|
|
40
|
+
node: default_layout,
|
|
41
|
+
parent: async () => ({})
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const server_data = await server_data_promise;
|
|
45
|
+
|
|
46
|
+
const data = await load_data({
|
|
47
|
+
event,
|
|
48
|
+
fetcher,
|
|
49
|
+
node: default_layout,
|
|
50
|
+
parent: async () => ({}),
|
|
51
|
+
server_data_promise,
|
|
52
|
+
state
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
branch.push(
|
|
56
|
+
{
|
|
57
|
+
node: default_layout,
|
|
58
|
+
server_data,
|
|
59
|
+
data
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
node: await options.manifest._.nodes[1](), // 1 is always the root error
|
|
63
|
+
data: null,
|
|
64
|
+
server_data: null
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return await render_response({
|
|
70
|
+
options,
|
|
71
|
+
state,
|
|
72
|
+
page_config: {
|
|
73
|
+
hydrate: options.hydrate,
|
|
74
|
+
router: options.router
|
|
75
|
+
},
|
|
76
|
+
status,
|
|
77
|
+
error,
|
|
78
|
+
branch,
|
|
79
|
+
fetched,
|
|
80
|
+
cookies,
|
|
81
|
+
event,
|
|
82
|
+
resolve_opts,
|
|
83
|
+
validation_errors: undefined
|
|
84
|
+
});
|
|
85
|
+
} catch (err) {
|
|
86
|
+
const error = coalesce_to_error(err);
|
|
87
|
+
|
|
88
|
+
options.handle_error(error, event);
|
|
89
|
+
|
|
90
|
+
return new Response(error.stack, {
|
|
91
|
+
status: 500
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ResponseHeaders, SSRNode, CspDirectives } from 'types';
|
|
2
|
+
import { HttpError } from '../../../index/private';
|
|
3
|
+
|
|
4
|
+
export interface Fetched {
|
|
5
|
+
url: string;
|
|
6
|
+
body?: string | null;
|
|
7
|
+
response: {
|
|
8
|
+
status: number;
|
|
9
|
+
statusText: string;
|
|
10
|
+
headers: ResponseHeaders;
|
|
11
|
+
body: string;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface FetchState {
|
|
16
|
+
fetched: Fetched[];
|
|
17
|
+
cookies: string[];
|
|
18
|
+
new_cookies: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type Loaded = {
|
|
22
|
+
node: SSRNode;
|
|
23
|
+
data: Record<string, any> | null;
|
|
24
|
+
server_data: any;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
type CspMode = 'hash' | 'nonce' | 'auto';
|
|
28
|
+
|
|
29
|
+
export interface CspConfig {
|
|
30
|
+
mode: CspMode;
|
|
31
|
+
directives: CspDirectives;
|
|
32
|
+
reportOnly: CspDirectives;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface CspOpts {
|
|
36
|
+
dev: boolean;
|
|
37
|
+
prerender: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface SerializedHttpError extends Pick<HttpError, 'message' | 'status'> {
|
|
41
|
+
name: 'HttpError';
|
|
42
|
+
stack: '';
|
|
43
|
+
__is_http_error: true;
|
|
44
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { HttpError } from '../../index/private.js';
|
|
2
|
+
|
|
3
|
+
/** @param {any} body */
|
|
4
|
+
export function is_pojo(body) {
|
|
5
|
+
if (typeof body !== 'object') return false;
|
|
6
|
+
|
|
7
|
+
if (body) {
|
|
8
|
+
if (body instanceof Uint8Array) return false;
|
|
9
|
+
if (body instanceof ReadableStream) return false;
|
|
10
|
+
|
|
11
|
+
// if body is a node Readable, throw an error
|
|
12
|
+
// TODO remove this for 1.0
|
|
13
|
+
if (body._readableState && typeof body.pipe === 'function') {
|
|
14
|
+
throw new Error('Node streams are no longer supported — use a ReadableStream instead');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Serialize an error into a JSON string through `error_to_pojo`.
|
|
23
|
+
* This is necessary because `JSON.stringify(error) === '{}'`
|
|
24
|
+
*
|
|
25
|
+
* @param {Error | HttpError} error
|
|
26
|
+
* @param {(error: Error) => string | undefined} get_stack
|
|
27
|
+
*/
|
|
28
|
+
export function serialize_error(error, get_stack) {
|
|
29
|
+
return JSON.stringify(error_to_pojo(error, get_stack));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Transform an error into a POJO, by copying its `name`, `message`
|
|
34
|
+
* and (in dev) `stack`, plus any custom properties, plus recursively
|
|
35
|
+
* serialized `cause` properties.
|
|
36
|
+
* Our own HttpError gets a meta property attached so we can identify it on the client.
|
|
37
|
+
*
|
|
38
|
+
* @param {HttpError | Error } error
|
|
39
|
+
* @param {(error: Error) => string | undefined} get_stack
|
|
40
|
+
*/
|
|
41
|
+
export function error_to_pojo(error, get_stack) {
|
|
42
|
+
if (error instanceof HttpError) {
|
|
43
|
+
return /** @type {import('./page/types').SerializedHttpError} */ ({
|
|
44
|
+
message: error.message,
|
|
45
|
+
status: error.status,
|
|
46
|
+
__is_http_error: true // TODO we should probably make this unnecessary
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const {
|
|
51
|
+
name,
|
|
52
|
+
message,
|
|
53
|
+
stack,
|
|
54
|
+
// @ts-expect-error i guess typescript doesn't know about error.cause yet
|
|
55
|
+
cause,
|
|
56
|
+
...custom
|
|
57
|
+
} = error;
|
|
58
|
+
|
|
59
|
+
/** @type {Record<string, any>} */
|
|
60
|
+
const object = { name, message, stack: get_stack(error) };
|
|
61
|
+
|
|
62
|
+
if (cause) object.cause = error_to_pojo(cause, get_stack);
|
|
63
|
+
|
|
64
|
+
for (const key in custom) {
|
|
65
|
+
// @ts-expect-error
|
|
66
|
+
object[key] = custom[key];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return object;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// TODO: Remove for 1.0
|
|
73
|
+
/** @param {Record<string, any>} mod */
|
|
74
|
+
export function check_method_names(mod) {
|
|
75
|
+
['get', 'post', 'put', 'patch', 'del'].forEach((m) => {
|
|
76
|
+
if (m in mod) {
|
|
77
|
+
const replacement = m === 'del' ? 'DELETE' : m.toUpperCase();
|
|
78
|
+
throw Error(
|
|
79
|
+
`Endpoint method "${m}" has changed to "${replacement}". See https://github.com/sveltejs/kit/discussions/5359 for more information.`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** @type {import('types').SSRErrorPage} */
|
|
86
|
+
export const GENERIC_ERROR = {
|
|
87
|
+
id: '__error'
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {Partial<Record<import('types').HttpMethod, any>>} mod
|
|
92
|
+
* @param {import('types').HttpMethod} method
|
|
93
|
+
*/
|
|
94
|
+
export function method_not_allowed(mod, method) {
|
|
95
|
+
return new Response(`${method} method not allowed`, {
|
|
96
|
+
status: 405,
|
|
97
|
+
headers: {
|
|
98
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
|
|
99
|
+
// "The server must generate an Allow header field in a 405 status code response"
|
|
100
|
+
allow: allowed_methods(mod).join(', ')
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** @param {Partial<Record<import('types').HttpMethod, any>>} mod */
|
|
106
|
+
export function allowed_methods(mod) {
|
|
107
|
+
const allowed = [];
|
|
108
|
+
|
|
109
|
+
for (const method in ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']) {
|
|
110
|
+
if (method in mod) allowed.push(method);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (mod.GET || mod.HEAD) allowed.push('HEAD');
|
|
114
|
+
|
|
115
|
+
return allowed;
|
|
116
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { HttpError, Redirect } from '../index/private.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {unknown} err
|
|
5
|
+
* @return {Error}
|
|
6
|
+
*/
|
|
7
|
+
export function coalesce_to_error(err) {
|
|
8
|
+
return err instanceof Error ||
|
|
9
|
+
(err && /** @type {any} */ (err).name && /** @type {any} */ (err).message)
|
|
10
|
+
? /** @type {Error} */ (err)
|
|
11
|
+
: new Error(JSON.stringify(err));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* This is an identity function that exists to make TypeScript less
|
|
16
|
+
* paranoid about people throwing things that aren't errors, which
|
|
17
|
+
* frankly is not something we should care about
|
|
18
|
+
* @param {unknown} error
|
|
19
|
+
*/
|
|
20
|
+
export function normalize_error(error) {
|
|
21
|
+
return /** @type {Redirect | HttpError | Error} */ (error);
|
|
22
|
+
}
|