@sveltejs/kit 1.0.0-next.233 → 1.0.0-next.237
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/assets/server/index.js +273 -448
- package/dist/chunks/{build.js → http.js} +23 -1
- package/dist/chunks/index.js +278 -444
- package/dist/chunks/index3.js +15 -20
- package/dist/chunks/index5.js +24 -39
- package/dist/chunks/index6.js +16 -13
- package/dist/chunks/url.js +1 -23
- package/dist/cli.js +28 -7
- package/dist/hooks.js +8 -8
- package/dist/node.js +27 -1
- package/package.json +1 -1
- package/types/ambient-modules.d.ts +11 -14
- package/types/app.d.ts +3 -17
- package/types/config.d.ts +2 -7
- package/types/endpoint.d.ts +11 -13
- package/types/helper.d.ts +4 -25
- package/types/hooks.d.ts +15 -28
- package/types/index.d.ts +2 -10
- package/types/internal.d.ts +11 -14
- package/types/page.d.ts +29 -17
package/assets/server/index.js
CHANGED
|
@@ -1,37 +1,44 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
/** @param {Partial<import('types/helper').ResponseHeaders> | undefined} object */
|
|
2
|
+
function to_headers(object) {
|
|
3
|
+
const headers = new Headers();
|
|
4
|
+
|
|
5
|
+
if (object) {
|
|
6
|
+
for (const key in object) {
|
|
7
|
+
const value = object[key];
|
|
8
|
+
if (!value) continue;
|
|
9
|
+
|
|
10
|
+
if (typeof value === 'string') {
|
|
11
|
+
headers.set(key, value);
|
|
12
|
+
} else {
|
|
13
|
+
value.forEach((value) => {
|
|
14
|
+
headers.append(key, value);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
17
|
}
|
|
18
|
-
return value[0];
|
|
19
18
|
}
|
|
20
|
-
|
|
19
|
+
|
|
20
|
+
return headers;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Hash using djb2
|
|
25
|
+
* @param {import('types/hooks').StrictBody} value
|
|
26
|
+
*/
|
|
27
|
+
function hash(value) {
|
|
28
|
+
let hash = 5381;
|
|
29
|
+
let i = value.length;
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
if (typeof value === 'string') {
|
|
32
|
+
while (i) hash = (hash * 33) ^ value.charCodeAt(--i);
|
|
33
|
+
} else {
|
|
34
|
+
while (i) hash = (hash * 33) ^ value[--i];
|
|
30
35
|
}
|
|
31
36
|
|
|
32
|
-
return
|
|
37
|
+
return (hash >>> 0).toString(36);
|
|
33
38
|
}
|
|
34
39
|
|
|
40
|
+
/** @param {Record<string, any>} obj */
|
|
41
|
+
|
|
35
42
|
/** @param {Record<string, string>} params */
|
|
36
43
|
function decode_params(params) {
|
|
37
44
|
for (const key in params) {
|
|
@@ -56,11 +63,9 @@ function decode_params(params) {
|
|
|
56
63
|
|
|
57
64
|
/** @param {string} body */
|
|
58
65
|
function error(body) {
|
|
59
|
-
return {
|
|
60
|
-
status: 500
|
|
61
|
-
|
|
62
|
-
headers: {}
|
|
63
|
-
};
|
|
66
|
+
return new Response(body, {
|
|
67
|
+
status: 500
|
|
68
|
+
});
|
|
64
69
|
}
|
|
65
70
|
|
|
66
71
|
/** @param {unknown} s */
|
|
@@ -89,16 +94,16 @@ function is_text(content_type) {
|
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
/**
|
|
92
|
-
* @param {import('types/hooks').
|
|
97
|
+
* @param {import('types/hooks').RequestEvent} event
|
|
93
98
|
* @param {import('types/internal').SSREndpoint} route
|
|
94
99
|
* @param {RegExpExecArray} match
|
|
95
|
-
* @returns {Promise<
|
|
100
|
+
* @returns {Promise<Response | undefined>}
|
|
96
101
|
*/
|
|
97
|
-
async function render_endpoint(
|
|
102
|
+
async function render_endpoint(event, route, match) {
|
|
98
103
|
const mod = await route.load();
|
|
99
104
|
|
|
100
105
|
/** @type {import('types/endpoint').RequestHandler} */
|
|
101
|
-
const handler = mod[request.method.toLowerCase().replace('delete', 'del')]; // 'delete' is a reserved word
|
|
106
|
+
const handler = mod[event.request.method.toLowerCase().replace('delete', 'del')]; // 'delete' is a reserved word
|
|
102
107
|
|
|
103
108
|
if (!handler) {
|
|
104
109
|
return;
|
|
@@ -107,10 +112,10 @@ async function render_endpoint(request, route, match) {
|
|
|
107
112
|
// we're mutating `request` so that we don't have to do { ...request, params }
|
|
108
113
|
// on the next line, since that breaks the getters that replace path, query and
|
|
109
114
|
// origin. We could revert that once we remove the getters
|
|
110
|
-
|
|
115
|
+
event.params = route.params ? decode_params(route.params(match)) : {};
|
|
111
116
|
|
|
112
|
-
const response = await handler(
|
|
113
|
-
const preface = `Invalid response from route ${
|
|
117
|
+
const response = await handler(event);
|
|
118
|
+
const preface = `Invalid response from route ${event.url.pathname}`;
|
|
114
119
|
|
|
115
120
|
if (typeof response !== 'object') {
|
|
116
121
|
return error(`${preface}: expected an object, got ${typeof response}`);
|
|
@@ -120,10 +125,11 @@ async function render_endpoint(request, route, match) {
|
|
|
120
125
|
return;
|
|
121
126
|
}
|
|
122
127
|
|
|
123
|
-
|
|
128
|
+
const { status = 200, body = {} } = response;
|
|
129
|
+
const headers =
|
|
130
|
+
response.headers instanceof Headers ? response.headers : to_headers(response.headers);
|
|
124
131
|
|
|
125
|
-
|
|
126
|
-
const type = get_single_valued_header(headers, 'content-type');
|
|
132
|
+
const type = headers.get('content-type');
|
|
127
133
|
|
|
128
134
|
if (!is_text(type) && !(body instanceof Uint8Array || is_string(body))) {
|
|
129
135
|
return error(
|
|
@@ -134,19 +140,45 @@ async function render_endpoint(request, route, match) {
|
|
|
134
140
|
/** @type {import('types/hooks').StrictBody} */
|
|
135
141
|
let normalized_body;
|
|
136
142
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
!(body instanceof Uint8Array) &&
|
|
141
|
-
(!type || type.startsWith('application/json'))
|
|
142
|
-
) {
|
|
143
|
-
headers = { ...headers, 'content-type': 'application/json; charset=utf-8' };
|
|
144
|
-
normalized_body = JSON.stringify(typeof body === 'undefined' ? {} : body);
|
|
143
|
+
if (is_pojo(body) && (!type || type.startsWith('application/json'))) {
|
|
144
|
+
headers.set('content-type', 'application/json; charset=utf-8');
|
|
145
|
+
normalized_body = JSON.stringify(body);
|
|
145
146
|
} else {
|
|
146
147
|
normalized_body = /** @type {import('types/hooks').StrictBody} */ (body);
|
|
147
148
|
}
|
|
148
149
|
|
|
149
|
-
|
|
150
|
+
if (
|
|
151
|
+
(typeof normalized_body === 'string' || normalized_body instanceof Uint8Array) &&
|
|
152
|
+
!headers.has('etag')
|
|
153
|
+
) {
|
|
154
|
+
const cache_control = headers.get('cache-control');
|
|
155
|
+
if (!cache_control || !/(no-store|immutable)/.test(cache_control)) {
|
|
156
|
+
headers.set('etag', `"${hash(normalized_body)}"`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return new Response(normalized_body, {
|
|
161
|
+
status,
|
|
162
|
+
headers
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** @param {any} body */
|
|
167
|
+
function is_pojo(body) {
|
|
168
|
+
if (typeof body !== 'object') return false;
|
|
169
|
+
|
|
170
|
+
if (body) {
|
|
171
|
+
if (body instanceof Uint8Array) return false;
|
|
172
|
+
|
|
173
|
+
// body could be a node Readable, but we don't want to import
|
|
174
|
+
// node built-ins, so we use duck typing
|
|
175
|
+
if (body._readableState && body._writableState && body._events) return false;
|
|
176
|
+
|
|
177
|
+
// similarly, it could be a web ReadableStream
|
|
178
|
+
if (body[Symbol.toStringTag] === 'ReadableStream') return false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return true;
|
|
150
182
|
}
|
|
151
183
|
|
|
152
184
|
var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$';
|
|
@@ -439,23 +471,6 @@ function coalesce_to_error(err) {
|
|
|
439
471
|
: new Error(JSON.stringify(err));
|
|
440
472
|
}
|
|
441
473
|
|
|
442
|
-
/**
|
|
443
|
-
* Hash using djb2
|
|
444
|
-
* @param {import('types/hooks').StrictBody} value
|
|
445
|
-
*/
|
|
446
|
-
function hash(value) {
|
|
447
|
-
let hash = 5381;
|
|
448
|
-
let i = value.length;
|
|
449
|
-
|
|
450
|
-
if (typeof value === 'string') {
|
|
451
|
-
while (i) hash = (hash * 33) ^ value.charCodeAt(--i);
|
|
452
|
-
} else {
|
|
453
|
-
while (i) hash = (hash * 33) ^ value[--i];
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
return (hash >>> 0).toString(36);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
474
|
/** @type {Record<string, string>} */
|
|
460
475
|
const escape_json_string_in_html_dict = {
|
|
461
476
|
'"': '\\"',
|
|
@@ -723,12 +738,7 @@ async function render_response({
|
|
|
723
738
|
params: ${devalue(params)}
|
|
724
739
|
}` : 'null'}
|
|
725
740
|
});
|
|
726
|
-
</script
|
|
727
|
-
<script>
|
|
728
|
-
if ('serviceWorker' in navigator) {
|
|
729
|
-
navigator.serviceWorker.register('${options.service_worker}');
|
|
730
|
-
}
|
|
731
|
-
</script>` : ''}`;
|
|
741
|
+
</script>`;
|
|
732
742
|
|
|
733
743
|
body += serialized_data
|
|
734
744
|
.map(({ url, body, json }) => {
|
|
@@ -741,34 +751,41 @@ async function render_response({
|
|
|
741
751
|
})
|
|
742
752
|
.join('\n\n\t');
|
|
743
753
|
}
|
|
754
|
+
|
|
755
|
+
if (options.service_worker) {
|
|
756
|
+
// always include service worker unless it's turned off explicitly
|
|
757
|
+
head += `
|
|
758
|
+
<script>
|
|
759
|
+
if ('serviceWorker' in navigator) {
|
|
760
|
+
navigator.serviceWorker.register('${options.service_worker}');
|
|
761
|
+
}
|
|
762
|
+
</script>`;
|
|
763
|
+
}
|
|
744
764
|
}
|
|
745
765
|
|
|
746
|
-
|
|
747
|
-
const
|
|
748
|
-
'
|
|
749
|
-
|
|
766
|
+
const segments = url.pathname.slice(options.paths.base.length).split('/').slice(2);
|
|
767
|
+
const assets =
|
|
768
|
+
options.paths.assets || (segments.length > 0 ? segments.map(() => '..').join('/') : '.');
|
|
769
|
+
|
|
770
|
+
const html = options.template({ head, body, assets });
|
|
771
|
+
|
|
772
|
+
const headers = new Headers({
|
|
773
|
+
'content-type': 'text/html',
|
|
774
|
+
etag: `"${hash(html)}"`
|
|
775
|
+
});
|
|
750
776
|
|
|
751
777
|
if (maxage) {
|
|
752
|
-
headers
|
|
778
|
+
headers.set('cache-control', `${is_private ? 'private' : 'public'}, max-age=${maxage}`);
|
|
753
779
|
}
|
|
754
780
|
|
|
755
781
|
if (!options.floc) {
|
|
756
|
-
headers
|
|
782
|
+
headers.set('permissions-policy', 'interest-cohort=()');
|
|
757
783
|
}
|
|
758
784
|
|
|
759
|
-
|
|
760
|
-
const assets =
|
|
761
|
-
options.paths.assets || (segments.length > 0 ? segments.map(() => '..').join('/') : '.');
|
|
762
|
-
|
|
763
|
-
return {
|
|
785
|
+
return new Response(html, {
|
|
764
786
|
status,
|
|
765
|
-
headers
|
|
766
|
-
|
|
767
|
-
head,
|
|
768
|
-
body,
|
|
769
|
-
assets
|
|
770
|
-
})
|
|
771
|
-
};
|
|
787
|
+
headers
|
|
788
|
+
});
|
|
772
789
|
}
|
|
773
790
|
|
|
774
791
|
/**
|
|
@@ -906,7 +923,7 @@ function is_root_relative(path) {
|
|
|
906
923
|
|
|
907
924
|
/**
|
|
908
925
|
* @param {{
|
|
909
|
-
*
|
|
926
|
+
* event: import('types/hooks').RequestEvent;
|
|
910
927
|
* options: import('types/internal').SSRRenderOptions;
|
|
911
928
|
* state: import('types/internal').SSRRenderState;
|
|
912
929
|
* route: import('types/internal').SSRPage | null;
|
|
@@ -922,7 +939,7 @@ function is_root_relative(path) {
|
|
|
922
939
|
* @returns {Promise<import('./types').Loaded | undefined>} undefined for fallthrough
|
|
923
940
|
*/
|
|
924
941
|
async function load_node({
|
|
925
|
-
|
|
942
|
+
event,
|
|
926
943
|
options,
|
|
927
944
|
state,
|
|
928
945
|
route,
|
|
@@ -993,7 +1010,7 @@ async function load_node({
|
|
|
993
1010
|
|
|
994
1011
|
opts.headers = new Headers(opts.headers);
|
|
995
1012
|
|
|
996
|
-
const resolved = resolve(
|
|
1013
|
+
const resolved = resolve(event.url.pathname, requested.split('?')[0]);
|
|
997
1014
|
|
|
998
1015
|
let response;
|
|
999
1016
|
|
|
@@ -1029,12 +1046,15 @@ async function load_node({
|
|
|
1029
1046
|
if (opts.credentials !== 'omit') {
|
|
1030
1047
|
uses_credentials = true;
|
|
1031
1048
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1049
|
+
const cookie = event.request.headers.get('cookie');
|
|
1050
|
+
const authorization = event.request.headers.get('authorization');
|
|
1051
|
+
|
|
1052
|
+
if (cookie) {
|
|
1053
|
+
opts.headers.set('cookie', cookie);
|
|
1034
1054
|
}
|
|
1035
1055
|
|
|
1036
|
-
if (
|
|
1037
|
-
opts.headers.set('authorization',
|
|
1056
|
+
if (authorization && !opts.headers.has('authorization')) {
|
|
1057
|
+
opts.headers.set('authorization', authorization);
|
|
1038
1058
|
}
|
|
1039
1059
|
}
|
|
1040
1060
|
|
|
@@ -1047,12 +1067,7 @@ async function load_node({
|
|
|
1047
1067
|
}
|
|
1048
1068
|
|
|
1049
1069
|
const rendered = await respond(
|
|
1050
|
-
|
|
1051
|
-
url: new URL(requested, request.url),
|
|
1052
|
-
method: opts.method || 'GET',
|
|
1053
|
-
headers: Object.fromEntries(opts.headers),
|
|
1054
|
-
rawBody: opts.body == null ? null : new TextEncoder().encode(opts.body)
|
|
1055
|
-
},
|
|
1070
|
+
new Request(new URL(requested, event.url).href, opts),
|
|
1056
1071
|
options,
|
|
1057
1072
|
{
|
|
1058
1073
|
fetched: requested,
|
|
@@ -1065,17 +1080,11 @@ async function load_node({
|
|
|
1065
1080
|
state.prerender.dependencies.set(relative, rendered);
|
|
1066
1081
|
}
|
|
1067
1082
|
|
|
1068
|
-
|
|
1069
|
-
// can be an array so we know we have only simple values
|
|
1070
|
-
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
|
|
1071
|
-
response = new Response(rendered.body, {
|
|
1072
|
-
status: rendered.status,
|
|
1073
|
-
headers: /** @type {Record<string, string>} */ (rendered.headers)
|
|
1074
|
-
});
|
|
1083
|
+
response = rendered;
|
|
1075
1084
|
} else {
|
|
1076
1085
|
// we can't load the endpoint from our own manifest,
|
|
1077
1086
|
// so we need to make an actual HTTP request
|
|
1078
|
-
return fetch(new URL(requested,
|
|
1087
|
+
return fetch(new URL(requested, event.url).href, {
|
|
1079
1088
|
method: opts.method || 'GET',
|
|
1080
1089
|
headers: opts.headers
|
|
1081
1090
|
});
|
|
@@ -1098,11 +1107,13 @@ async function load_node({
|
|
|
1098
1107
|
// ports do not affect the resolution
|
|
1099
1108
|
// leading dot prevents mydomain.com matching domain.com
|
|
1100
1109
|
if (
|
|
1101
|
-
`.${new URL(requested).hostname}`.endsWith(`.${
|
|
1110
|
+
`.${new URL(requested).hostname}`.endsWith(`.${event.url.hostname}`) &&
|
|
1102
1111
|
opts.credentials !== 'omit'
|
|
1103
1112
|
) {
|
|
1104
1113
|
uses_credentials = true;
|
|
1105
|
-
|
|
1114
|
+
|
|
1115
|
+
const cookie = event.request.headers.get('cookie');
|
|
1116
|
+
if (cookie) opts.headers.set('cookie', cookie);
|
|
1106
1117
|
}
|
|
1107
1118
|
|
|
1108
1119
|
const external_request = new Request(requested, /** @type {RequestInit} */ (opts));
|
|
@@ -1211,7 +1222,7 @@ async function load_node({
|
|
|
1211
1222
|
|
|
1212
1223
|
/**
|
|
1213
1224
|
* @param {{
|
|
1214
|
-
*
|
|
1225
|
+
* event: import('types/hooks').RequestEvent;
|
|
1215
1226
|
* options: SSRRenderOptions;
|
|
1216
1227
|
* state: SSRRenderState;
|
|
1217
1228
|
* $session: any;
|
|
@@ -1220,15 +1231,7 @@ async function load_node({
|
|
|
1220
1231
|
* ssr: boolean;
|
|
1221
1232
|
* }} opts
|
|
1222
1233
|
*/
|
|
1223
|
-
async function respond_with_error({
|
|
1224
|
-
request,
|
|
1225
|
-
options,
|
|
1226
|
-
state,
|
|
1227
|
-
$session,
|
|
1228
|
-
status,
|
|
1229
|
-
error,
|
|
1230
|
-
ssr
|
|
1231
|
-
}) {
|
|
1234
|
+
async function respond_with_error({ event, options, state, $session, status, error, ssr }) {
|
|
1232
1235
|
try {
|
|
1233
1236
|
const default_layout = await options.manifest._.nodes[0](); // 0 is always the root layout
|
|
1234
1237
|
const default_error = await options.manifest._.nodes[1](); // 1 is always the root error
|
|
@@ -1238,11 +1241,11 @@ async function respond_with_error({
|
|
|
1238
1241
|
|
|
1239
1242
|
const layout_loaded = /** @type {Loaded} */ (
|
|
1240
1243
|
await load_node({
|
|
1241
|
-
|
|
1244
|
+
event,
|
|
1242
1245
|
options,
|
|
1243
1246
|
state,
|
|
1244
1247
|
route: null,
|
|
1245
|
-
url:
|
|
1248
|
+
url: event.url, // TODO this is redundant, no?
|
|
1246
1249
|
params,
|
|
1247
1250
|
node: default_layout,
|
|
1248
1251
|
$session,
|
|
@@ -1253,11 +1256,11 @@ async function respond_with_error({
|
|
|
1253
1256
|
|
|
1254
1257
|
const error_loaded = /** @type {Loaded} */ (
|
|
1255
1258
|
await load_node({
|
|
1256
|
-
|
|
1259
|
+
event,
|
|
1257
1260
|
options,
|
|
1258
1261
|
state,
|
|
1259
1262
|
route: null,
|
|
1260
|
-
url:
|
|
1263
|
+
url: event.url,
|
|
1261
1264
|
params,
|
|
1262
1265
|
node: default_error,
|
|
1263
1266
|
$session,
|
|
@@ -1280,26 +1283,23 @@ async function respond_with_error({
|
|
|
1280
1283
|
status,
|
|
1281
1284
|
error,
|
|
1282
1285
|
branch: [layout_loaded, error_loaded],
|
|
1283
|
-
url:
|
|
1286
|
+
url: event.url,
|
|
1284
1287
|
params,
|
|
1285
1288
|
ssr
|
|
1286
1289
|
});
|
|
1287
1290
|
} catch (err) {
|
|
1288
1291
|
const error = coalesce_to_error(err);
|
|
1289
1292
|
|
|
1290
|
-
options.handle_error(error,
|
|
1293
|
+
options.handle_error(error, event);
|
|
1291
1294
|
|
|
1292
|
-
return {
|
|
1293
|
-
status: 500
|
|
1294
|
-
|
|
1295
|
-
body: error.stack
|
|
1296
|
-
};
|
|
1295
|
+
return new Response(error.stack, {
|
|
1296
|
+
status: 500
|
|
1297
|
+
});
|
|
1297
1298
|
}
|
|
1298
1299
|
}
|
|
1299
1300
|
|
|
1300
1301
|
/**
|
|
1301
1302
|
* @typedef {import('./types.js').Loaded} Loaded
|
|
1302
|
-
* @typedef {import('types/hooks').ServerResponse} ServerResponse
|
|
1303
1303
|
* @typedef {import('types/internal').SSRNode} SSRNode
|
|
1304
1304
|
* @typedef {import('types/internal').SSRRenderOptions} SSRRenderOptions
|
|
1305
1305
|
* @typedef {import('types/internal').SSRRenderState} SSRRenderState
|
|
@@ -1307,7 +1307,7 @@ async function respond_with_error({
|
|
|
1307
1307
|
|
|
1308
1308
|
/**
|
|
1309
1309
|
* @param {{
|
|
1310
|
-
*
|
|
1310
|
+
* event: import('types/hooks').RequestEvent;
|
|
1311
1311
|
* options: SSRRenderOptions;
|
|
1312
1312
|
* state: SSRRenderState;
|
|
1313
1313
|
* $session: any;
|
|
@@ -1315,10 +1315,10 @@ async function respond_with_error({
|
|
|
1315
1315
|
* params: Record<string, string>;
|
|
1316
1316
|
* ssr: boolean;
|
|
1317
1317
|
* }} opts
|
|
1318
|
-
* @returns {Promise<
|
|
1318
|
+
* @returns {Promise<Response | undefined>}
|
|
1319
1319
|
*/
|
|
1320
1320
|
async function respond$1(opts) {
|
|
1321
|
-
const {
|
|
1321
|
+
const { event, options, state, $session, route, ssr } = opts;
|
|
1322
1322
|
|
|
1323
1323
|
/** @type {Array<SSRNode | undefined>} */
|
|
1324
1324
|
let nodes;
|
|
@@ -1332,7 +1332,7 @@ async function respond$1(opts) {
|
|
|
1332
1332
|
router: true
|
|
1333
1333
|
},
|
|
1334
1334
|
status: 200,
|
|
1335
|
-
url:
|
|
1335
|
+
url: event.url,
|
|
1336
1336
|
stuff: {}
|
|
1337
1337
|
});
|
|
1338
1338
|
}
|
|
@@ -1344,10 +1344,10 @@ async function respond$1(opts) {
|
|
|
1344
1344
|
} catch (err) {
|
|
1345
1345
|
const error = coalesce_to_error(err);
|
|
1346
1346
|
|
|
1347
|
-
options.handle_error(error,
|
|
1347
|
+
options.handle_error(error, event);
|
|
1348
1348
|
|
|
1349
1349
|
return await respond_with_error({
|
|
1350
|
-
|
|
1350
|
+
event,
|
|
1351
1351
|
options,
|
|
1352
1352
|
state,
|
|
1353
1353
|
$session,
|
|
@@ -1365,10 +1365,9 @@ async function respond$1(opts) {
|
|
|
1365
1365
|
if (!leaf.prerender && state.prerender && !state.prerender.all) {
|
|
1366
1366
|
// if the page has `export const prerender = true`, continue,
|
|
1367
1367
|
// otherwise bail out at this point
|
|
1368
|
-
return {
|
|
1369
|
-
status: 204
|
|
1370
|
-
|
|
1371
|
-
};
|
|
1368
|
+
return new Response(undefined, {
|
|
1369
|
+
status: 204
|
|
1370
|
+
});
|
|
1372
1371
|
}
|
|
1373
1372
|
|
|
1374
1373
|
/** @type {Array<Loaded>} */
|
|
@@ -1396,7 +1395,7 @@ async function respond$1(opts) {
|
|
|
1396
1395
|
try {
|
|
1397
1396
|
loaded = await load_node({
|
|
1398
1397
|
...opts,
|
|
1399
|
-
url:
|
|
1398
|
+
url: event.url,
|
|
1400
1399
|
node,
|
|
1401
1400
|
stuff,
|
|
1402
1401
|
is_error: false
|
|
@@ -1408,12 +1407,12 @@ async function respond$1(opts) {
|
|
|
1408
1407
|
|
|
1409
1408
|
if (loaded.loaded.redirect) {
|
|
1410
1409
|
return with_cookies(
|
|
1411
|
-
{
|
|
1410
|
+
new Response(undefined, {
|
|
1412
1411
|
status: loaded.loaded.status,
|
|
1413
1412
|
headers: {
|
|
1414
|
-
location:
|
|
1413
|
+
location: loaded.loaded.redirect
|
|
1415
1414
|
}
|
|
1416
|
-
},
|
|
1415
|
+
}),
|
|
1417
1416
|
set_cookie_headers
|
|
1418
1417
|
);
|
|
1419
1418
|
}
|
|
@@ -1424,7 +1423,7 @@ async function respond$1(opts) {
|
|
|
1424
1423
|
} catch (err) {
|
|
1425
1424
|
const e = coalesce_to_error(err);
|
|
1426
1425
|
|
|
1427
|
-
options.handle_error(e,
|
|
1426
|
+
options.handle_error(e, event);
|
|
1428
1427
|
|
|
1429
1428
|
status = 500;
|
|
1430
1429
|
error = e;
|
|
@@ -1450,7 +1449,7 @@ async function respond$1(opts) {
|
|
|
1450
1449
|
const error_loaded = /** @type {import('./types').Loaded} */ (
|
|
1451
1450
|
await load_node({
|
|
1452
1451
|
...opts,
|
|
1453
|
-
url:
|
|
1452
|
+
url: event.url,
|
|
1454
1453
|
node: error_node,
|
|
1455
1454
|
stuff: node_loaded.stuff,
|
|
1456
1455
|
is_error: true,
|
|
@@ -1470,7 +1469,7 @@ async function respond$1(opts) {
|
|
|
1470
1469
|
} catch (err) {
|
|
1471
1470
|
const e = coalesce_to_error(err);
|
|
1472
1471
|
|
|
1473
|
-
options.handle_error(e,
|
|
1472
|
+
options.handle_error(e, event);
|
|
1474
1473
|
|
|
1475
1474
|
continue;
|
|
1476
1475
|
}
|
|
@@ -1482,7 +1481,7 @@ async function respond$1(opts) {
|
|
|
1482
1481
|
// for now just return regular error page
|
|
1483
1482
|
return with_cookies(
|
|
1484
1483
|
await respond_with_error({
|
|
1485
|
-
|
|
1484
|
+
event,
|
|
1486
1485
|
options,
|
|
1487
1486
|
state,
|
|
1488
1487
|
$session,
|
|
@@ -1509,7 +1508,7 @@ async function respond$1(opts) {
|
|
|
1509
1508
|
await render_response({
|
|
1510
1509
|
...opts,
|
|
1511
1510
|
stuff,
|
|
1512
|
-
url:
|
|
1511
|
+
url: event.url,
|
|
1513
1512
|
page_config,
|
|
1514
1513
|
status,
|
|
1515
1514
|
error,
|
|
@@ -1520,7 +1519,7 @@ async function respond$1(opts) {
|
|
|
1520
1519
|
} catch (err) {
|
|
1521
1520
|
const error = coalesce_to_error(err);
|
|
1522
1521
|
|
|
1523
|
-
options.handle_error(error,
|
|
1522
|
+
options.handle_error(error, event);
|
|
1524
1523
|
|
|
1525
1524
|
return with_cookies(
|
|
1526
1525
|
await respond_with_error({
|
|
@@ -1552,41 +1551,41 @@ function get_page_config(leaf, options) {
|
|
|
1552
1551
|
}
|
|
1553
1552
|
|
|
1554
1553
|
/**
|
|
1555
|
-
* @param {
|
|
1554
|
+
* @param {Response} response
|
|
1556
1555
|
* @param {string[]} set_cookie_headers
|
|
1557
1556
|
*/
|
|
1558
1557
|
function with_cookies(response, set_cookie_headers) {
|
|
1559
1558
|
if (set_cookie_headers.length) {
|
|
1560
|
-
|
|
1559
|
+
set_cookie_headers.forEach((value) => {
|
|
1560
|
+
response.headers.append('set-cookie', value);
|
|
1561
|
+
});
|
|
1561
1562
|
}
|
|
1562
1563
|
return response;
|
|
1563
1564
|
}
|
|
1564
1565
|
|
|
1565
1566
|
/**
|
|
1566
|
-
* @param {import('types/hooks').
|
|
1567
|
+
* @param {import('types/hooks').RequestEvent} event
|
|
1567
1568
|
* @param {import('types/internal').SSRPage} route
|
|
1568
1569
|
* @param {RegExpExecArray} match
|
|
1569
1570
|
* @param {import('types/internal').SSRRenderOptions} options
|
|
1570
1571
|
* @param {import('types/internal').SSRRenderState} state
|
|
1571
1572
|
* @param {boolean} ssr
|
|
1572
|
-
* @returns {Promise<
|
|
1573
|
+
* @returns {Promise<Response | undefined>}
|
|
1573
1574
|
*/
|
|
1574
|
-
async function render_page(
|
|
1575
|
+
async function render_page(event, route, match, options, state, ssr) {
|
|
1575
1576
|
if (state.initiator === route) {
|
|
1576
1577
|
// infinite request cycle detected
|
|
1577
|
-
return {
|
|
1578
|
-
status: 404
|
|
1579
|
-
|
|
1580
|
-
body: `Not found: ${request.url.pathname}`
|
|
1581
|
-
};
|
|
1578
|
+
return new Response(`Not found: ${event.url.pathname}`, {
|
|
1579
|
+
status: 404
|
|
1580
|
+
});
|
|
1582
1581
|
}
|
|
1583
1582
|
|
|
1584
1583
|
const params = route.params ? decode_params(route.params(match)) : {};
|
|
1585
1584
|
|
|
1586
|
-
const $session = await options.hooks.getSession(
|
|
1585
|
+
const $session = await options.hooks.getSession(event);
|
|
1587
1586
|
|
|
1588
1587
|
const response = await respond$1({
|
|
1589
|
-
|
|
1588
|
+
event,
|
|
1590
1589
|
options,
|
|
1591
1590
|
state,
|
|
1592
1591
|
$session,
|
|
@@ -1604,290 +1603,119 @@ async function render_page(request, route, match, options, state, ssr) {
|
|
|
1604
1603
|
// rather than render the error page — which could lead to an
|
|
1605
1604
|
// infinite loop, if the `load` belonged to the root layout,
|
|
1606
1605
|
// we respond with a bare-bones 500
|
|
1607
|
-
return {
|
|
1608
|
-
status: 500
|
|
1609
|
-
headers: {},
|
|
1610
|
-
body: `Bad request in load function: failed to fetch ${state.fetched}`
|
|
1611
|
-
};
|
|
1612
|
-
}
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
function read_only_form_data() {
|
|
1616
|
-
/** @type {Map<string, string[]>} */
|
|
1617
|
-
const map = new Map();
|
|
1618
|
-
|
|
1619
|
-
return {
|
|
1620
|
-
/**
|
|
1621
|
-
* @param {string} key
|
|
1622
|
-
* @param {string} value
|
|
1623
|
-
*/
|
|
1624
|
-
append(key, value) {
|
|
1625
|
-
const existing_values = map.get(key);
|
|
1626
|
-
if (existing_values) {
|
|
1627
|
-
existing_values.push(value);
|
|
1628
|
-
} else {
|
|
1629
|
-
map.set(key, [value]);
|
|
1630
|
-
}
|
|
1631
|
-
},
|
|
1632
|
-
|
|
1633
|
-
data: new ReadOnlyFormData(map)
|
|
1634
|
-
};
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
|
-
class ReadOnlyFormData {
|
|
1638
|
-
/** @type {Map<string, string[]>} */
|
|
1639
|
-
#map;
|
|
1640
|
-
|
|
1641
|
-
/** @param {Map<string, string[]>} map */
|
|
1642
|
-
constructor(map) {
|
|
1643
|
-
this.#map = map;
|
|
1644
|
-
}
|
|
1645
|
-
|
|
1646
|
-
/** @param {string} key */
|
|
1647
|
-
get(key) {
|
|
1648
|
-
const value = this.#map.get(key);
|
|
1649
|
-
if (!value) {
|
|
1650
|
-
return null;
|
|
1651
|
-
}
|
|
1652
|
-
return value[0];
|
|
1653
|
-
}
|
|
1654
|
-
|
|
1655
|
-
/** @param {string} key */
|
|
1656
|
-
getAll(key) {
|
|
1657
|
-
return this.#map.get(key) || [];
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
/** @param {string} key */
|
|
1661
|
-
has(key) {
|
|
1662
|
-
return this.#map.has(key);
|
|
1663
|
-
}
|
|
1664
|
-
|
|
1665
|
-
*[Symbol.iterator]() {
|
|
1666
|
-
for (const [key, value] of this.#map) {
|
|
1667
|
-
for (let i = 0; i < value.length; i += 1) {
|
|
1668
|
-
yield [key, value[i]];
|
|
1669
|
-
}
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
1672
|
-
|
|
1673
|
-
*entries() {
|
|
1674
|
-
for (const [key, value] of this.#map) {
|
|
1675
|
-
for (let i = 0; i < value.length; i += 1) {
|
|
1676
|
-
yield [key, value[i]];
|
|
1677
|
-
}
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
|
|
1681
|
-
*keys() {
|
|
1682
|
-
for (const [key] of this.#map) yield key;
|
|
1683
|
-
}
|
|
1684
|
-
|
|
1685
|
-
*values() {
|
|
1686
|
-
for (const [, value] of this.#map) {
|
|
1687
|
-
for (let i = 0; i < value.length; i += 1) {
|
|
1688
|
-
yield value[i];
|
|
1689
|
-
}
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
}
|
|
1693
|
-
|
|
1694
|
-
/**
|
|
1695
|
-
* @param {import('types/app').RawBody} raw
|
|
1696
|
-
* @param {import('types/helper').RequestHeaders} headers
|
|
1697
|
-
*/
|
|
1698
|
-
function parse_body(raw, headers) {
|
|
1699
|
-
if (!raw) return raw;
|
|
1700
|
-
|
|
1701
|
-
const content_type = headers['content-type'];
|
|
1702
|
-
const [type, ...directives] = content_type ? content_type.split(/;\s*/) : [];
|
|
1703
|
-
|
|
1704
|
-
const text = () => new TextDecoder(headers['content-encoding'] || 'utf-8').decode(raw);
|
|
1705
|
-
|
|
1706
|
-
switch (type) {
|
|
1707
|
-
case 'text/plain':
|
|
1708
|
-
return text();
|
|
1709
|
-
|
|
1710
|
-
case 'application/json':
|
|
1711
|
-
return JSON.parse(text());
|
|
1712
|
-
|
|
1713
|
-
case 'application/x-www-form-urlencoded':
|
|
1714
|
-
return get_urlencoded(text());
|
|
1715
|
-
|
|
1716
|
-
case 'multipart/form-data': {
|
|
1717
|
-
const boundary = directives.find((directive) => directive.startsWith('boundary='));
|
|
1718
|
-
if (!boundary) throw new Error('Missing boundary');
|
|
1719
|
-
return get_multipart(text(), boundary.slice('boundary='.length));
|
|
1720
|
-
}
|
|
1721
|
-
default:
|
|
1722
|
-
return raw;
|
|
1723
|
-
}
|
|
1724
|
-
}
|
|
1725
|
-
|
|
1726
|
-
/** @param {string} text */
|
|
1727
|
-
function get_urlencoded(text) {
|
|
1728
|
-
const { data, append } = read_only_form_data();
|
|
1729
|
-
|
|
1730
|
-
text
|
|
1731
|
-
.replace(/\+/g, ' ')
|
|
1732
|
-
.split('&')
|
|
1733
|
-
.forEach((str) => {
|
|
1734
|
-
const [key, value] = str.split('=');
|
|
1735
|
-
append(decodeURIComponent(key), decodeURIComponent(value));
|
|
1606
|
+
return new Response(`Bad request in load function: failed to fetch ${state.fetched}`, {
|
|
1607
|
+
status: 500
|
|
1736
1608
|
});
|
|
1737
|
-
|
|
1738
|
-
return data;
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1741
|
-
/**
|
|
1742
|
-
* @param {string} text
|
|
1743
|
-
* @param {string} boundary
|
|
1744
|
-
*/
|
|
1745
|
-
function get_multipart(text, boundary) {
|
|
1746
|
-
const parts = text.split(`--${boundary}`);
|
|
1747
|
-
|
|
1748
|
-
if (parts[0] !== '' || parts[parts.length - 1].trim() !== '--') {
|
|
1749
|
-
throw new Error('Malformed form data');
|
|
1750
1609
|
}
|
|
1751
|
-
|
|
1752
|
-
const { data, append } = read_only_form_data();
|
|
1753
|
-
|
|
1754
|
-
parts.slice(1, -1).forEach((part) => {
|
|
1755
|
-
const match = /\s*([\s\S]+?)\r\n\r\n([\s\S]*)\s*/.exec(part);
|
|
1756
|
-
if (!match) {
|
|
1757
|
-
throw new Error('Malformed form data');
|
|
1758
|
-
}
|
|
1759
|
-
const raw_headers = match[1];
|
|
1760
|
-
const body = match[2].trim();
|
|
1761
|
-
|
|
1762
|
-
let key;
|
|
1763
|
-
|
|
1764
|
-
/** @type {Record<string, string>} */
|
|
1765
|
-
const headers = {};
|
|
1766
|
-
raw_headers.split('\r\n').forEach((str) => {
|
|
1767
|
-
const [raw_header, ...raw_directives] = str.split('; ');
|
|
1768
|
-
let [name, value] = raw_header.split(': ');
|
|
1769
|
-
|
|
1770
|
-
name = name.toLowerCase();
|
|
1771
|
-
headers[name] = value;
|
|
1772
|
-
|
|
1773
|
-
/** @type {Record<string, string>} */
|
|
1774
|
-
const directives = {};
|
|
1775
|
-
raw_directives.forEach((raw_directive) => {
|
|
1776
|
-
const [name, value] = raw_directive.split('=');
|
|
1777
|
-
directives[name] = JSON.parse(value); // TODO is this right?
|
|
1778
|
-
});
|
|
1779
|
-
|
|
1780
|
-
if (name === 'content-disposition') {
|
|
1781
|
-
if (value !== 'form-data') throw new Error('Malformed form data');
|
|
1782
|
-
|
|
1783
|
-
if (directives.filename) {
|
|
1784
|
-
// TODO we probably don't want to do this automatically
|
|
1785
|
-
throw new Error('File upload is not yet implemented');
|
|
1786
|
-
}
|
|
1787
|
-
|
|
1788
|
-
if (directives.name) {
|
|
1789
|
-
key = directives.name;
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
});
|
|
1793
|
-
|
|
1794
|
-
if (!key) throw new Error('Malformed form data');
|
|
1795
|
-
|
|
1796
|
-
append(key, body);
|
|
1797
|
-
});
|
|
1798
|
-
|
|
1799
|
-
return data;
|
|
1800
1610
|
}
|
|
1801
1611
|
|
|
1802
|
-
/** @type {import('
|
|
1803
|
-
async function respond(
|
|
1804
|
-
|
|
1805
|
-
|
|
1612
|
+
/** @type {import('types/internal').Respond} */
|
|
1613
|
+
async function respond(request, options, state = {}) {
|
|
1614
|
+
const url = new URL(request.url);
|
|
1615
|
+
|
|
1616
|
+
if (url.pathname !== '/' && options.trailing_slash !== 'ignore') {
|
|
1617
|
+
const has_trailing_slash = url.pathname.endsWith('/');
|
|
1806
1618
|
|
|
1807
1619
|
if (
|
|
1808
1620
|
(has_trailing_slash && options.trailing_slash === 'never') ||
|
|
1809
1621
|
(!has_trailing_slash &&
|
|
1810
1622
|
options.trailing_slash === 'always' &&
|
|
1811
|
-
!(
|
|
1623
|
+
!(url.pathname.split('/').pop() || '').includes('.'))
|
|
1812
1624
|
) {
|
|
1813
|
-
|
|
1814
|
-
? incoming.url.pathname.slice(0, -1)
|
|
1815
|
-
: incoming.url.pathname + '/';
|
|
1625
|
+
url.pathname = has_trailing_slash ? url.pathname.slice(0, -1) : url.pathname + '/';
|
|
1816
1626
|
|
|
1817
|
-
if (
|
|
1627
|
+
if (url.search === '?') url.search = '';
|
|
1818
1628
|
|
|
1819
|
-
return {
|
|
1629
|
+
return new Response(undefined, {
|
|
1820
1630
|
status: 301,
|
|
1821
1631
|
headers: {
|
|
1822
|
-
location:
|
|
1632
|
+
location: url.pathname + url.search
|
|
1823
1633
|
}
|
|
1824
|
-
};
|
|
1634
|
+
});
|
|
1825
1635
|
}
|
|
1826
1636
|
}
|
|
1827
1637
|
|
|
1828
|
-
const headers = lowercase_keys(incoming.headers);
|
|
1829
|
-
const request = {
|
|
1830
|
-
...incoming,
|
|
1831
|
-
headers,
|
|
1832
|
-
body: parse_body(incoming.rawBody, headers),
|
|
1833
|
-
params: {},
|
|
1834
|
-
locals: {}
|
|
1835
|
-
};
|
|
1836
|
-
|
|
1837
1638
|
const { parameter, allowed } = options.method_override;
|
|
1838
|
-
const method_override =
|
|
1639
|
+
const method_override = url.searchParams.get(parameter)?.toUpperCase();
|
|
1839
1640
|
|
|
1840
1641
|
if (method_override) {
|
|
1841
|
-
if (request.method
|
|
1642
|
+
if (request.method === 'POST') {
|
|
1842
1643
|
if (allowed.includes(method_override)) {
|
|
1843
|
-
request
|
|
1644
|
+
request = new Proxy(request, {
|
|
1645
|
+
get: (target, property, _receiver) => {
|
|
1646
|
+
if (property === 'method') return method_override;
|
|
1647
|
+
return Reflect.get(target, property, target);
|
|
1648
|
+
}
|
|
1649
|
+
});
|
|
1844
1650
|
} else {
|
|
1845
1651
|
const verb = allowed.length === 0 ? 'enabled' : 'allowed';
|
|
1846
1652
|
const body = `${parameter}=${method_override} is not ${verb}. See https://kit.svelte.dev/docs#configuration-methodoverride`;
|
|
1847
1653
|
|
|
1848
|
-
return {
|
|
1849
|
-
status: 400
|
|
1850
|
-
|
|
1851
|
-
body
|
|
1852
|
-
};
|
|
1654
|
+
return new Response(body, {
|
|
1655
|
+
status: 400
|
|
1656
|
+
});
|
|
1853
1657
|
}
|
|
1854
1658
|
} else {
|
|
1855
1659
|
throw new Error(`${parameter}=${method_override} is only allowed with POST requests`);
|
|
1856
1660
|
}
|
|
1857
1661
|
}
|
|
1858
1662
|
|
|
1663
|
+
/** @type {import('types/hooks').RequestEvent} */
|
|
1664
|
+
const event = {
|
|
1665
|
+
request,
|
|
1666
|
+
url,
|
|
1667
|
+
params: {},
|
|
1668
|
+
locals: {}
|
|
1669
|
+
};
|
|
1670
|
+
|
|
1859
1671
|
// TODO remove this for 1.0
|
|
1860
1672
|
/**
|
|
1861
1673
|
* @param {string} property
|
|
1862
1674
|
* @param {string} replacement
|
|
1675
|
+
* @param {string} suffix
|
|
1863
1676
|
*/
|
|
1864
|
-
const
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1677
|
+
const removed = (property, replacement, suffix = '') => ({
|
|
1678
|
+
get: () => {
|
|
1679
|
+
throw new Error(`event.${property} has been replaced by event.${replacement}` + suffix);
|
|
1680
|
+
}
|
|
1681
|
+
});
|
|
1682
|
+
|
|
1683
|
+
const details = '. See https://github.com/sveltejs/kit/pull/3384 for details';
|
|
1684
|
+
|
|
1685
|
+
const body_getter = {
|
|
1686
|
+
get: () => {
|
|
1687
|
+
throw new Error(
|
|
1688
|
+
'To access the request body use the text/json/arrayBuffer/formData methods, e.g. `body = await request.json()`' +
|
|
1689
|
+
details
|
|
1690
|
+
);
|
|
1691
|
+
}
|
|
1870
1692
|
};
|
|
1871
1693
|
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1694
|
+
Object.defineProperties(event, {
|
|
1695
|
+
method: removed('method', 'request.method', details),
|
|
1696
|
+
headers: removed('headers', 'request.headers', details),
|
|
1697
|
+
origin: removed('origin', 'url.origin'),
|
|
1698
|
+
path: removed('path', 'url.pathname'),
|
|
1699
|
+
query: removed('query', 'url.searchParams'),
|
|
1700
|
+
body: body_getter,
|
|
1701
|
+
rawBody: body_getter
|
|
1702
|
+
});
|
|
1875
1703
|
|
|
1876
1704
|
let ssr = true;
|
|
1877
1705
|
|
|
1878
1706
|
try {
|
|
1879
1707
|
return await options.hooks.handle({
|
|
1880
|
-
|
|
1881
|
-
resolve: async (
|
|
1708
|
+
event,
|
|
1709
|
+
resolve: async (event, opts) => {
|
|
1882
1710
|
if (opts && 'ssr' in opts) ssr = /** @type {boolean} */ (opts.ssr);
|
|
1883
1711
|
|
|
1884
1712
|
if (state.prerender && state.prerender.fallback) {
|
|
1885
1713
|
return await render_response({
|
|
1886
|
-
url:
|
|
1887
|
-
params:
|
|
1714
|
+
url: event.url,
|
|
1715
|
+
params: event.params,
|
|
1888
1716
|
options,
|
|
1889
1717
|
state,
|
|
1890
|
-
$session: await options.hooks.getSession(
|
|
1718
|
+
$session: await options.hooks.getSession(event),
|
|
1891
1719
|
page_config: { router: true, hydrate: true },
|
|
1892
1720
|
stuff: {},
|
|
1893
1721
|
status: 200,
|
|
@@ -1896,7 +1724,7 @@ async function respond(incoming, options, state = {}) {
|
|
|
1896
1724
|
});
|
|
1897
1725
|
}
|
|
1898
1726
|
|
|
1899
|
-
let decoded = decodeURI(
|
|
1727
|
+
let decoded = decodeURI(event.url.pathname);
|
|
1900
1728
|
|
|
1901
1729
|
if (options.paths.base) {
|
|
1902
1730
|
if (!decoded.startsWith(options.paths.base)) return;
|
|
@@ -1909,47 +1737,40 @@ async function respond(incoming, options, state = {}) {
|
|
|
1909
1737
|
|
|
1910
1738
|
const response =
|
|
1911
1739
|
route.type === 'endpoint'
|
|
1912
|
-
? await render_endpoint(
|
|
1913
|
-
: await render_page(
|
|
1740
|
+
? await render_endpoint(event, route, match)
|
|
1741
|
+
: await render_page(event, route, match, options, state, ssr);
|
|
1914
1742
|
|
|
1915
1743
|
if (response) {
|
|
1916
|
-
//
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
const cache_control = get_single_valued_header(response.headers, 'cache-control');
|
|
1920
|
-
if (!cache_control || !/(no-store|immutable)/.test(cache_control)) {
|
|
1921
|
-
let if_none_match_value = request.headers['if-none-match'];
|
|
1922
|
-
// ignore W/ prefix https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match#directives
|
|
1923
|
-
if (if_none_match_value?.startsWith('W/"')) {
|
|
1924
|
-
if_none_match_value = if_none_match_value.substring(2);
|
|
1925
|
-
}
|
|
1744
|
+
// respond with 304 if etag matches
|
|
1745
|
+
if (response.status === 200 && response.headers.has('etag')) {
|
|
1746
|
+
let if_none_match_value = request.headers.get('if-none-match');
|
|
1926
1747
|
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
const headers = { etag };
|
|
1932
|
-
|
|
1933
|
-
// https://datatracker.ietf.org/doc/html/rfc7232#section-4.1
|
|
1934
|
-
for (const key of [
|
|
1935
|
-
'cache-control',
|
|
1936
|
-
'content-location',
|
|
1937
|
-
'date',
|
|
1938
|
-
'expires',
|
|
1939
|
-
'vary'
|
|
1940
|
-
]) {
|
|
1941
|
-
if (key in response.headers) {
|
|
1942
|
-
headers[key] = /** @type {string} */ (response.headers[key]);
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1748
|
+
// ignore W/ prefix https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match#directives
|
|
1749
|
+
if (if_none_match_value?.startsWith('W/"')) {
|
|
1750
|
+
if_none_match_value = if_none_match_value.substring(2);
|
|
1751
|
+
}
|
|
1945
1752
|
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1753
|
+
const etag = /** @type {string} */ (response.headers.get('etag'));
|
|
1754
|
+
|
|
1755
|
+
if (if_none_match_value === etag) {
|
|
1756
|
+
const headers = new Headers({ etag });
|
|
1757
|
+
|
|
1758
|
+
// https://datatracker.ietf.org/doc/html/rfc7232#section-4.1
|
|
1759
|
+
for (const key of [
|
|
1760
|
+
'cache-control',
|
|
1761
|
+
'content-location',
|
|
1762
|
+
'date',
|
|
1763
|
+
'expires',
|
|
1764
|
+
'vary'
|
|
1765
|
+
]) {
|
|
1766
|
+
const value = response.headers.get(key);
|
|
1767
|
+
if (value) headers.set(key, value);
|
|
1950
1768
|
}
|
|
1951
1769
|
|
|
1952
|
-
|
|
1770
|
+
return new Response(undefined, {
|
|
1771
|
+
status: 304,
|
|
1772
|
+
headers
|
|
1773
|
+
});
|
|
1953
1774
|
}
|
|
1954
1775
|
}
|
|
1955
1776
|
|
|
@@ -1960,28 +1781,34 @@ async function respond(incoming, options, state = {}) {
|
|
|
1960
1781
|
// if this request came direct from the user, rather than
|
|
1961
1782
|
// via a `fetch` in a `load`, render a 404 page
|
|
1962
1783
|
if (!state.initiator) {
|
|
1963
|
-
const $session = await options.hooks.getSession(
|
|
1784
|
+
const $session = await options.hooks.getSession(event);
|
|
1964
1785
|
return await respond_with_error({
|
|
1965
|
-
|
|
1786
|
+
event,
|
|
1966
1787
|
options,
|
|
1967
1788
|
state,
|
|
1968
1789
|
$session,
|
|
1969
1790
|
status: 404,
|
|
1970
|
-
error: new Error(`Not found: ${
|
|
1791
|
+
error: new Error(`Not found: ${event.url.pathname}`),
|
|
1971
1792
|
ssr
|
|
1972
1793
|
});
|
|
1973
1794
|
}
|
|
1795
|
+
},
|
|
1796
|
+
|
|
1797
|
+
// TODO remove for 1.0
|
|
1798
|
+
// @ts-expect-error
|
|
1799
|
+
get request() {
|
|
1800
|
+
throw new Error('request in handle has been replaced with event' + details);
|
|
1974
1801
|
}
|
|
1975
1802
|
});
|
|
1976
1803
|
} catch (/** @type {unknown} */ e) {
|
|
1977
1804
|
const error = coalesce_to_error(e);
|
|
1978
1805
|
|
|
1979
|
-
options.handle_error(error,
|
|
1806
|
+
options.handle_error(error, event);
|
|
1980
1807
|
|
|
1981
1808
|
try {
|
|
1982
|
-
const $session = await options.hooks.getSession(
|
|
1809
|
+
const $session = await options.hooks.getSession(event);
|
|
1983
1810
|
return await respond_with_error({
|
|
1984
|
-
|
|
1811
|
+
event,
|
|
1985
1812
|
options,
|
|
1986
1813
|
state,
|
|
1987
1814
|
$session,
|
|
@@ -1992,11 +1819,9 @@ async function respond(incoming, options, state = {}) {
|
|
|
1992
1819
|
} catch (/** @type {unknown} */ e) {
|
|
1993
1820
|
const error = coalesce_to_error(e);
|
|
1994
1821
|
|
|
1995
|
-
return {
|
|
1996
|
-
status: 500
|
|
1997
|
-
|
|
1998
|
-
body: options.dev ? error.stack : error.message
|
|
1999
|
-
};
|
|
1822
|
+
return new Response(options.dev ? error.stack : error.message, {
|
|
1823
|
+
status: 500
|
|
1824
|
+
});
|
|
2000
1825
|
}
|
|
2001
1826
|
}
|
|
2002
1827
|
}
|