@sveltejs/kit 1.0.0-next.51 → 1.0.0-next.511
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 -67
- package/postinstall.js +47 -0
- package/scripts/special-types/$env+dynamic+private.md +10 -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 +5 -0
- package/src/cli.js +108 -0
- package/src/constants.js +7 -0
- package/src/core/adapt/builder.js +206 -0
- package/src/core/adapt/index.js +31 -0
- package/src/core/config/default-error.html +56 -0
- package/src/core/config/index.js +110 -0
- package/src/core/config/options.js +504 -0
- package/src/core/config/types.d.ts +1 -0
- package/src/core/env.js +121 -0
- package/src/core/generate_manifest/index.js +93 -0
- package/src/core/prerender/crawl.js +198 -0
- package/src/core/prerender/entities.js +2252 -0
- package/src/core/prerender/prerender.js +458 -0
- package/src/core/prerender/queue.js +80 -0
- package/src/core/sync/create_manifest_data/index.js +488 -0
- package/src/core/sync/create_manifest_data/types.d.ts +37 -0
- package/src/core/sync/sync.js +70 -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 +106 -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 +783 -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 +161 -0
- package/src/exports/node/polyfills.js +28 -0
- package/src/exports/vite/build/build_server.js +378 -0
- package/src/exports/vite/build/build_service_worker.js +91 -0
- package/src/exports/vite/build/utils.js +180 -0
- package/src/exports/vite/dev/index.js +577 -0
- package/src/exports/vite/graph_analysis/index.js +277 -0
- package/src/exports/vite/graph_analysis/types.d.ts +5 -0
- package/src/exports/vite/graph_analysis/utils.js +30 -0
- package/src/exports/vite/index.js +598 -0
- package/src/exports/vite/preview/index.js +189 -0
- package/src/exports/vite/types.d.ts +3 -0
- package/src/exports/vite/utils.js +157 -0
- package/src/runtime/app/env.js +1 -0
- package/src/runtime/app/environment.js +11 -0
- package/src/runtime/app/forms.js +122 -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 +30 -0
- package/src/runtime/client/client.js +1587 -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 +37 -0
- package/src/runtime/client/types.d.ts +84 -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 +98 -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 +20 -0
- package/src/runtime/paths.js +11 -0
- package/src/runtime/server/cookie.js +166 -0
- package/src/runtime/server/data/index.js +136 -0
- package/src/runtime/server/endpoint.js +92 -0
- package/src/runtime/server/fetch.js +174 -0
- package/src/runtime/server/index.js +349 -0
- package/src/runtime/server/page/actions.js +243 -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 +301 -0
- package/src/runtime/server/page/load_data.js +211 -0
- package/src/runtime/server/page/render.js +342 -0
- package/src/runtime/server/page/respond_with_error.js +100 -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 +176 -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 +137 -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/promises.js +17 -0
- package/src/utils/routing.js +117 -0
- package/src/utils/unit_test.js +11 -0
- package/src/utils/url.js +142 -0
- package/svelte-kit.js +1 -1
- package/types/ambient.d.ts +431 -0
- package/types/index.d.ts +474 -0
- package/types/internal.d.ts +378 -0
- package/types/private.d.ts +224 -0
- package/CHANGELOG.md +0 -490
- 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 -776
- package/assets/runtime/paths.js +0 -12
- package/dist/chunks/index.js +0 -3528
- package/dist/chunks/index2.js +0 -587
- package/dist/chunks/index3.js +0 -246
- package/dist/chunks/index4.js +0 -536
- 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 -550
- package/dist/ssr.js +0 -2599
- package/types.d.ts +0 -72
- package/types.internal.d.ts +0 -217
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { parse, serialize } from 'cookie';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {Request} request
|
|
5
|
+
* @param {URL} url
|
|
6
|
+
*/
|
|
7
|
+
export function get_cookies(request, url) {
|
|
8
|
+
const header = request.headers.get('cookie') ?? '';
|
|
9
|
+
|
|
10
|
+
const initial_cookies = parse(header);
|
|
11
|
+
|
|
12
|
+
/** @type {Record<string, import('./page/types').Cookie>} */
|
|
13
|
+
const new_cookies = {};
|
|
14
|
+
|
|
15
|
+
/** @type {import('cookie').CookieSerializeOptions} */
|
|
16
|
+
const defaults = {
|
|
17
|
+
httpOnly: true,
|
|
18
|
+
sameSite: 'lax',
|
|
19
|
+
secure: url.hostname === 'localhost' && url.protocol === 'http:' ? false : true
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** @type {import('types').Cookies} */
|
|
23
|
+
const cookies = {
|
|
24
|
+
// The JSDoc param annotations appearing below for get, set and delete
|
|
25
|
+
// are necessary to expose the `cookie` library types to
|
|
26
|
+
// typescript users. `@type {import('types').Cookies}` above is not
|
|
27
|
+
// sufficient to do so.
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {string} name
|
|
31
|
+
* @param {import('cookie').CookieParseOptions} opts
|
|
32
|
+
*/
|
|
33
|
+
get(name, opts) {
|
|
34
|
+
const c = new_cookies[name];
|
|
35
|
+
if (
|
|
36
|
+
c &&
|
|
37
|
+
domain_matches(url.hostname, c.options.domain) &&
|
|
38
|
+
path_matches(url.pathname, c.options.path)
|
|
39
|
+
) {
|
|
40
|
+
return c.value;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const decode = opts?.decode || decodeURIComponent;
|
|
44
|
+
const req_cookies = parse(header, { decode });
|
|
45
|
+
return req_cookies[name]; // the decoded string or undefined
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} name
|
|
50
|
+
* @param {string} value
|
|
51
|
+
* @param {import('cookie').CookieSerializeOptions} opts
|
|
52
|
+
*/
|
|
53
|
+
set(name, value, opts = {}) {
|
|
54
|
+
new_cookies[name] = {
|
|
55
|
+
name,
|
|
56
|
+
value,
|
|
57
|
+
options: {
|
|
58
|
+
...defaults,
|
|
59
|
+
...opts
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {string} name
|
|
66
|
+
* @param {import('cookie').CookieSerializeOptions} opts
|
|
67
|
+
*/
|
|
68
|
+
delete(name, opts = {}) {
|
|
69
|
+
new_cookies[name] = {
|
|
70
|
+
name,
|
|
71
|
+
value: '',
|
|
72
|
+
options: {
|
|
73
|
+
...defaults,
|
|
74
|
+
...opts,
|
|
75
|
+
maxAge: 0
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {string} name
|
|
82
|
+
* @param {string} value
|
|
83
|
+
* @param {import('cookie').CookieSerializeOptions} opts
|
|
84
|
+
*/
|
|
85
|
+
serialize(name, value, opts) {
|
|
86
|
+
return serialize(name, value, {
|
|
87
|
+
...defaults,
|
|
88
|
+
...opts
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @param {URL} destination
|
|
95
|
+
* @param {string | null} header
|
|
96
|
+
*/
|
|
97
|
+
function get_cookie_header(destination, header) {
|
|
98
|
+
/** @type {Record<string, string>} */
|
|
99
|
+
const combined_cookies = {};
|
|
100
|
+
|
|
101
|
+
// cookies sent by the user agent have lowest precedence
|
|
102
|
+
for (const name in initial_cookies) {
|
|
103
|
+
combined_cookies[name] = initial_cookies[name];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// cookies previous set during this event with cookies.set have higher precedence
|
|
107
|
+
for (const key in new_cookies) {
|
|
108
|
+
const cookie = new_cookies[key];
|
|
109
|
+
if (!domain_matches(destination.hostname, cookie.options.domain)) continue;
|
|
110
|
+
if (!path_matches(destination.pathname, cookie.options.path)) continue;
|
|
111
|
+
|
|
112
|
+
combined_cookies[cookie.name] = cookie.value;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// explicit header has highest precedence
|
|
116
|
+
if (header) {
|
|
117
|
+
const parsed = parse(header);
|
|
118
|
+
for (const name in parsed) {
|
|
119
|
+
combined_cookies[name] = parsed[name];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return Object.entries(combined_cookies)
|
|
124
|
+
.map(([name, value]) => `${name}=${value}`)
|
|
125
|
+
.join('; ');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { cookies, new_cookies, get_cookie_header };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @param {string} hostname
|
|
133
|
+
* @param {string} [constraint]
|
|
134
|
+
*/
|
|
135
|
+
export function domain_matches(hostname, constraint) {
|
|
136
|
+
if (!constraint) return true;
|
|
137
|
+
|
|
138
|
+
const normalized = constraint[0] === '.' ? constraint.slice(1) : constraint;
|
|
139
|
+
|
|
140
|
+
if (hostname === normalized) return true;
|
|
141
|
+
return hostname.endsWith('.' + normalized);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @param {string} path
|
|
146
|
+
* @param {string} [constraint]
|
|
147
|
+
*/
|
|
148
|
+
export function path_matches(path, constraint) {
|
|
149
|
+
if (!constraint) return true;
|
|
150
|
+
|
|
151
|
+
const normalized = constraint.endsWith('/') ? constraint.slice(0, -1) : constraint;
|
|
152
|
+
|
|
153
|
+
if (path === normalized) return true;
|
|
154
|
+
return path.startsWith(normalized + '/');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @param {Headers} headers
|
|
159
|
+
* @param {import('./page/types').Cookie[]} cookies
|
|
160
|
+
*/
|
|
161
|
+
export function add_cookies_to_headers(headers, cookies) {
|
|
162
|
+
for (const new_cookie of cookies) {
|
|
163
|
+
const { name, value, options } = new_cookie;
|
|
164
|
+
headers.append('set-cookie', serialize(name, value, options));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
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 { data_response, handle_error_and_jsonify } from '../utils.js';
|
|
6
|
+
import { normalize_path } from '../../../utils/url.js';
|
|
7
|
+
import { DATA_SUFFIX } from '../../../constants.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {import('types').RequestEvent} event
|
|
11
|
+
* @param {import('types').SSRRoute} route
|
|
12
|
+
* @param {import('types').SSROptions} options
|
|
13
|
+
* @param {import('types').SSRState} state
|
|
14
|
+
* @returns {Promise<Response>}
|
|
15
|
+
*/
|
|
16
|
+
export async function render_data(event, route, options, state) {
|
|
17
|
+
if (!route.page) {
|
|
18
|
+
// requesting /__data.js should fail for a +server.js
|
|
19
|
+
return new Response(undefined, {
|
|
20
|
+
status: 404
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const node_ids = [...route.page.layouts, route.page.leaf];
|
|
26
|
+
|
|
27
|
+
const invalidated =
|
|
28
|
+
event.url.searchParams
|
|
29
|
+
.get('__invalid')
|
|
30
|
+
?.split('')
|
|
31
|
+
.map((x) => x === 'y') ?? node_ids.map(() => true);
|
|
32
|
+
|
|
33
|
+
let aborted = false;
|
|
34
|
+
|
|
35
|
+
const url = new URL(event.url);
|
|
36
|
+
url.pathname = normalize_path(
|
|
37
|
+
url.pathname.slice(0, -DATA_SUFFIX.length),
|
|
38
|
+
options.trailing_slash
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
url.searchParams.delete('__invalid');
|
|
42
|
+
url.searchParams.delete('__id');
|
|
43
|
+
|
|
44
|
+
const new_event = { ...event, url };
|
|
45
|
+
|
|
46
|
+
const functions = node_ids.map((n, i) => {
|
|
47
|
+
return once(async () => {
|
|
48
|
+
try {
|
|
49
|
+
if (aborted) {
|
|
50
|
+
return /** @type {import('types').ServerDataSkippedNode} */ ({
|
|
51
|
+
type: 'skip'
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// == because it could be undefined (in dev) or null (in build, because of JSON.stringify)
|
|
56
|
+
const node = n == undefined ? n : await options.manifest._.nodes[n]();
|
|
57
|
+
return load_server_data({
|
|
58
|
+
event: new_event,
|
|
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((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: handle_error_and_jsonify(event, options, error),
|
|
107
|
+
status: error instanceof HttpError ? error.status : undefined
|
|
108
|
+
});
|
|
109
|
+
})
|
|
110
|
+
)
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
/** @type {import('types').ServerData} */
|
|
114
|
+
const server_data = {
|
|
115
|
+
type: 'data',
|
|
116
|
+
nodes: nodes.slice(0, length)
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
return data_response(server_data);
|
|
120
|
+
} catch (e) {
|
|
121
|
+
const error = normalize_error(e);
|
|
122
|
+
|
|
123
|
+
if (error instanceof Redirect) {
|
|
124
|
+
/** @type {import('types').ServerData} */
|
|
125
|
+
const server_data = {
|
|
126
|
+
type: 'redirect',
|
|
127
|
+
location: error.location
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
return data_response(server_data);
|
|
131
|
+
} else {
|
|
132
|
+
// TODO make it clearer that this was an unexpected error
|
|
133
|
+
return data_response(handle_error_and_jsonify(event, options, error));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { json } from '../../exports/index.js';
|
|
2
|
+
import { negotiate } from '../../utils/http.js';
|
|
3
|
+
import { Redirect, ValidationError } from '../control.js';
|
|
4
|
+
import { check_method_names, method_not_allowed } from './utils.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {import('types').RequestEvent} event
|
|
8
|
+
* @param {import('types').SSREndpoint} mod
|
|
9
|
+
* @param {import('types').SSRState} state
|
|
10
|
+
* @returns {Promise<Response>}
|
|
11
|
+
*/
|
|
12
|
+
export async function render_endpoint(event, mod, state) {
|
|
13
|
+
const method = /** @type {import('types').HttpMethod} */ (event.request.method);
|
|
14
|
+
|
|
15
|
+
// TODO: Remove for 1.0
|
|
16
|
+
check_method_names(mod);
|
|
17
|
+
|
|
18
|
+
let handler = mod[method];
|
|
19
|
+
|
|
20
|
+
if (!handler && method === 'HEAD') {
|
|
21
|
+
handler = mod.GET;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!handler) {
|
|
25
|
+
return method_not_allowed(mod, method);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const prerender = mod.prerender ?? state.prerender_default;
|
|
29
|
+
|
|
30
|
+
if (prerender && (mod.POST || mod.PATCH || mod.PUT || mod.DELETE)) {
|
|
31
|
+
throw new Error('Cannot prerender endpoints that have mutative methods');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (state.prerendering && !prerender) {
|
|
35
|
+
if (state.initiator) {
|
|
36
|
+
// if request came from a prerendered page, bail
|
|
37
|
+
throw new Error(`${event.routeId} is not prerenderable`);
|
|
38
|
+
} else {
|
|
39
|
+
// if request came direct from the crawler, signal that
|
|
40
|
+
// this route cannot be prerendered, but don't bail
|
|
41
|
+
return new Response(undefined, { status: 204 });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const response = await handler(
|
|
47
|
+
/** @type {import('types').RequestEvent<Record<string, any>>} */ (event)
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
if (!(response instanceof Response)) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Invalid response from route ${event.url.pathname}: handler should return a Response object`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (state.prerendering) {
|
|
57
|
+
response.headers.set('x-sveltekit-prerender', String(prerender));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return response;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (error instanceof Redirect) {
|
|
63
|
+
return new Response(undefined, {
|
|
64
|
+
status: error.status,
|
|
65
|
+
headers: { location: error.location }
|
|
66
|
+
});
|
|
67
|
+
} else if (error instanceof ValidationError) {
|
|
68
|
+
return json(error.data, { status: error.status });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param {import('types').RequestEvent} event
|
|
77
|
+
*/
|
|
78
|
+
export function is_endpoint_request(event) {
|
|
79
|
+
const { method, headers } = event.request;
|
|
80
|
+
|
|
81
|
+
if (method === 'PUT' || method === 'PATCH' || method === 'DELETE') {
|
|
82
|
+
// These methods exist exclusively for endpoints
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// use:enhance uses a custom header to disambiguate
|
|
87
|
+
if (method === 'POST' && headers.get('x-sveltekit-action') === 'true') return false;
|
|
88
|
+
|
|
89
|
+
// GET/POST requests may be for endpoints or pages. We prefer endpoints if this isn't a text/html request
|
|
90
|
+
const accept = event.request.headers.get('accept') ?? '*/*';
|
|
91
|
+
return negotiate(accept, ['*', 'text/html']) !== 'text/html';
|
|
92
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
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 request = normalize_fetch_input(info, init, event.url);
|
|
16
|
+
|
|
17
|
+
const request_body = init?.body;
|
|
18
|
+
|
|
19
|
+
/** @type {import('types').PrerenderDependency} */
|
|
20
|
+
let dependency;
|
|
21
|
+
|
|
22
|
+
return await options.hooks.handleFetch({
|
|
23
|
+
event,
|
|
24
|
+
request,
|
|
25
|
+
fetch: async (info, init) => {
|
|
26
|
+
const request = normalize_fetch_input(info, init, event.url);
|
|
27
|
+
|
|
28
|
+
const url = new URL(request.url);
|
|
29
|
+
|
|
30
|
+
if (!request.headers.has('origin')) {
|
|
31
|
+
request.headers.set('origin', event.url.origin);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Remove Origin, according to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin#description
|
|
35
|
+
if (
|
|
36
|
+
(request.method === 'GET' || request.method === 'HEAD') &&
|
|
37
|
+
((request.mode === 'no-cors' && url.origin !== event.url.origin) ||
|
|
38
|
+
url.origin === event.url.origin)
|
|
39
|
+
) {
|
|
40
|
+
request.headers.delete('origin');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (url.origin !== event.url.origin) {
|
|
44
|
+
// allow cookie passthrough for "same-origin"
|
|
45
|
+
// if SvelteKit is serving my.domain.com:
|
|
46
|
+
// - domain.com WILL NOT receive cookies
|
|
47
|
+
// - my.domain.com WILL receive cookies
|
|
48
|
+
// - api.domain.dom WILL NOT receive cookies
|
|
49
|
+
// - sub.my.domain.com WILL receive cookies
|
|
50
|
+
// ports do not affect the resolution
|
|
51
|
+
// leading dot prevents mydomain.com matching domain.com
|
|
52
|
+
if (
|
|
53
|
+
`.${url.hostname}`.endsWith(`.${event.url.hostname}`) &&
|
|
54
|
+
request.credentials !== 'omit'
|
|
55
|
+
) {
|
|
56
|
+
const cookie = get_cookie_header(url, request.headers.get('cookie'));
|
|
57
|
+
if (cookie) request.headers.set('cookie', cookie);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let response = await fetch(request);
|
|
61
|
+
|
|
62
|
+
if (request.mode === 'no-cors') {
|
|
63
|
+
response = new Response('', {
|
|
64
|
+
status: response.status,
|
|
65
|
+
statusText: response.statusText,
|
|
66
|
+
headers: response.headers
|
|
67
|
+
});
|
|
68
|
+
} else {
|
|
69
|
+
if (url.origin !== event.url.origin) {
|
|
70
|
+
const acao = response.headers.get('access-control-allow-origin');
|
|
71
|
+
if (!acao || (acao !== event.url.origin && acao !== '*')) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`CORS error: ${
|
|
74
|
+
acao ? 'Incorrect' : 'No'
|
|
75
|
+
} 'Access-Control-Allow-Origin' header is present on the requested resource`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return response;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** @type {Response} */
|
|
85
|
+
let response;
|
|
86
|
+
|
|
87
|
+
// handle fetch requests for static assets. e.g. prebaked data, etc.
|
|
88
|
+
// we need to support everything the browser's fetch supports
|
|
89
|
+
const prefix = options.paths.assets || options.paths.base;
|
|
90
|
+
const decoded = decodeURIComponent(url.pathname);
|
|
91
|
+
const filename = (
|
|
92
|
+
decoded.startsWith(prefix) ? decoded.slice(prefix.length) : decoded
|
|
93
|
+
).slice(1);
|
|
94
|
+
const filename_html = `${filename}/index.html`; // path may also match path/index.html
|
|
95
|
+
|
|
96
|
+
const is_asset = options.manifest.assets.has(filename);
|
|
97
|
+
const is_asset_html = options.manifest.assets.has(filename_html);
|
|
98
|
+
|
|
99
|
+
if (is_asset || is_asset_html) {
|
|
100
|
+
const file = is_asset ? filename : filename_html;
|
|
101
|
+
|
|
102
|
+
if (options.read) {
|
|
103
|
+
const type = is_asset
|
|
104
|
+
? options.manifest.mimeTypes[filename.slice(filename.lastIndexOf('.'))]
|
|
105
|
+
: 'text/html';
|
|
106
|
+
|
|
107
|
+
return new Response(options.read(file), {
|
|
108
|
+
headers: type ? { 'content-type': type } : {}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return await fetch(request);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (request.credentials !== 'omit') {
|
|
116
|
+
const cookie = get_cookie_header(url, request.headers.get('cookie'));
|
|
117
|
+
if (cookie) {
|
|
118
|
+
request.headers.set('cookie', cookie);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const authorization = event.request.headers.get('authorization');
|
|
122
|
+
if (authorization && !request.headers.has('authorization')) {
|
|
123
|
+
request.headers.set('authorization', authorization);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (request_body && typeof request_body !== 'string' && !ArrayBuffer.isView(request_body)) {
|
|
128
|
+
// TODO is this still necessary? we just bail out below
|
|
129
|
+
// per https://developer.mozilla.org/en-US/docs/Web/API/Request/Request, this can be a
|
|
130
|
+
// Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object.
|
|
131
|
+
// non-string bodies are irksome to deal with, but luckily aren't particularly useful
|
|
132
|
+
// in this context anyway, so we take the easy route and ban them
|
|
133
|
+
throw new Error('Request body must be a string or TypedArray');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
response = await respond(request, options, state);
|
|
137
|
+
|
|
138
|
+
if (state.prerendering) {
|
|
139
|
+
dependency = { response, body: null };
|
|
140
|
+
state.prerendering.dependencies.set(url.pathname, dependency);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const set_cookie = response.headers.get('set-cookie');
|
|
144
|
+
if (set_cookie) {
|
|
145
|
+
for (const str of set_cookie_parser.splitCookiesString(set_cookie)) {
|
|
146
|
+
const { name, value, ...options } = set_cookie_parser.parseString(str);
|
|
147
|
+
|
|
148
|
+
// options.sameSite is string, something more specific is required - type cast is safe
|
|
149
|
+
event.cookies.set(
|
|
150
|
+
name,
|
|
151
|
+
value,
|
|
152
|
+
/** @type {import('cookie').CookieSerializeOptions} */ (options)
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return response;
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @param {RequestInfo | URL} info
|
|
165
|
+
* @param {RequestInit | undefined} init
|
|
166
|
+
* @param {URL} url
|
|
167
|
+
*/
|
|
168
|
+
function normalize_fetch_input(info, init, url) {
|
|
169
|
+
if (info instanceof Request) {
|
|
170
|
+
return info;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return new Request(typeof info === 'string' ? new URL(info, url) : info, init);
|
|
174
|
+
}
|