@sveltejs/kit 1.0.0-next.233 → 1.0.0-next.234
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 +261 -441
- package/dist/chunks/{build.js → http.js} +23 -1
- package/dist/chunks/index.js +266 -437
- 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 +9 -14
- package/types/app.d.ts +3 -17
- package/types/config.d.ts +0 -6
- package/types/endpoint.d.ts +4 -5
- package/types/helper.d.ts +0 -1
- package/types/hooks.d.ts +15 -28
- package/types/index.d.ts +2 -10
- package/types/internal.d.ts +9 -10
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
|
'"': '\\"',
|
|
@@ -743,32 +758,29 @@ async function render_response({
|
|
|
743
758
|
}
|
|
744
759
|
}
|
|
745
760
|
|
|
746
|
-
|
|
747
|
-
const
|
|
748
|
-
'
|
|
749
|
-
|
|
761
|
+
const segments = url.pathname.slice(options.paths.base.length).split('/').slice(2);
|
|
762
|
+
const assets =
|
|
763
|
+
options.paths.assets || (segments.length > 0 ? segments.map(() => '..').join('/') : '.');
|
|
764
|
+
|
|
765
|
+
const html = options.template({ head, body, assets });
|
|
766
|
+
|
|
767
|
+
const headers = new Headers({
|
|
768
|
+
'content-type': 'text/html',
|
|
769
|
+
etag: `"${hash(html)}"`
|
|
770
|
+
});
|
|
750
771
|
|
|
751
772
|
if (maxage) {
|
|
752
|
-
headers
|
|
773
|
+
headers.set('cache-control', `${is_private ? 'private' : 'public'}, max-age=${maxage}`);
|
|
753
774
|
}
|
|
754
775
|
|
|
755
776
|
if (!options.floc) {
|
|
756
|
-
headers
|
|
777
|
+
headers.set('permissions-policy', 'interest-cohort=()');
|
|
757
778
|
}
|
|
758
779
|
|
|
759
|
-
|
|
760
|
-
const assets =
|
|
761
|
-
options.paths.assets || (segments.length > 0 ? segments.map(() => '..').join('/') : '.');
|
|
762
|
-
|
|
763
|
-
return {
|
|
780
|
+
return new Response(html, {
|
|
764
781
|
status,
|
|
765
|
-
headers
|
|
766
|
-
|
|
767
|
-
head,
|
|
768
|
-
body,
|
|
769
|
-
assets
|
|
770
|
-
})
|
|
771
|
-
};
|
|
782
|
+
headers
|
|
783
|
+
});
|
|
772
784
|
}
|
|
773
785
|
|
|
774
786
|
/**
|
|
@@ -906,7 +918,7 @@ function is_root_relative(path) {
|
|
|
906
918
|
|
|
907
919
|
/**
|
|
908
920
|
* @param {{
|
|
909
|
-
*
|
|
921
|
+
* event: import('types/hooks').RequestEvent;
|
|
910
922
|
* options: import('types/internal').SSRRenderOptions;
|
|
911
923
|
* state: import('types/internal').SSRRenderState;
|
|
912
924
|
* route: import('types/internal').SSRPage | null;
|
|
@@ -922,7 +934,7 @@ function is_root_relative(path) {
|
|
|
922
934
|
* @returns {Promise<import('./types').Loaded | undefined>} undefined for fallthrough
|
|
923
935
|
*/
|
|
924
936
|
async function load_node({
|
|
925
|
-
|
|
937
|
+
event,
|
|
926
938
|
options,
|
|
927
939
|
state,
|
|
928
940
|
route,
|
|
@@ -993,7 +1005,7 @@ async function load_node({
|
|
|
993
1005
|
|
|
994
1006
|
opts.headers = new Headers(opts.headers);
|
|
995
1007
|
|
|
996
|
-
const resolved = resolve(
|
|
1008
|
+
const resolved = resolve(event.url.pathname, requested.split('?')[0]);
|
|
997
1009
|
|
|
998
1010
|
let response;
|
|
999
1011
|
|
|
@@ -1029,12 +1041,15 @@ async function load_node({
|
|
|
1029
1041
|
if (opts.credentials !== 'omit') {
|
|
1030
1042
|
uses_credentials = true;
|
|
1031
1043
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1044
|
+
const cookie = event.request.headers.get('cookie');
|
|
1045
|
+
const authorization = event.request.headers.get('authorization');
|
|
1046
|
+
|
|
1047
|
+
if (cookie) {
|
|
1048
|
+
opts.headers.set('cookie', cookie);
|
|
1034
1049
|
}
|
|
1035
1050
|
|
|
1036
|
-
if (
|
|
1037
|
-
opts.headers.set('authorization',
|
|
1051
|
+
if (authorization && !opts.headers.has('authorization')) {
|
|
1052
|
+
opts.headers.set('authorization', authorization);
|
|
1038
1053
|
}
|
|
1039
1054
|
}
|
|
1040
1055
|
|
|
@@ -1047,12 +1062,7 @@ async function load_node({
|
|
|
1047
1062
|
}
|
|
1048
1063
|
|
|
1049
1064
|
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
|
-
},
|
|
1065
|
+
new Request(new URL(requested, event.url).href, opts),
|
|
1056
1066
|
options,
|
|
1057
1067
|
{
|
|
1058
1068
|
fetched: requested,
|
|
@@ -1065,17 +1075,11 @@ async function load_node({
|
|
|
1065
1075
|
state.prerender.dependencies.set(relative, rendered);
|
|
1066
1076
|
}
|
|
1067
1077
|
|
|
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
|
-
});
|
|
1078
|
+
response = rendered;
|
|
1075
1079
|
} else {
|
|
1076
1080
|
// we can't load the endpoint from our own manifest,
|
|
1077
1081
|
// so we need to make an actual HTTP request
|
|
1078
|
-
return fetch(new URL(requested,
|
|
1082
|
+
return fetch(new URL(requested, event.url).href, {
|
|
1079
1083
|
method: opts.method || 'GET',
|
|
1080
1084
|
headers: opts.headers
|
|
1081
1085
|
});
|
|
@@ -1098,11 +1102,13 @@ async function load_node({
|
|
|
1098
1102
|
// ports do not affect the resolution
|
|
1099
1103
|
// leading dot prevents mydomain.com matching domain.com
|
|
1100
1104
|
if (
|
|
1101
|
-
`.${new URL(requested).hostname}`.endsWith(`.${
|
|
1105
|
+
`.${new URL(requested).hostname}`.endsWith(`.${event.url.hostname}`) &&
|
|
1102
1106
|
opts.credentials !== 'omit'
|
|
1103
1107
|
) {
|
|
1104
1108
|
uses_credentials = true;
|
|
1105
|
-
|
|
1109
|
+
|
|
1110
|
+
const cookie = event.request.headers.get('cookie');
|
|
1111
|
+
if (cookie) opts.headers.set('cookie', cookie);
|
|
1106
1112
|
}
|
|
1107
1113
|
|
|
1108
1114
|
const external_request = new Request(requested, /** @type {RequestInit} */ (opts));
|
|
@@ -1211,7 +1217,7 @@ async function load_node({
|
|
|
1211
1217
|
|
|
1212
1218
|
/**
|
|
1213
1219
|
* @param {{
|
|
1214
|
-
*
|
|
1220
|
+
* event: import('types/hooks').RequestEvent;
|
|
1215
1221
|
* options: SSRRenderOptions;
|
|
1216
1222
|
* state: SSRRenderState;
|
|
1217
1223
|
* $session: any;
|
|
@@ -1220,15 +1226,7 @@ async function load_node({
|
|
|
1220
1226
|
* ssr: boolean;
|
|
1221
1227
|
* }} opts
|
|
1222
1228
|
*/
|
|
1223
|
-
async function respond_with_error({
|
|
1224
|
-
request,
|
|
1225
|
-
options,
|
|
1226
|
-
state,
|
|
1227
|
-
$session,
|
|
1228
|
-
status,
|
|
1229
|
-
error,
|
|
1230
|
-
ssr
|
|
1231
|
-
}) {
|
|
1229
|
+
async function respond_with_error({ event, options, state, $session, status, error, ssr }) {
|
|
1232
1230
|
try {
|
|
1233
1231
|
const default_layout = await options.manifest._.nodes[0](); // 0 is always the root layout
|
|
1234
1232
|
const default_error = await options.manifest._.nodes[1](); // 1 is always the root error
|
|
@@ -1238,11 +1236,11 @@ async function respond_with_error({
|
|
|
1238
1236
|
|
|
1239
1237
|
const layout_loaded = /** @type {Loaded} */ (
|
|
1240
1238
|
await load_node({
|
|
1241
|
-
|
|
1239
|
+
event,
|
|
1242
1240
|
options,
|
|
1243
1241
|
state,
|
|
1244
1242
|
route: null,
|
|
1245
|
-
url:
|
|
1243
|
+
url: event.url, // TODO this is redundant, no?
|
|
1246
1244
|
params,
|
|
1247
1245
|
node: default_layout,
|
|
1248
1246
|
$session,
|
|
@@ -1253,11 +1251,11 @@ async function respond_with_error({
|
|
|
1253
1251
|
|
|
1254
1252
|
const error_loaded = /** @type {Loaded} */ (
|
|
1255
1253
|
await load_node({
|
|
1256
|
-
|
|
1254
|
+
event,
|
|
1257
1255
|
options,
|
|
1258
1256
|
state,
|
|
1259
1257
|
route: null,
|
|
1260
|
-
url:
|
|
1258
|
+
url: event.url,
|
|
1261
1259
|
params,
|
|
1262
1260
|
node: default_error,
|
|
1263
1261
|
$session,
|
|
@@ -1280,26 +1278,23 @@ async function respond_with_error({
|
|
|
1280
1278
|
status,
|
|
1281
1279
|
error,
|
|
1282
1280
|
branch: [layout_loaded, error_loaded],
|
|
1283
|
-
url:
|
|
1281
|
+
url: event.url,
|
|
1284
1282
|
params,
|
|
1285
1283
|
ssr
|
|
1286
1284
|
});
|
|
1287
1285
|
} catch (err) {
|
|
1288
1286
|
const error = coalesce_to_error(err);
|
|
1289
1287
|
|
|
1290
|
-
options.handle_error(error,
|
|
1288
|
+
options.handle_error(error, event);
|
|
1291
1289
|
|
|
1292
|
-
return {
|
|
1293
|
-
status: 500
|
|
1294
|
-
|
|
1295
|
-
body: error.stack
|
|
1296
|
-
};
|
|
1290
|
+
return new Response(error.stack, {
|
|
1291
|
+
status: 500
|
|
1292
|
+
});
|
|
1297
1293
|
}
|
|
1298
1294
|
}
|
|
1299
1295
|
|
|
1300
1296
|
/**
|
|
1301
1297
|
* @typedef {import('./types.js').Loaded} Loaded
|
|
1302
|
-
* @typedef {import('types/hooks').ServerResponse} ServerResponse
|
|
1303
1298
|
* @typedef {import('types/internal').SSRNode} SSRNode
|
|
1304
1299
|
* @typedef {import('types/internal').SSRRenderOptions} SSRRenderOptions
|
|
1305
1300
|
* @typedef {import('types/internal').SSRRenderState} SSRRenderState
|
|
@@ -1307,7 +1302,7 @@ async function respond_with_error({
|
|
|
1307
1302
|
|
|
1308
1303
|
/**
|
|
1309
1304
|
* @param {{
|
|
1310
|
-
*
|
|
1305
|
+
* event: import('types/hooks').RequestEvent;
|
|
1311
1306
|
* options: SSRRenderOptions;
|
|
1312
1307
|
* state: SSRRenderState;
|
|
1313
1308
|
* $session: any;
|
|
@@ -1315,10 +1310,10 @@ async function respond_with_error({
|
|
|
1315
1310
|
* params: Record<string, string>;
|
|
1316
1311
|
* ssr: boolean;
|
|
1317
1312
|
* }} opts
|
|
1318
|
-
* @returns {Promise<
|
|
1313
|
+
* @returns {Promise<Response | undefined>}
|
|
1319
1314
|
*/
|
|
1320
1315
|
async function respond$1(opts) {
|
|
1321
|
-
const {
|
|
1316
|
+
const { event, options, state, $session, route, ssr } = opts;
|
|
1322
1317
|
|
|
1323
1318
|
/** @type {Array<SSRNode | undefined>} */
|
|
1324
1319
|
let nodes;
|
|
@@ -1332,7 +1327,7 @@ async function respond$1(opts) {
|
|
|
1332
1327
|
router: true
|
|
1333
1328
|
},
|
|
1334
1329
|
status: 200,
|
|
1335
|
-
url:
|
|
1330
|
+
url: event.url,
|
|
1336
1331
|
stuff: {}
|
|
1337
1332
|
});
|
|
1338
1333
|
}
|
|
@@ -1344,10 +1339,10 @@ async function respond$1(opts) {
|
|
|
1344
1339
|
} catch (err) {
|
|
1345
1340
|
const error = coalesce_to_error(err);
|
|
1346
1341
|
|
|
1347
|
-
options.handle_error(error,
|
|
1342
|
+
options.handle_error(error, event);
|
|
1348
1343
|
|
|
1349
1344
|
return await respond_with_error({
|
|
1350
|
-
|
|
1345
|
+
event,
|
|
1351
1346
|
options,
|
|
1352
1347
|
state,
|
|
1353
1348
|
$session,
|
|
@@ -1365,10 +1360,9 @@ async function respond$1(opts) {
|
|
|
1365
1360
|
if (!leaf.prerender && state.prerender && !state.prerender.all) {
|
|
1366
1361
|
// if the page has `export const prerender = true`, continue,
|
|
1367
1362
|
// otherwise bail out at this point
|
|
1368
|
-
return {
|
|
1369
|
-
status: 204
|
|
1370
|
-
|
|
1371
|
-
};
|
|
1363
|
+
return new Response(undefined, {
|
|
1364
|
+
status: 204
|
|
1365
|
+
});
|
|
1372
1366
|
}
|
|
1373
1367
|
|
|
1374
1368
|
/** @type {Array<Loaded>} */
|
|
@@ -1396,7 +1390,7 @@ async function respond$1(opts) {
|
|
|
1396
1390
|
try {
|
|
1397
1391
|
loaded = await load_node({
|
|
1398
1392
|
...opts,
|
|
1399
|
-
url:
|
|
1393
|
+
url: event.url,
|
|
1400
1394
|
node,
|
|
1401
1395
|
stuff,
|
|
1402
1396
|
is_error: false
|
|
@@ -1408,12 +1402,12 @@ async function respond$1(opts) {
|
|
|
1408
1402
|
|
|
1409
1403
|
if (loaded.loaded.redirect) {
|
|
1410
1404
|
return with_cookies(
|
|
1411
|
-
{
|
|
1405
|
+
new Response(undefined, {
|
|
1412
1406
|
status: loaded.loaded.status,
|
|
1413
1407
|
headers: {
|
|
1414
1408
|
location: encodeURI(loaded.loaded.redirect)
|
|
1415
1409
|
}
|
|
1416
|
-
},
|
|
1410
|
+
}),
|
|
1417
1411
|
set_cookie_headers
|
|
1418
1412
|
);
|
|
1419
1413
|
}
|
|
@@ -1424,7 +1418,7 @@ async function respond$1(opts) {
|
|
|
1424
1418
|
} catch (err) {
|
|
1425
1419
|
const e = coalesce_to_error(err);
|
|
1426
1420
|
|
|
1427
|
-
options.handle_error(e,
|
|
1421
|
+
options.handle_error(e, event);
|
|
1428
1422
|
|
|
1429
1423
|
status = 500;
|
|
1430
1424
|
error = e;
|
|
@@ -1450,7 +1444,7 @@ async function respond$1(opts) {
|
|
|
1450
1444
|
const error_loaded = /** @type {import('./types').Loaded} */ (
|
|
1451
1445
|
await load_node({
|
|
1452
1446
|
...opts,
|
|
1453
|
-
url:
|
|
1447
|
+
url: event.url,
|
|
1454
1448
|
node: error_node,
|
|
1455
1449
|
stuff: node_loaded.stuff,
|
|
1456
1450
|
is_error: true,
|
|
@@ -1470,7 +1464,7 @@ async function respond$1(opts) {
|
|
|
1470
1464
|
} catch (err) {
|
|
1471
1465
|
const e = coalesce_to_error(err);
|
|
1472
1466
|
|
|
1473
|
-
options.handle_error(e,
|
|
1467
|
+
options.handle_error(e, event);
|
|
1474
1468
|
|
|
1475
1469
|
continue;
|
|
1476
1470
|
}
|
|
@@ -1482,7 +1476,7 @@ async function respond$1(opts) {
|
|
|
1482
1476
|
// for now just return regular error page
|
|
1483
1477
|
return with_cookies(
|
|
1484
1478
|
await respond_with_error({
|
|
1485
|
-
|
|
1479
|
+
event,
|
|
1486
1480
|
options,
|
|
1487
1481
|
state,
|
|
1488
1482
|
$session,
|
|
@@ -1509,7 +1503,7 @@ async function respond$1(opts) {
|
|
|
1509
1503
|
await render_response({
|
|
1510
1504
|
...opts,
|
|
1511
1505
|
stuff,
|
|
1512
|
-
url:
|
|
1506
|
+
url: event.url,
|
|
1513
1507
|
page_config,
|
|
1514
1508
|
status,
|
|
1515
1509
|
error,
|
|
@@ -1520,7 +1514,7 @@ async function respond$1(opts) {
|
|
|
1520
1514
|
} catch (err) {
|
|
1521
1515
|
const error = coalesce_to_error(err);
|
|
1522
1516
|
|
|
1523
|
-
options.handle_error(error,
|
|
1517
|
+
options.handle_error(error, event);
|
|
1524
1518
|
|
|
1525
1519
|
return with_cookies(
|
|
1526
1520
|
await respond_with_error({
|
|
@@ -1552,41 +1546,41 @@ function get_page_config(leaf, options) {
|
|
|
1552
1546
|
}
|
|
1553
1547
|
|
|
1554
1548
|
/**
|
|
1555
|
-
* @param {
|
|
1549
|
+
* @param {Response} response
|
|
1556
1550
|
* @param {string[]} set_cookie_headers
|
|
1557
1551
|
*/
|
|
1558
1552
|
function with_cookies(response, set_cookie_headers) {
|
|
1559
1553
|
if (set_cookie_headers.length) {
|
|
1560
|
-
|
|
1554
|
+
set_cookie_headers.forEach((value) => {
|
|
1555
|
+
response.headers.append('set-cookie', value);
|
|
1556
|
+
});
|
|
1561
1557
|
}
|
|
1562
1558
|
return response;
|
|
1563
1559
|
}
|
|
1564
1560
|
|
|
1565
1561
|
/**
|
|
1566
|
-
* @param {import('types/hooks').
|
|
1562
|
+
* @param {import('types/hooks').RequestEvent} event
|
|
1567
1563
|
* @param {import('types/internal').SSRPage} route
|
|
1568
1564
|
* @param {RegExpExecArray} match
|
|
1569
1565
|
* @param {import('types/internal').SSRRenderOptions} options
|
|
1570
1566
|
* @param {import('types/internal').SSRRenderState} state
|
|
1571
1567
|
* @param {boolean} ssr
|
|
1572
|
-
* @returns {Promise<
|
|
1568
|
+
* @returns {Promise<Response | undefined>}
|
|
1573
1569
|
*/
|
|
1574
|
-
async function render_page(
|
|
1570
|
+
async function render_page(event, route, match, options, state, ssr) {
|
|
1575
1571
|
if (state.initiator === route) {
|
|
1576
1572
|
// infinite request cycle detected
|
|
1577
|
-
return {
|
|
1578
|
-
status: 404
|
|
1579
|
-
|
|
1580
|
-
body: `Not found: ${request.url.pathname}`
|
|
1581
|
-
};
|
|
1573
|
+
return new Response(`Not found: ${event.url.pathname}`, {
|
|
1574
|
+
status: 404
|
|
1575
|
+
});
|
|
1582
1576
|
}
|
|
1583
1577
|
|
|
1584
1578
|
const params = route.params ? decode_params(route.params(match)) : {};
|
|
1585
1579
|
|
|
1586
|
-
const $session = await options.hooks.getSession(
|
|
1580
|
+
const $session = await options.hooks.getSession(event);
|
|
1587
1581
|
|
|
1588
1582
|
const response = await respond$1({
|
|
1589
|
-
|
|
1583
|
+
event,
|
|
1590
1584
|
options,
|
|
1591
1585
|
state,
|
|
1592
1586
|
$session,
|
|
@@ -1604,290 +1598,119 @@ async function render_page(request, route, match, options, state, ssr) {
|
|
|
1604
1598
|
// rather than render the error page — which could lead to an
|
|
1605
1599
|
// infinite loop, if the `load` belonged to the root layout,
|
|
1606
1600
|
// 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));
|
|
1601
|
+
return new Response(`Bad request in load function: failed to fetch ${state.fetched}`, {
|
|
1602
|
+
status: 500
|
|
1736
1603
|
});
|
|
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
1604
|
}
|
|
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
1605
|
}
|
|
1801
1606
|
|
|
1802
|
-
/** @type {import('
|
|
1803
|
-
async function respond(
|
|
1804
|
-
|
|
1805
|
-
|
|
1607
|
+
/** @type {import('types/internal').Respond} */
|
|
1608
|
+
async function respond(request, options, state = {}) {
|
|
1609
|
+
const url = new URL(request.url);
|
|
1610
|
+
|
|
1611
|
+
if (url.pathname !== '/' && options.trailing_slash !== 'ignore') {
|
|
1612
|
+
const has_trailing_slash = url.pathname.endsWith('/');
|
|
1806
1613
|
|
|
1807
1614
|
if (
|
|
1808
1615
|
(has_trailing_slash && options.trailing_slash === 'never') ||
|
|
1809
1616
|
(!has_trailing_slash &&
|
|
1810
1617
|
options.trailing_slash === 'always' &&
|
|
1811
|
-
!(
|
|
1618
|
+
!(url.pathname.split('/').pop() || '').includes('.'))
|
|
1812
1619
|
) {
|
|
1813
|
-
|
|
1814
|
-
? incoming.url.pathname.slice(0, -1)
|
|
1815
|
-
: incoming.url.pathname + '/';
|
|
1620
|
+
url.pathname = has_trailing_slash ? url.pathname.slice(0, -1) : url.pathname + '/';
|
|
1816
1621
|
|
|
1817
|
-
if (
|
|
1622
|
+
if (url.search === '?') url.search = '';
|
|
1818
1623
|
|
|
1819
|
-
return {
|
|
1624
|
+
return new Response(undefined, {
|
|
1820
1625
|
status: 301,
|
|
1821
1626
|
headers: {
|
|
1822
|
-
location:
|
|
1627
|
+
location: url.pathname + url.search
|
|
1823
1628
|
}
|
|
1824
|
-
};
|
|
1629
|
+
});
|
|
1825
1630
|
}
|
|
1826
1631
|
}
|
|
1827
1632
|
|
|
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
1633
|
const { parameter, allowed } = options.method_override;
|
|
1838
|
-
const method_override =
|
|
1634
|
+
const method_override = url.searchParams.get(parameter)?.toUpperCase();
|
|
1839
1635
|
|
|
1840
1636
|
if (method_override) {
|
|
1841
|
-
if (request.method
|
|
1637
|
+
if (request.method === 'POST') {
|
|
1842
1638
|
if (allowed.includes(method_override)) {
|
|
1843
|
-
request
|
|
1639
|
+
request = new Proxy(request, {
|
|
1640
|
+
get: (target, property, _receiver) => {
|
|
1641
|
+
if (property === 'method') return method_override;
|
|
1642
|
+
return Reflect.get(target, property, target);
|
|
1643
|
+
}
|
|
1644
|
+
});
|
|
1844
1645
|
} else {
|
|
1845
1646
|
const verb = allowed.length === 0 ? 'enabled' : 'allowed';
|
|
1846
1647
|
const body = `${parameter}=${method_override} is not ${verb}. See https://kit.svelte.dev/docs#configuration-methodoverride`;
|
|
1847
1648
|
|
|
1848
|
-
return {
|
|
1849
|
-
status: 400
|
|
1850
|
-
|
|
1851
|
-
body
|
|
1852
|
-
};
|
|
1649
|
+
return new Response(body, {
|
|
1650
|
+
status: 400
|
|
1651
|
+
});
|
|
1853
1652
|
}
|
|
1854
1653
|
} else {
|
|
1855
1654
|
throw new Error(`${parameter}=${method_override} is only allowed with POST requests`);
|
|
1856
1655
|
}
|
|
1857
1656
|
}
|
|
1858
1657
|
|
|
1658
|
+
/** @type {import('types/hooks').RequestEvent} */
|
|
1659
|
+
const event = {
|
|
1660
|
+
request,
|
|
1661
|
+
url,
|
|
1662
|
+
params: {},
|
|
1663
|
+
locals: {}
|
|
1664
|
+
};
|
|
1665
|
+
|
|
1859
1666
|
// TODO remove this for 1.0
|
|
1860
1667
|
/**
|
|
1861
1668
|
* @param {string} property
|
|
1862
1669
|
* @param {string} replacement
|
|
1670
|
+
* @param {string} suffix
|
|
1863
1671
|
*/
|
|
1864
|
-
const
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1672
|
+
const removed = (property, replacement, suffix = '') => ({
|
|
1673
|
+
get: () => {
|
|
1674
|
+
throw new Error(`event.${property} has been replaced by event.${replacement}` + suffix);
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
|
|
1678
|
+
const details = '. See https://github.com/sveltejs/kit/pull/3384 for details';
|
|
1679
|
+
|
|
1680
|
+
const body_getter = {
|
|
1681
|
+
get: () => {
|
|
1682
|
+
throw new Error(
|
|
1683
|
+
'To access the request body use the text/json/arrayBuffer/formData methods, e.g. `body = await request.json()`' +
|
|
1684
|
+
details
|
|
1685
|
+
);
|
|
1686
|
+
}
|
|
1870
1687
|
};
|
|
1871
1688
|
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1689
|
+
Object.defineProperties(event, {
|
|
1690
|
+
method: removed('method', 'request.method', details),
|
|
1691
|
+
headers: removed('headers', 'request.headers', details),
|
|
1692
|
+
origin: removed('origin', 'url.origin'),
|
|
1693
|
+
path: removed('path', 'url.pathname'),
|
|
1694
|
+
query: removed('query', 'url.searchParams'),
|
|
1695
|
+
body: body_getter,
|
|
1696
|
+
rawBody: body_getter
|
|
1697
|
+
});
|
|
1875
1698
|
|
|
1876
1699
|
let ssr = true;
|
|
1877
1700
|
|
|
1878
1701
|
try {
|
|
1879
1702
|
return await options.hooks.handle({
|
|
1880
|
-
|
|
1881
|
-
resolve: async (
|
|
1703
|
+
event,
|
|
1704
|
+
resolve: async (event, opts) => {
|
|
1882
1705
|
if (opts && 'ssr' in opts) ssr = /** @type {boolean} */ (opts.ssr);
|
|
1883
1706
|
|
|
1884
1707
|
if (state.prerender && state.prerender.fallback) {
|
|
1885
1708
|
return await render_response({
|
|
1886
|
-
url:
|
|
1887
|
-
params:
|
|
1709
|
+
url: event.url,
|
|
1710
|
+
params: event.params,
|
|
1888
1711
|
options,
|
|
1889
1712
|
state,
|
|
1890
|
-
$session: await options.hooks.getSession(
|
|
1713
|
+
$session: await options.hooks.getSession(event),
|
|
1891
1714
|
page_config: { router: true, hydrate: true },
|
|
1892
1715
|
stuff: {},
|
|
1893
1716
|
status: 200,
|
|
@@ -1896,7 +1719,7 @@ async function respond(incoming, options, state = {}) {
|
|
|
1896
1719
|
});
|
|
1897
1720
|
}
|
|
1898
1721
|
|
|
1899
|
-
let decoded = decodeURI(
|
|
1722
|
+
let decoded = decodeURI(event.url.pathname);
|
|
1900
1723
|
|
|
1901
1724
|
if (options.paths.base) {
|
|
1902
1725
|
if (!decoded.startsWith(options.paths.base)) return;
|
|
@@ -1909,47 +1732,40 @@ async function respond(incoming, options, state = {}) {
|
|
|
1909
1732
|
|
|
1910
1733
|
const response =
|
|
1911
1734
|
route.type === 'endpoint'
|
|
1912
|
-
? await render_endpoint(
|
|
1913
|
-
: await render_page(
|
|
1735
|
+
? await render_endpoint(event, route, match)
|
|
1736
|
+
: await render_page(event, route, match, options, state, ssr);
|
|
1914
1737
|
|
|
1915
1738
|
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
|
-
}
|
|
1739
|
+
// respond with 304 if etag matches
|
|
1740
|
+
if (response.status === 200 && response.headers.has('etag')) {
|
|
1741
|
+
let if_none_match_value = request.headers.get('if-none-match');
|
|
1926
1742
|
|
|
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
|
-
}
|
|
1743
|
+
// ignore W/ prefix https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match#directives
|
|
1744
|
+
if (if_none_match_value?.startsWith('W/"')) {
|
|
1745
|
+
if_none_match_value = if_none_match_value.substring(2);
|
|
1746
|
+
}
|
|
1945
1747
|
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1748
|
+
const etag = /** @type {string} */ (response.headers.get('etag'));
|
|
1749
|
+
|
|
1750
|
+
if (if_none_match_value === etag) {
|
|
1751
|
+
const headers = new Headers({ etag });
|
|
1752
|
+
|
|
1753
|
+
// https://datatracker.ietf.org/doc/html/rfc7232#section-4.1
|
|
1754
|
+
for (const key of [
|
|
1755
|
+
'cache-control',
|
|
1756
|
+
'content-location',
|
|
1757
|
+
'date',
|
|
1758
|
+
'expires',
|
|
1759
|
+
'vary'
|
|
1760
|
+
]) {
|
|
1761
|
+
const value = response.headers.get(key);
|
|
1762
|
+
if (value) headers.set(key, value);
|
|
1950
1763
|
}
|
|
1951
1764
|
|
|
1952
|
-
|
|
1765
|
+
return new Response(undefined, {
|
|
1766
|
+
status: 304,
|
|
1767
|
+
headers
|
|
1768
|
+
});
|
|
1953
1769
|
}
|
|
1954
1770
|
}
|
|
1955
1771
|
|
|
@@ -1960,28 +1776,34 @@ async function respond(incoming, options, state = {}) {
|
|
|
1960
1776
|
// if this request came direct from the user, rather than
|
|
1961
1777
|
// via a `fetch` in a `load`, render a 404 page
|
|
1962
1778
|
if (!state.initiator) {
|
|
1963
|
-
const $session = await options.hooks.getSession(
|
|
1779
|
+
const $session = await options.hooks.getSession(event);
|
|
1964
1780
|
return await respond_with_error({
|
|
1965
|
-
|
|
1781
|
+
event,
|
|
1966
1782
|
options,
|
|
1967
1783
|
state,
|
|
1968
1784
|
$session,
|
|
1969
1785
|
status: 404,
|
|
1970
|
-
error: new Error(`Not found: ${
|
|
1786
|
+
error: new Error(`Not found: ${event.url.pathname}`),
|
|
1971
1787
|
ssr
|
|
1972
1788
|
});
|
|
1973
1789
|
}
|
|
1790
|
+
},
|
|
1791
|
+
|
|
1792
|
+
// TODO remove for 1.0
|
|
1793
|
+
// @ts-expect-error
|
|
1794
|
+
get request() {
|
|
1795
|
+
throw new Error('request in handle has been replaced with event' + details);
|
|
1974
1796
|
}
|
|
1975
1797
|
});
|
|
1976
1798
|
} catch (/** @type {unknown} */ e) {
|
|
1977
1799
|
const error = coalesce_to_error(e);
|
|
1978
1800
|
|
|
1979
|
-
options.handle_error(error,
|
|
1801
|
+
options.handle_error(error, event);
|
|
1980
1802
|
|
|
1981
1803
|
try {
|
|
1982
|
-
const $session = await options.hooks.getSession(
|
|
1804
|
+
const $session = await options.hooks.getSession(event);
|
|
1983
1805
|
return await respond_with_error({
|
|
1984
|
-
|
|
1806
|
+
event,
|
|
1985
1807
|
options,
|
|
1986
1808
|
state,
|
|
1987
1809
|
$session,
|
|
@@ -1992,11 +1814,9 @@ async function respond(incoming, options, state = {}) {
|
|
|
1992
1814
|
} catch (/** @type {unknown} */ e) {
|
|
1993
1815
|
const error = coalesce_to_error(e);
|
|
1994
1816
|
|
|
1995
|
-
return {
|
|
1996
|
-
status: 500
|
|
1997
|
-
|
|
1998
|
-
body: options.dev ? error.stack : error.message
|
|
1999
|
-
};
|
|
1817
|
+
return new Response(options.dev ? error.stack : error.message, {
|
|
1818
|
+
status: 500
|
|
1819
|
+
});
|
|
2000
1820
|
}
|
|
2001
1821
|
}
|
|
2002
1822
|
}
|