@sveltejs/kit 1.0.0-next.98 → 1.0.0
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 +5 -1
- package/package.json +91 -77
- package/postinstall.js +47 -0
- package/src/cli.js +44 -0
- package/src/constants.js +5 -0
- package/src/core/adapt/builder.js +221 -0
- package/src/core/adapt/index.js +31 -0
- package/src/core/config/default-error.html +56 -0
- package/src/core/config/index.js +100 -0
- package/src/core/config/options.js +387 -0
- package/src/core/config/types.d.ts +1 -0
- package/src/core/env.js +138 -0
- package/src/core/generate_manifest/index.js +116 -0
- package/src/core/prerender/crawl.js +207 -0
- package/src/core/prerender/entities.js +2252 -0
- package/src/core/prerender/fallback.js +43 -0
- package/src/core/prerender/prerender.js +459 -0
- package/src/core/prerender/queue.js +80 -0
- package/src/core/sync/create_manifest_data/conflict.js +0 -0
- package/src/core/sync/create_manifest_data/index.js +523 -0
- package/src/core/sync/create_manifest_data/sort.js +161 -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 +58 -0
- package/src/core/sync/write_client_manifest.js +107 -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 +809 -0
- package/src/core/utils.js +67 -0
- package/src/exports/hooks/index.js +1 -0
- package/src/exports/hooks/sequence.js +44 -0
- package/src/exports/index.js +55 -0
- package/src/exports/node/index.js +172 -0
- package/src/exports/node/polyfills.js +28 -0
- package/src/exports/vite/build/build_server.js +359 -0
- package/src/exports/vite/build/build_service_worker.js +85 -0
- package/src/exports/vite/build/utils.js +230 -0
- package/src/exports/vite/dev/index.js +597 -0
- package/src/exports/vite/graph_analysis/index.js +99 -0
- package/src/exports/vite/graph_analysis/types.d.ts +5 -0
- package/src/exports/vite/graph_analysis/utils.js +6 -0
- package/src/exports/vite/index.js +708 -0
- package/src/exports/vite/preview/index.js +194 -0
- package/src/exports/vite/types.d.ts +3 -0
- package/src/exports/vite/utils.js +184 -0
- package/src/runtime/app/env.js +1 -0
- package/src/runtime/app/environment.js +13 -0
- package/src/runtime/app/forms.js +135 -0
- package/src/runtime/app/navigation.js +22 -0
- package/src/runtime/app/paths.js +1 -0
- package/src/runtime/app/stores.js +57 -0
- package/src/runtime/client/ambient.d.ts +30 -0
- package/src/runtime/client/client.js +1725 -0
- package/src/runtime/client/constants.js +10 -0
- package/src/runtime/client/fetcher.js +127 -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 +86 -0
- package/src/runtime/client/utils.js +257 -0
- package/src/runtime/components/error.svelte +6 -0
- package/{assets → src/runtime}/components/layout.svelte +0 -0
- package/src/runtime/control.js +45 -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 +12 -0
- package/src/runtime/hash.js +20 -0
- package/src/runtime/paths.js +11 -0
- package/src/runtime/server/cookie.js +228 -0
- package/src/runtime/server/data/index.js +158 -0
- package/src/runtime/server/endpoint.js +86 -0
- package/src/runtime/server/fetch.js +175 -0
- package/src/runtime/server/index.js +405 -0
- package/src/runtime/server/page/actions.js +267 -0
- package/src/runtime/server/page/crypto.js +239 -0
- package/src/runtime/server/page/csp.js +250 -0
- package/src/runtime/server/page/index.js +326 -0
- package/src/runtime/server/page/load_data.js +270 -0
- package/src/runtime/server/page/render.js +393 -0
- package/src/runtime/server/page/respond_with_error.js +103 -0
- package/src/runtime/server/page/serialize_data.js +87 -0
- package/src/runtime/server/page/types.d.ts +35 -0
- package/src/runtime/server/utils.js +179 -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/exports.js +54 -0
- package/src/utils/filesystem.js +178 -0
- package/src/utils/functions.js +16 -0
- package/src/utils/http.js +72 -0
- package/src/utils/misc.js +1 -0
- package/src/utils/promises.js +17 -0
- package/src/utils/routing.js +201 -0
- package/src/utils/unit_test.js +11 -0
- package/src/utils/url.js +161 -0
- package/svelte-kit.js +1 -1
- package/types/ambient.d.ts +451 -0
- package/types/index.d.ts +1168 -5
- package/types/internal.d.ts +348 -160
- package/types/private.d.ts +237 -0
- package/types/synthetic/$env+dynamic+private.md +10 -0
- package/types/synthetic/$env+dynamic+public.md +8 -0
- package/types/synthetic/$env+static+private.md +19 -0
- package/types/synthetic/$env+static+public.md +7 -0
- package/types/synthetic/$lib.md +5 -0
- package/CHANGELOG.md +0 -819
- package/assets/components/error.svelte +0 -21
- package/assets/runtime/app/env.js +0 -16
- package/assets/runtime/app/navigation.js +0 -53
- package/assets/runtime/app/paths.js +0 -1
- package/assets/runtime/app/stores.js +0 -87
- package/assets/runtime/chunks/utils.js +0 -13
- package/assets/runtime/env.js +0 -8
- package/assets/runtime/internal/singletons.js +0 -20
- package/assets/runtime/internal/start.js +0 -1061
- package/assets/runtime/paths.js +0 -12
- package/dist/chunks/_commonjsHelpers.js +0 -8
- package/dist/chunks/cert.js +0 -29079
- package/dist/chunks/constants.js +0 -3
- package/dist/chunks/index.js +0 -3526
- package/dist/chunks/index2.js +0 -583
- package/dist/chunks/index3.js +0 -31
- package/dist/chunks/index4.js +0 -1005
- package/dist/chunks/index5.js +0 -327
- package/dist/chunks/index6.js +0 -325
- package/dist/chunks/standard.js +0 -99
- package/dist/chunks/utils.js +0 -149
- package/dist/cli.js +0 -711
- package/dist/http.js +0 -66
- package/dist/install-fetch.js +0 -1699
- package/dist/ssr.js +0 -1529
- package/types/ambient-modules.d.ts +0 -115
- package/types/config.d.ts +0 -101
- package/types/endpoint.d.ts +0 -23
- package/types/helper.d.ts +0 -19
- package/types/hooks.d.ts +0 -23
- package/types/page.d.ts +0 -30
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hash using djb2
|
|
3
|
+
* @param {import('types').StrictBody} value
|
|
4
|
+
*/
|
|
5
|
+
export function hash(value) {
|
|
6
|
+
let hash = 5381;
|
|
7
|
+
|
|
8
|
+
if (typeof value === 'string') {
|
|
9
|
+
let i = value.length;
|
|
10
|
+
while (i) hash = (hash * 33) ^ value.charCodeAt(--i);
|
|
11
|
+
} else if (ArrayBuffer.isView(value)) {
|
|
12
|
+
const buffer = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
13
|
+
let i = buffer.length;
|
|
14
|
+
while (i) hash = (hash * 33) ^ buffer[--i];
|
|
15
|
+
} else {
|
|
16
|
+
throw new TypeError('value must be a string or TypedArray');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return (hash >>> 0).toString(36);
|
|
20
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { parse, serialize } from 'cookie';
|
|
2
|
+
import { normalize_path } from '../../utils/url.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tracks all cookies set during dev mode so we can emit warnings
|
|
6
|
+
* when we detect that there's likely cookie misusage due to wrong paths
|
|
7
|
+
*
|
|
8
|
+
* @type {Record<string, Set<string>>} */
|
|
9
|
+
const cookie_paths = {};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {Request} request
|
|
13
|
+
* @param {URL} url
|
|
14
|
+
* @param {boolean} dev
|
|
15
|
+
* @param {import('types').TrailingSlash} trailing_slash
|
|
16
|
+
*/
|
|
17
|
+
export function get_cookies(request, url, dev, trailing_slash) {
|
|
18
|
+
const header = request.headers.get('cookie') ?? '';
|
|
19
|
+
const initial_cookies = parse(header, { decode: (value) => value });
|
|
20
|
+
|
|
21
|
+
const normalized_url = normalize_path(url.pathname, trailing_slash);
|
|
22
|
+
// Emulate browser-behavior: if the cookie is set at '/foo/bar', its path is '/foo'
|
|
23
|
+
const default_path = normalized_url.split('/').slice(0, -1).join('/') || '/';
|
|
24
|
+
|
|
25
|
+
if (dev) {
|
|
26
|
+
// TODO this could theoretically be wrong if the cookie was set unencoded?
|
|
27
|
+
const initial_decoded_cookies = parse(header, { decode: decodeURIComponent });
|
|
28
|
+
// Remove all cookies that no longer exist according to the request
|
|
29
|
+
for (const name of Object.keys(cookie_paths)) {
|
|
30
|
+
cookie_paths[name] = new Set(
|
|
31
|
+
[...cookie_paths[name]].filter(
|
|
32
|
+
(path) => !path_matches(normalized_url, path) || name in initial_decoded_cookies
|
|
33
|
+
)
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
// Add all new cookies we might not have seen before
|
|
37
|
+
for (const name in initial_decoded_cookies) {
|
|
38
|
+
cookie_paths[name] = cookie_paths[name] ?? new Set();
|
|
39
|
+
if (![...cookie_paths[name]].some((path) => path_matches(normalized_url, path))) {
|
|
40
|
+
cookie_paths[name].add(default_path);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** @type {Record<string, import('./page/types').Cookie>} */
|
|
46
|
+
const new_cookies = {};
|
|
47
|
+
|
|
48
|
+
/** @type {import('cookie').CookieSerializeOptions} */
|
|
49
|
+
const defaults = {
|
|
50
|
+
httpOnly: true,
|
|
51
|
+
sameSite: 'lax',
|
|
52
|
+
secure: url.hostname === 'localhost' && url.protocol === 'http:' ? false : true
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** @type {import('types').Cookies} */
|
|
56
|
+
const cookies = {
|
|
57
|
+
// The JSDoc param annotations appearing below for get, set and delete
|
|
58
|
+
// are necessary to expose the `cookie` library types to
|
|
59
|
+
// typescript users. `@type {import('types').Cookies}` above is not
|
|
60
|
+
// sufficient to do so.
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {string} name
|
|
64
|
+
* @param {import('cookie').CookieParseOptions} opts
|
|
65
|
+
*/
|
|
66
|
+
get(name, opts) {
|
|
67
|
+
const c = new_cookies[name];
|
|
68
|
+
if (
|
|
69
|
+
c &&
|
|
70
|
+
domain_matches(url.hostname, c.options.domain) &&
|
|
71
|
+
path_matches(url.pathname, c.options.path)
|
|
72
|
+
) {
|
|
73
|
+
return c.value;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const decoder = opts?.decode || decodeURIComponent;
|
|
77
|
+
const req_cookies = parse(header, { decode: decoder });
|
|
78
|
+
const cookie = req_cookies[name]; // the decoded string or undefined
|
|
79
|
+
|
|
80
|
+
if (!dev || cookie) {
|
|
81
|
+
return cookie;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const paths = new Set([...(cookie_paths[name] ?? [])]);
|
|
85
|
+
if (c) {
|
|
86
|
+
paths.add(c.options.path ?? default_path);
|
|
87
|
+
}
|
|
88
|
+
if (paths.size > 0) {
|
|
89
|
+
console.warn(
|
|
90
|
+
// prettier-ignore
|
|
91
|
+
`Cookie with name '${name}' was not found at path '${url.pathname}', but a cookie with that name exists at these paths: '${[...paths].join("', '")}'. Did you mean to set its 'path' to '/' instead?`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @param {string} name
|
|
98
|
+
* @param {string} value
|
|
99
|
+
* @param {import('cookie').CookieSerializeOptions} opts
|
|
100
|
+
*/
|
|
101
|
+
set(name, value, opts = {}) {
|
|
102
|
+
let path = opts.path ?? default_path;
|
|
103
|
+
|
|
104
|
+
new_cookies[name] = {
|
|
105
|
+
name,
|
|
106
|
+
value,
|
|
107
|
+
options: {
|
|
108
|
+
...defaults,
|
|
109
|
+
...opts,
|
|
110
|
+
path
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
if (dev) {
|
|
115
|
+
cookie_paths[name] = cookie_paths[name] ?? new Set();
|
|
116
|
+
if (!value) {
|
|
117
|
+
if (!cookie_paths[name].has(path) && cookie_paths[name].size > 0) {
|
|
118
|
+
const paths = `'${Array.from(cookie_paths[name]).join("', '")}'`;
|
|
119
|
+
console.warn(
|
|
120
|
+
`Trying to delete cookie '${name}' at path '${path}', but a cookie with that name only exists at these paths: ${paths}.`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
cookie_paths[name].delete(path);
|
|
124
|
+
} else {
|
|
125
|
+
// We could also emit a warning here if the cookie already exists at a different path,
|
|
126
|
+
// but that's more likely a false positive because it's valid to set the same name at different paths
|
|
127
|
+
cookie_paths[name].add(path);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @param {string} name
|
|
134
|
+
* @param {import('cookie').CookieSerializeOptions} opts
|
|
135
|
+
*/
|
|
136
|
+
delete(name, opts = {}) {
|
|
137
|
+
cookies.set(name, '', {
|
|
138
|
+
...opts,
|
|
139
|
+
maxAge: 0
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @param {string} name
|
|
145
|
+
* @param {string} value
|
|
146
|
+
* @param {import('cookie').CookieSerializeOptions} opts
|
|
147
|
+
*/
|
|
148
|
+
serialize(name, value, opts) {
|
|
149
|
+
return serialize(name, value, {
|
|
150
|
+
...defaults,
|
|
151
|
+
...opts
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @param {URL} destination
|
|
158
|
+
* @param {string | null} header
|
|
159
|
+
*/
|
|
160
|
+
function get_cookie_header(destination, header) {
|
|
161
|
+
/** @type {Record<string, string>} */
|
|
162
|
+
const combined_cookies = {
|
|
163
|
+
// cookies sent by the user agent have lowest precedence
|
|
164
|
+
...initial_cookies
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// cookies previous set during this event with cookies.set have higher precedence
|
|
168
|
+
for (const key in new_cookies) {
|
|
169
|
+
const cookie = new_cookies[key];
|
|
170
|
+
if (!domain_matches(destination.hostname, cookie.options.domain)) continue;
|
|
171
|
+
if (!path_matches(destination.pathname, cookie.options.path)) continue;
|
|
172
|
+
|
|
173
|
+
const encoder = cookie.options.encode || encodeURIComponent;
|
|
174
|
+
combined_cookies[cookie.name] = encoder(cookie.value);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// explicit header has highest precedence
|
|
178
|
+
if (header) {
|
|
179
|
+
const parsed = parse(header, { decode: (value) => value });
|
|
180
|
+
for (const name in parsed) {
|
|
181
|
+
combined_cookies[name] = parsed[name];
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return Object.entries(combined_cookies)
|
|
186
|
+
.map(([name, value]) => `${name}=${value}`)
|
|
187
|
+
.join('; ');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return { cookies, new_cookies, get_cookie_header };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* @param {string} hostname
|
|
195
|
+
* @param {string} [constraint]
|
|
196
|
+
*/
|
|
197
|
+
export function domain_matches(hostname, constraint) {
|
|
198
|
+
if (!constraint) return true;
|
|
199
|
+
|
|
200
|
+
const normalized = constraint[0] === '.' ? constraint.slice(1) : constraint;
|
|
201
|
+
|
|
202
|
+
if (hostname === normalized) return true;
|
|
203
|
+
return hostname.endsWith('.' + normalized);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* @param {string} path
|
|
208
|
+
* @param {string} [constraint]
|
|
209
|
+
*/
|
|
210
|
+
export function path_matches(path, constraint) {
|
|
211
|
+
if (!constraint) return true;
|
|
212
|
+
|
|
213
|
+
const normalized = constraint.endsWith('/') ? constraint.slice(0, -1) : constraint;
|
|
214
|
+
|
|
215
|
+
if (path === normalized) return true;
|
|
216
|
+
return path.startsWith(normalized + '/');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @param {Headers} headers
|
|
221
|
+
* @param {import('./page/types').Cookie[]} cookies
|
|
222
|
+
*/
|
|
223
|
+
export function add_cookies_to_headers(headers, cookies) {
|
|
224
|
+
for (const new_cookie of cookies) {
|
|
225
|
+
const { name, value, options } = new_cookie;
|
|
226
|
+
headers.append('set-cookie', serialize(name, value, options));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { HttpError, Redirect } from '../../control.js';
|
|
2
|
+
import { normalize_error } from '../../../utils/error.js';
|
|
3
|
+
import { once } from '../../../utils/functions.js';
|
|
4
|
+
import { load_server_data } from '../page/load_data.js';
|
|
5
|
+
import { clarify_devalue_error, handle_error_and_jsonify, serialize_data_node } from '../utils.js';
|
|
6
|
+
import { normalize_path } from '../../../utils/url.js';
|
|
7
|
+
|
|
8
|
+
export const INVALIDATED_PARAM = 'x-sveltekit-invalidated';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {import('types').RequestEvent} event
|
|
12
|
+
* @param {import('types').SSRRoute} route
|
|
13
|
+
* @param {import('types').SSROptions} options
|
|
14
|
+
* @param {import('types').SSRState} state
|
|
15
|
+
* @param {boolean[] | undefined} invalidated_data_nodes
|
|
16
|
+
* @param {import('types').TrailingSlash} trailing_slash
|
|
17
|
+
* @returns {Promise<Response>}
|
|
18
|
+
*/
|
|
19
|
+
export async function render_data(
|
|
20
|
+
event,
|
|
21
|
+
route,
|
|
22
|
+
options,
|
|
23
|
+
state,
|
|
24
|
+
invalidated_data_nodes,
|
|
25
|
+
trailing_slash
|
|
26
|
+
) {
|
|
27
|
+
if (!route.page) {
|
|
28
|
+
// requesting /__data.json should fail for a +server.js
|
|
29
|
+
return new Response(undefined, {
|
|
30
|
+
status: 404
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const node_ids = [...route.page.layouts, route.page.leaf];
|
|
36
|
+
const invalidated = invalidated_data_nodes ?? node_ids.map(() => true);
|
|
37
|
+
|
|
38
|
+
let aborted = false;
|
|
39
|
+
|
|
40
|
+
const url = new URL(event.url);
|
|
41
|
+
url.pathname = normalize_path(url.pathname, trailing_slash);
|
|
42
|
+
|
|
43
|
+
const new_event = { ...event, url };
|
|
44
|
+
|
|
45
|
+
const functions = node_ids.map((n, i) => {
|
|
46
|
+
return once(async () => {
|
|
47
|
+
try {
|
|
48
|
+
if (aborted) {
|
|
49
|
+
return /** @type {import('types').ServerDataSkippedNode} */ ({
|
|
50
|
+
type: 'skip'
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// == because it could be undefined (in dev) or null (in build, because of JSON.stringify)
|
|
55
|
+
const node = n == undefined ? n : await options.manifest._.nodes[n]();
|
|
56
|
+
return load_server_data({
|
|
57
|
+
event: new_event,
|
|
58
|
+
options,
|
|
59
|
+
state,
|
|
60
|
+
node,
|
|
61
|
+
parent: async () => {
|
|
62
|
+
/** @type {Record<string, any>} */
|
|
63
|
+
const data = {};
|
|
64
|
+
for (let j = 0; j < i; j += 1) {
|
|
65
|
+
const parent = /** @type {import('types').ServerDataNode | null} */ (
|
|
66
|
+
await functions[j]()
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (parent) {
|
|
70
|
+
Object.assign(data, parent.data);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return data;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
} catch (e) {
|
|
77
|
+
aborted = true;
|
|
78
|
+
throw e;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const promises = functions.map(async (fn, i) => {
|
|
84
|
+
if (!invalidated[i]) {
|
|
85
|
+
return /** @type {import('types').ServerDataSkippedNode} */ ({
|
|
86
|
+
type: 'skip'
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return fn();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
let length = promises.length;
|
|
94
|
+
const nodes = await Promise.all(
|
|
95
|
+
promises.map((p, i) =>
|
|
96
|
+
p.catch(async (error) => {
|
|
97
|
+
if (error instanceof Redirect) {
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Math.min because array isn't guaranteed to resolve in order
|
|
102
|
+
length = Math.min(length, i + 1);
|
|
103
|
+
|
|
104
|
+
return /** @type {import('types').ServerErrorNode} */ ({
|
|
105
|
+
type: 'error',
|
|
106
|
+
error: await handle_error_and_jsonify(event, options, error),
|
|
107
|
+
status: error instanceof HttpError ? error.status : undefined
|
|
108
|
+
});
|
|
109
|
+
})
|
|
110
|
+
)
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const stubs = nodes.slice(0, length).map(serialize_data_node);
|
|
115
|
+
|
|
116
|
+
const json = `{"type":"data","nodes":[${stubs.join(',')}]}`;
|
|
117
|
+
return json_response(json);
|
|
118
|
+
} catch (e) {
|
|
119
|
+
const error = /** @type {any} */ (e);
|
|
120
|
+
return json_response(JSON.stringify(clarify_devalue_error(event, error)), 500);
|
|
121
|
+
}
|
|
122
|
+
} catch (e) {
|
|
123
|
+
const error = normalize_error(e);
|
|
124
|
+
|
|
125
|
+
if (error instanceof Redirect) {
|
|
126
|
+
return redirect_json_response(error);
|
|
127
|
+
} else {
|
|
128
|
+
// TODO make it clearer that this was an unexpected error
|
|
129
|
+
return json_response(JSON.stringify(await handle_error_and_jsonify(event, options, error)));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @param {string} json
|
|
136
|
+
* @param {number} [status]
|
|
137
|
+
*/
|
|
138
|
+
function json_response(json, status = 200) {
|
|
139
|
+
return new Response(json, {
|
|
140
|
+
status,
|
|
141
|
+
headers: {
|
|
142
|
+
'content-type': 'application/json',
|
|
143
|
+
'cache-control': 'private, no-store'
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @param {Redirect} redirect
|
|
150
|
+
*/
|
|
151
|
+
export function redirect_json_response(redirect) {
|
|
152
|
+
return json_response(
|
|
153
|
+
JSON.stringify({
|
|
154
|
+
type: 'redirect',
|
|
155
|
+
location: redirect.location
|
|
156
|
+
})
|
|
157
|
+
);
|
|
158
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { negotiate } from '../../utils/http.js';
|
|
2
|
+
import { Redirect } from '../control.js';
|
|
3
|
+
import { method_not_allowed } from './utils.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {import('types').RequestEvent} event
|
|
7
|
+
* @param {import('types').SSREndpoint} mod
|
|
8
|
+
* @param {import('types').SSRState} state
|
|
9
|
+
* @returns {Promise<Response>}
|
|
10
|
+
*/
|
|
11
|
+
export async function render_endpoint(event, mod, state) {
|
|
12
|
+
const method = /** @type {import('types').HttpMethod} */ (event.request.method);
|
|
13
|
+
|
|
14
|
+
let handler = mod[method];
|
|
15
|
+
|
|
16
|
+
if (!handler && method === 'HEAD') {
|
|
17
|
+
handler = mod.GET;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!handler) {
|
|
21
|
+
return method_not_allowed(mod, method);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const prerender = mod.prerender ?? state.prerender_default;
|
|
25
|
+
|
|
26
|
+
if (prerender && (mod.POST || mod.PATCH || mod.PUT || mod.DELETE)) {
|
|
27
|
+
throw new Error('Cannot prerender endpoints that have mutative methods');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (state.prerendering && !prerender) {
|
|
31
|
+
if (state.initiator) {
|
|
32
|
+
// if request came from a prerendered page, bail
|
|
33
|
+
throw new Error(`${event.route.id} is not prerenderable`);
|
|
34
|
+
} else {
|
|
35
|
+
// if request came direct from the crawler, signal that
|
|
36
|
+
// this route cannot be prerendered, but don't bail
|
|
37
|
+
return new Response(undefined, { status: 204 });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const response = await handler(
|
|
43
|
+
/** @type {import('types').RequestEvent<Record<string, any>>} */ (event)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
if (!(response instanceof Response)) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`Invalid response from route ${event.url.pathname}: handler should return a Response object`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (state.prerendering) {
|
|
53
|
+
response.headers.set('x-sveltekit-prerender', String(prerender));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return response;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if (error instanceof Redirect) {
|
|
59
|
+
return new Response(undefined, {
|
|
60
|
+
status: error.status,
|
|
61
|
+
headers: { location: error.location }
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @param {import('types').RequestEvent} event
|
|
71
|
+
*/
|
|
72
|
+
export function is_endpoint_request(event) {
|
|
73
|
+
const { method, headers } = event.request;
|
|
74
|
+
|
|
75
|
+
if (method === 'PUT' || method === 'PATCH' || method === 'DELETE') {
|
|
76
|
+
// These methods exist exclusively for endpoints
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// use:enhance uses a custom header to disambiguate
|
|
81
|
+
if (method === 'POST' && headers.get('x-sveltekit-action') === 'true') return false;
|
|
82
|
+
|
|
83
|
+
// GET/POST requests may be for endpoints or pages. We prefer endpoints if this isn't a text/html request
|
|
84
|
+
const accept = event.request.headers.get('accept') ?? '*/*';
|
|
85
|
+
return negotiate(accept, ['*', 'text/html']) !== 'text/html';
|
|
86
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import * as set_cookie_parser from 'set-cookie-parser';
|
|
2
|
+
import { respond } from './index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {{
|
|
6
|
+
* event: import('types').RequestEvent;
|
|
7
|
+
* options: import('types').SSROptions;
|
|
8
|
+
* state: import('types').SSRState;
|
|
9
|
+
* get_cookie_header: (url: URL, header: string | null) => string;
|
|
10
|
+
* }} opts
|
|
11
|
+
* @returns {typeof fetch}
|
|
12
|
+
*/
|
|
13
|
+
export function create_fetch({ event, options, state, get_cookie_header }) {
|
|
14
|
+
return async (info, init) => {
|
|
15
|
+
const original_request = normalize_fetch_input(info, init, event.url);
|
|
16
|
+
|
|
17
|
+
const request_body = init?.body;
|
|
18
|
+
|
|
19
|
+
// some runtimes (e.g. Cloudflare) error if you access `request.mode`,
|
|
20
|
+
// annoyingly, so we need to read the value from the `init` object instead
|
|
21
|
+
let mode = (info instanceof Request ? info.mode : init?.mode) ?? 'cors';
|
|
22
|
+
let credentials =
|
|
23
|
+
(info instanceof Request ? info.credentials : init?.credentials) ?? 'same-origin';
|
|
24
|
+
|
|
25
|
+
return await options.hooks.handleFetch({
|
|
26
|
+
event,
|
|
27
|
+
request: original_request,
|
|
28
|
+
fetch: async (info, init) => {
|
|
29
|
+
const request = normalize_fetch_input(info, init, event.url);
|
|
30
|
+
|
|
31
|
+
const url = new URL(request.url);
|
|
32
|
+
|
|
33
|
+
if (!request.headers.has('origin')) {
|
|
34
|
+
request.headers.set('origin', event.url.origin);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (info !== original_request) {
|
|
38
|
+
mode = (info instanceof Request ? info.mode : init?.mode) ?? 'cors';
|
|
39
|
+
credentials =
|
|
40
|
+
(info instanceof Request ? info.credentials : init?.credentials) ?? 'same-origin';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Remove Origin, according to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin#description
|
|
44
|
+
if (
|
|
45
|
+
(request.method === 'GET' || request.method === 'HEAD') &&
|
|
46
|
+
((mode === 'no-cors' && url.origin !== event.url.origin) ||
|
|
47
|
+
url.origin === event.url.origin)
|
|
48
|
+
) {
|
|
49
|
+
request.headers.delete('origin');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (url.origin !== event.url.origin) {
|
|
53
|
+
// allow cookie passthrough for "same-origin"
|
|
54
|
+
// if SvelteKit is serving my.domain.com:
|
|
55
|
+
// - domain.com WILL NOT receive cookies
|
|
56
|
+
// - my.domain.com WILL receive cookies
|
|
57
|
+
// - api.domain.dom WILL NOT receive cookies
|
|
58
|
+
// - sub.my.domain.com WILL receive cookies
|
|
59
|
+
// ports do not affect the resolution
|
|
60
|
+
// leading dot prevents mydomain.com matching domain.com
|
|
61
|
+
if (`.${url.hostname}`.endsWith(`.${event.url.hostname}`) && credentials !== 'omit') {
|
|
62
|
+
const cookie = get_cookie_header(url, request.headers.get('cookie'));
|
|
63
|
+
if (cookie) request.headers.set('cookie', cookie);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let response = await fetch(request);
|
|
67
|
+
|
|
68
|
+
if (mode === 'no-cors') {
|
|
69
|
+
response = new Response('', {
|
|
70
|
+
status: response.status,
|
|
71
|
+
statusText: response.statusText,
|
|
72
|
+
headers: response.headers
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return response;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** @type {Response} */
|
|
80
|
+
let response;
|
|
81
|
+
|
|
82
|
+
// handle fetch requests for static assets. e.g. prebaked data, etc.
|
|
83
|
+
// we need to support everything the browser's fetch supports
|
|
84
|
+
const prefix = options.paths.assets || options.paths.base;
|
|
85
|
+
const decoded = decodeURIComponent(url.pathname);
|
|
86
|
+
const filename = (
|
|
87
|
+
decoded.startsWith(prefix) ? decoded.slice(prefix.length) : decoded
|
|
88
|
+
).slice(1);
|
|
89
|
+
const filename_html = `${filename}/index.html`; // path may also match path/index.html
|
|
90
|
+
|
|
91
|
+
const is_asset = options.manifest.assets.has(filename);
|
|
92
|
+
const is_asset_html = options.manifest.assets.has(filename_html);
|
|
93
|
+
|
|
94
|
+
if (is_asset || is_asset_html) {
|
|
95
|
+
const file = is_asset ? filename : filename_html;
|
|
96
|
+
|
|
97
|
+
if (options.read) {
|
|
98
|
+
const type = is_asset
|
|
99
|
+
? options.manifest.mimeTypes[filename.slice(filename.lastIndexOf('.'))]
|
|
100
|
+
: 'text/html';
|
|
101
|
+
|
|
102
|
+
return new Response(options.read(file), {
|
|
103
|
+
headers: type ? { 'content-type': type } : {}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return await fetch(request);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (credentials !== 'omit') {
|
|
111
|
+
const cookie = get_cookie_header(url, request.headers.get('cookie'));
|
|
112
|
+
if (cookie) {
|
|
113
|
+
request.headers.set('cookie', cookie);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const authorization = event.request.headers.get('authorization');
|
|
117
|
+
if (authorization && !request.headers.has('authorization')) {
|
|
118
|
+
request.headers.set('authorization', authorization);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (request_body && typeof request_body !== 'string' && !ArrayBuffer.isView(request_body)) {
|
|
123
|
+
// TODO is this still necessary? we just bail out below
|
|
124
|
+
// per https://developer.mozilla.org/en-US/docs/Web/API/Request/Request, this can be a
|
|
125
|
+
// Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object.
|
|
126
|
+
// non-string bodies are irksome to deal with, but luckily aren't particularly useful
|
|
127
|
+
// in this context anyway, so we take the easy route and ban them
|
|
128
|
+
throw new Error('Request body must be a string or TypedArray');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (!request.headers.has('accept')) {
|
|
132
|
+
request.headers.set('accept', '*/*');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!request.headers.has('accept-language')) {
|
|
136
|
+
request.headers.set(
|
|
137
|
+
'accept-language',
|
|
138
|
+
/** @type {string} */ (event.request.headers.get('accept-language'))
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
response = await respond(request, options, state);
|
|
143
|
+
|
|
144
|
+
const set_cookie = response.headers.get('set-cookie');
|
|
145
|
+
if (set_cookie) {
|
|
146
|
+
for (const str of set_cookie_parser.splitCookiesString(set_cookie)) {
|
|
147
|
+
const { name, value, ...options } = set_cookie_parser.parseString(str);
|
|
148
|
+
|
|
149
|
+
// options.sameSite is string, something more specific is required - type cast is safe
|
|
150
|
+
event.cookies.set(
|
|
151
|
+
name,
|
|
152
|
+
value,
|
|
153
|
+
/** @type {import('cookie').CookieSerializeOptions} */ (options)
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return response;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* @param {RequestInfo | URL} info
|
|
166
|
+
* @param {RequestInit | undefined} init
|
|
167
|
+
* @param {URL} url
|
|
168
|
+
*/
|
|
169
|
+
function normalize_fetch_input(info, init, url) {
|
|
170
|
+
if (info instanceof Request) {
|
|
171
|
+
return info;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return new Request(typeof info === 'string' ? new URL(info, url) : info, init);
|
|
175
|
+
}
|