@sveltejs/kit 1.0.0-next.471 → 1.0.0-next.472
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/package.json +1 -1
- package/src/core/config/options.js +4 -14
- package/src/core/sync/write_root.js +4 -4
- package/src/core/sync/write_types/index.js +104 -26
- package/src/exports/index.js +10 -1
- package/src/exports/node/polyfills.js +3 -2
- package/src/exports/vite/build/build_server.js +0 -1
- package/src/exports/vite/dev/index.js +0 -1
- package/src/runtime/app/forms.js +65 -0
- package/src/runtime/client/client.js +122 -58
- package/src/runtime/client/start.js +1 -9
- package/src/runtime/client/types.d.ts +9 -13
- package/src/runtime/control.js +67 -0
- package/src/runtime/server/cookie.js +76 -0
- package/src/runtime/server/endpoint.js +4 -1
- package/src/runtime/server/index.js +21 -40
- package/src/runtime/server/page/actions.js +225 -0
- package/src/runtime/server/page/fetch.js +1 -1
- package/src/runtime/server/page/index.js +35 -132
- package/src/runtime/server/page/render.js +14 -18
- package/src/runtime/server/page/respond_with_error.js +1 -2
- package/src/runtime/server/utils.js +11 -0
- package/types/ambient.d.ts +48 -0
- package/types/index.d.ts +58 -19
- package/types/internal.d.ts +3 -12
- package/types/private.d.ts +0 -3
- package/src/runtime/server/page/cookie.js +0 -25
|
@@ -145,7 +145,7 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
145
145
|
const url = new URL(location.href);
|
|
146
146
|
|
|
147
147
|
invalidating = Promise.resolve().then(async () => {
|
|
148
|
-
const intent = get_navigation_intent(url);
|
|
148
|
+
const intent = get_navigation_intent(url, true);
|
|
149
149
|
await update(intent, url, []);
|
|
150
150
|
|
|
151
151
|
invalidating = null;
|
|
@@ -187,7 +187,7 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
187
187
|
|
|
188
188
|
/** @param {URL} url */
|
|
189
189
|
async function prefetch(url) {
|
|
190
|
-
const intent = get_navigation_intent(url);
|
|
190
|
+
const intent = get_navigation_intent(url, false);
|
|
191
191
|
|
|
192
192
|
if (!intent) {
|
|
193
193
|
throw new Error('Attempted to prefetch a URL that does not belong to this app');
|
|
@@ -278,24 +278,9 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
278
278
|
navigation_result.props.page.url = url;
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
console.warn = (...args) => {
|
|
285
|
-
if (
|
|
286
|
-
args.length !== 1 ||
|
|
287
|
-
!/<(Layout|Page)(_[\w$]+)?> was created with unknown prop '(data|errors)'/.test(args[0])
|
|
288
|
-
) {
|
|
289
|
-
warn(...args);
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
|
-
root.$set(navigation_result.props);
|
|
293
|
-
tick().then(() => (console.warn = warn));
|
|
294
|
-
|
|
295
|
-
check_for_removed_attributes();
|
|
296
|
-
} else {
|
|
297
|
-
root.$set(navigation_result.props);
|
|
298
|
-
}
|
|
281
|
+
const post_update = pre_update();
|
|
282
|
+
root.$set(navigation_result.props);
|
|
283
|
+
post_update();
|
|
299
284
|
} else {
|
|
300
285
|
initialize(navigation_result);
|
|
301
286
|
}
|
|
@@ -371,32 +356,13 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
371
356
|
|
|
372
357
|
page = result.props.page;
|
|
373
358
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
) {
|
|
382
|
-
warn(...args);
|
|
383
|
-
}
|
|
384
|
-
};
|
|
385
|
-
root = new Root({
|
|
386
|
-
target,
|
|
387
|
-
props: { ...result.props, stores },
|
|
388
|
-
hydrate: true
|
|
389
|
-
});
|
|
390
|
-
console.warn = warn;
|
|
391
|
-
|
|
392
|
-
check_for_removed_attributes();
|
|
393
|
-
} else {
|
|
394
|
-
root = new Root({
|
|
395
|
-
target,
|
|
396
|
-
props: { ...result.props, stores },
|
|
397
|
-
hydrate: true
|
|
398
|
-
});
|
|
399
|
-
}
|
|
359
|
+
const post_update = pre_update();
|
|
360
|
+
root = new Root({
|
|
361
|
+
target,
|
|
362
|
+
props: { ...result.props, stores },
|
|
363
|
+
hydrate: true
|
|
364
|
+
});
|
|
365
|
+
post_update();
|
|
400
366
|
|
|
401
367
|
/** @type {import('types').Navigation} */
|
|
402
368
|
const navigation = {
|
|
@@ -422,7 +388,7 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
422
388
|
* status: number;
|
|
423
389
|
* error: HttpError | Error | null;
|
|
424
390
|
* route: import('types').CSRRoute | null;
|
|
425
|
-
*
|
|
391
|
+
* form?: Record<string, any> | null;
|
|
426
392
|
* }} opts
|
|
427
393
|
*/
|
|
428
394
|
async function get_navigation_result_from_branch({
|
|
@@ -432,7 +398,7 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
432
398
|
status,
|
|
433
399
|
error,
|
|
434
400
|
route,
|
|
435
|
-
|
|
401
|
+
form
|
|
436
402
|
}) {
|
|
437
403
|
const filtered = /** @type {import('./types').BranchNode[] } */ (branch.filter(Boolean));
|
|
438
404
|
|
|
@@ -448,11 +414,14 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
448
414
|
session_id
|
|
449
415
|
},
|
|
450
416
|
props: {
|
|
451
|
-
components: filtered.map((branch_node) => branch_node.node.component)
|
|
452
|
-
errors: validation_errors
|
|
417
|
+
components: filtered.map((branch_node) => branch_node.node.component)
|
|
453
418
|
}
|
|
454
419
|
};
|
|
455
420
|
|
|
421
|
+
if (form !== undefined) {
|
|
422
|
+
result.props.form = form;
|
|
423
|
+
}
|
|
424
|
+
|
|
456
425
|
let data = {};
|
|
457
426
|
let data_changed = !page;
|
|
458
427
|
for (let i = 0; i < filtered.length; i += 1) {
|
|
@@ -713,7 +682,7 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
713
682
|
* @param {import('./types').NavigationIntent} intent
|
|
714
683
|
* @returns {Promise<import('./types').NavigationResult | undefined>}
|
|
715
684
|
*/
|
|
716
|
-
async function load_route({ id, url, params, route }) {
|
|
685
|
+
async function load_route({ id, invalidating, url, params, route }) {
|
|
717
686
|
if (load_cache.id === id && load_cache.promise) {
|
|
718
687
|
return load_cache.promise;
|
|
719
688
|
}
|
|
@@ -881,7 +850,9 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
881
850
|
branch,
|
|
882
851
|
status: 200,
|
|
883
852
|
error: null,
|
|
884
|
-
route
|
|
853
|
+
route,
|
|
854
|
+
// Reset `form` on navigation, but not invalidation
|
|
855
|
+
form: invalidating ? undefined : null
|
|
885
856
|
});
|
|
886
857
|
}
|
|
887
858
|
|
|
@@ -954,8 +925,11 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
954
925
|
});
|
|
955
926
|
}
|
|
956
927
|
|
|
957
|
-
/**
|
|
958
|
-
|
|
928
|
+
/**
|
|
929
|
+
* @param {URL} url
|
|
930
|
+
* @param {boolean} invalidating
|
|
931
|
+
*/
|
|
932
|
+
function get_navigation_intent(url, invalidating) {
|
|
959
933
|
if (is_external_url(url)) return;
|
|
960
934
|
|
|
961
935
|
const path = decodeURI(url.pathname.slice(base.length) || '/');
|
|
@@ -969,7 +943,7 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
969
943
|
);
|
|
970
944
|
const id = normalized.pathname + normalized.search;
|
|
971
945
|
/** @type {import('./types').NavigationIntent} */
|
|
972
|
-
const intent = { id, route, params: decode_params(params), url: normalized };
|
|
946
|
+
const intent = { id, invalidating, route, params: decode_params(params), url: normalized };
|
|
973
947
|
return intent;
|
|
974
948
|
}
|
|
975
949
|
}
|
|
@@ -1009,7 +983,7 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
1009
983
|
}) {
|
|
1010
984
|
let should_block = false;
|
|
1011
985
|
|
|
1012
|
-
const intent = get_navigation_intent(url);
|
|
986
|
+
const intent = get_navigation_intent(url, false);
|
|
1013
987
|
|
|
1014
988
|
/** @type {import('types').Navigation} */
|
|
1015
989
|
const navigation = {
|
|
@@ -1161,6 +1135,71 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
1161
1135
|
await Promise.all(promises);
|
|
1162
1136
|
},
|
|
1163
1137
|
|
|
1138
|
+
apply_action: async (result) => {
|
|
1139
|
+
if (result.type === 'error') {
|
|
1140
|
+
const url = new URL(location.href);
|
|
1141
|
+
|
|
1142
|
+
const { branch, route } = current;
|
|
1143
|
+
if (!route) return;
|
|
1144
|
+
|
|
1145
|
+
let i = current.branch.length;
|
|
1146
|
+
|
|
1147
|
+
while (i--) {
|
|
1148
|
+
if (route.errors[i]) {
|
|
1149
|
+
/** @type {import('./types').BranchNode | undefined} */
|
|
1150
|
+
let error_loaded;
|
|
1151
|
+
|
|
1152
|
+
let j = i;
|
|
1153
|
+
while (!branch[j]) j -= 1;
|
|
1154
|
+
try {
|
|
1155
|
+
error_loaded = {
|
|
1156
|
+
node: await /** @type {import('types').CSRPageNodeLoader } */ (route.errors[i])(),
|
|
1157
|
+
loader: /** @type {import('types').CSRPageNodeLoader } */ (route.errors[i]),
|
|
1158
|
+
data: {},
|
|
1159
|
+
server: null,
|
|
1160
|
+
shared: null
|
|
1161
|
+
};
|
|
1162
|
+
|
|
1163
|
+
const navigation_result = await get_navigation_result_from_branch({
|
|
1164
|
+
url,
|
|
1165
|
+
params: current.params,
|
|
1166
|
+
branch: branch.slice(0, j + 1).concat(error_loaded),
|
|
1167
|
+
status: 500, // TODO might not be 500?
|
|
1168
|
+
error: result.error,
|
|
1169
|
+
route
|
|
1170
|
+
});
|
|
1171
|
+
|
|
1172
|
+
current = navigation_result.state;
|
|
1173
|
+
|
|
1174
|
+
const post_update = pre_update();
|
|
1175
|
+
root.$set(navigation_result.props);
|
|
1176
|
+
post_update();
|
|
1177
|
+
|
|
1178
|
+
return;
|
|
1179
|
+
} catch (e) {
|
|
1180
|
+
continue;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
} else if (result.type === 'redirect') {
|
|
1185
|
+
goto(result.location, {}, []);
|
|
1186
|
+
} else {
|
|
1187
|
+
/** @type {Record<string, any>} */
|
|
1188
|
+
const props = { form: result.data };
|
|
1189
|
+
|
|
1190
|
+
if (result.status !== page.status) {
|
|
1191
|
+
props.page = {
|
|
1192
|
+
...page,
|
|
1193
|
+
status: result.status
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
const post_update = pre_update();
|
|
1198
|
+
root.$set(props);
|
|
1199
|
+
post_update();
|
|
1200
|
+
}
|
|
1201
|
+
},
|
|
1202
|
+
|
|
1164
1203
|
_start_router: () => {
|
|
1165
1204
|
history.scrollRestoration = 'manual';
|
|
1166
1205
|
|
|
@@ -1361,7 +1400,7 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
1361
1400
|
params,
|
|
1362
1401
|
routeId,
|
|
1363
1402
|
data: server_data_nodes,
|
|
1364
|
-
|
|
1403
|
+
form
|
|
1365
1404
|
}) => {
|
|
1366
1405
|
const url = new URL(location.href);
|
|
1367
1406
|
|
|
@@ -1402,7 +1441,7 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
1402
1441
|
original_error.message
|
|
1403
1442
|
)
|
|
1404
1443
|
: original_error,
|
|
1405
|
-
|
|
1444
|
+
form,
|
|
1406
1445
|
route: routes.find((route) => route.id === routeId) ?? null
|
|
1407
1446
|
});
|
|
1408
1447
|
} catch (e) {
|
|
@@ -1492,3 +1531,28 @@ function add_url_properties(type, target) {
|
|
|
1492
1531
|
|
|
1493
1532
|
return target;
|
|
1494
1533
|
}
|
|
1534
|
+
|
|
1535
|
+
function pre_update() {
|
|
1536
|
+
if (__SVELTEKIT_DEV__) {
|
|
1537
|
+
// Nasty hack to silence harmless warnings the user can do nothing about
|
|
1538
|
+
const warn = console.warn;
|
|
1539
|
+
console.warn = (...args) => {
|
|
1540
|
+
if (
|
|
1541
|
+
args.length === 1 &&
|
|
1542
|
+
/<(Layout|Page)(_[\w$]+)?> was created (with unknown|without expected) prop '(data|form)'/.test(
|
|
1543
|
+
args[0]
|
|
1544
|
+
)
|
|
1545
|
+
) {
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
warn(...args);
|
|
1549
|
+
};
|
|
1550
|
+
|
|
1551
|
+
return () => {
|
|
1552
|
+
tick().then(() => (console.warn = warn));
|
|
1553
|
+
check_for_removed_attributes();
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
return () => {};
|
|
1558
|
+
}
|
|
@@ -6,15 +6,7 @@ import { set_public_env } from '../env-public.js';
|
|
|
6
6
|
/**
|
|
7
7
|
* @param {{
|
|
8
8
|
* env: Record<string, string>;
|
|
9
|
-
* hydrate:
|
|
10
|
-
* status: number;
|
|
11
|
-
* error: Error | (import('../server/page/types').SerializedHttpError);
|
|
12
|
-
* node_ids: number[];
|
|
13
|
-
* params: Record<string, string>;
|
|
14
|
-
* routeId: string | null;
|
|
15
|
-
* data: Array<import('types').ServerDataNode | null>;
|
|
16
|
-
* errors: Record<string, any> | null;
|
|
17
|
-
* };
|
|
9
|
+
* hydrate: Parameters<import('./types').Client['_hydrate']>[0];
|
|
18
10
|
* paths: {
|
|
19
11
|
* assets: string;
|
|
20
12
|
* base: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { applyAction } from '$app/forms';
|
|
1
2
|
import {
|
|
2
3
|
afterNavigate,
|
|
3
4
|
beforeNavigate,
|
|
@@ -21,6 +22,7 @@ export interface Client {
|
|
|
21
22
|
invalidateAll: typeof invalidateAll;
|
|
22
23
|
prefetch: typeof prefetch;
|
|
23
24
|
prefetch_routes: typeof prefetchRoutes;
|
|
25
|
+
apply_action: typeof applyAction;
|
|
24
26
|
|
|
25
27
|
// private API
|
|
26
28
|
_hydrate: (opts: {
|
|
@@ -30,27 +32,21 @@ export interface Client {
|
|
|
30
32
|
params: Record<string, string>;
|
|
31
33
|
routeId: string | null;
|
|
32
34
|
data: Array<import('types').ServerDataNode | null>;
|
|
33
|
-
|
|
35
|
+
form: Record<string, any> | null;
|
|
34
36
|
}) => Promise<void>;
|
|
35
37
|
_start_router: () => void;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
export type NavigationIntent = {
|
|
39
|
-
/**
|
|
40
|
-
* `url.pathname + url.search`
|
|
41
|
-
*/
|
|
41
|
+
/** `url.pathname + url.search` */
|
|
42
42
|
id: string;
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
/** Whether we are invalidating or navigating */
|
|
44
|
+
invalidating: boolean;
|
|
45
|
+
/** The route parameters */
|
|
46
46
|
params: Record<string, string>;
|
|
47
|
-
/**
|
|
48
|
-
* The route that matches `path`
|
|
49
|
-
*/
|
|
47
|
+
/** The route that matches `path` */
|
|
50
48
|
route: CSRRoute;
|
|
51
|
-
/**
|
|
52
|
-
* The destination URL
|
|
53
|
-
*/
|
|
49
|
+
/** The destination URL */
|
|
54
50
|
url: URL;
|
|
55
51
|
};
|
|
56
52
|
|
package/src/runtime/control.js
CHANGED
|
@@ -31,3 +31,70 @@ export class Redirect {
|
|
|
31
31
|
this.location = location;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @template {Record<string, unknown> | undefined} [T=undefined]
|
|
37
|
+
*/
|
|
38
|
+
export class ValidationError {
|
|
39
|
+
/**
|
|
40
|
+
* @param {number} status
|
|
41
|
+
* @param {T} [data]
|
|
42
|
+
*/
|
|
43
|
+
constructor(status, data) {
|
|
44
|
+
this.status = status;
|
|
45
|
+
this.data = data;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Creates an `HttpError` object with an HTTP status code and an optional message.
|
|
51
|
+
* This object, if thrown during request handling, will cause SvelteKit to
|
|
52
|
+
* return an error response without invoking `handleError`
|
|
53
|
+
* @param {number} status
|
|
54
|
+
* @param {string | undefined} [message]
|
|
55
|
+
*/
|
|
56
|
+
export function error(status, message) {
|
|
57
|
+
return new HttpError(status, message);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Creates a `Redirect` object. If thrown during request handling, SvelteKit will
|
|
62
|
+
* return a redirect response.
|
|
63
|
+
* @param {number} status
|
|
64
|
+
* @param {string} location
|
|
65
|
+
*/
|
|
66
|
+
export function redirect(status, location) {
|
|
67
|
+
if (isNaN(status) || status < 300 || status > 399) {
|
|
68
|
+
throw new Error('Invalid status code');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return new Redirect(status, location);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Generates a JSON `Response` object from the supplied data.
|
|
76
|
+
* @param {any} data
|
|
77
|
+
* @param {ResponseInit} [init]
|
|
78
|
+
*/
|
|
79
|
+
export function json(data, init) {
|
|
80
|
+
// TODO deprecate this in favour of `Response.json` when it's
|
|
81
|
+
// more widely supported
|
|
82
|
+
const headers = new Headers(init?.headers);
|
|
83
|
+
if (!headers.has('content-type')) {
|
|
84
|
+
headers.set('content-type', 'application/json');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return new Response(JSON.stringify(data), {
|
|
88
|
+
...init,
|
|
89
|
+
headers
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Generates a `ValidationError` object.
|
|
95
|
+
* @param {number} status
|
|
96
|
+
* @param {Record<string, any> | undefined} [data]
|
|
97
|
+
*/
|
|
98
|
+
export function invalid(status, data) {
|
|
99
|
+
return new ValidationError(status, data);
|
|
100
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as cookie from 'cookie';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {Request} request
|
|
5
|
+
* @param {URL} url
|
|
6
|
+
*/
|
|
7
|
+
export function get_cookies(request, url) {
|
|
8
|
+
const initial_cookies = cookie.parse(request.headers.get('cookie') ?? '');
|
|
9
|
+
|
|
10
|
+
/** @type {Array<{ name: string, value: string, options: import('cookie').CookieSerializeOptions }>} */
|
|
11
|
+
const new_cookies = [];
|
|
12
|
+
|
|
13
|
+
/** @type {import('types').Cookies} */
|
|
14
|
+
const cookies = {
|
|
15
|
+
get(name, opts) {
|
|
16
|
+
const decode = opts?.decode || decodeURIComponent;
|
|
17
|
+
|
|
18
|
+
let i = new_cookies.length;
|
|
19
|
+
while (i--) {
|
|
20
|
+
const cookie = new_cookies[i];
|
|
21
|
+
|
|
22
|
+
if (
|
|
23
|
+
cookie.name === name &&
|
|
24
|
+
domain_matches(url.hostname, cookie.options.domain) &&
|
|
25
|
+
path_matches(url.pathname, cookie.options.path)
|
|
26
|
+
) {
|
|
27
|
+
return cookie.value;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return name in initial_cookies ? decode(initial_cookies[name]) : undefined;
|
|
32
|
+
},
|
|
33
|
+
set(name, value, options = {}) {
|
|
34
|
+
new_cookies.push({
|
|
35
|
+
name,
|
|
36
|
+
value,
|
|
37
|
+
options: {
|
|
38
|
+
httpOnly: true,
|
|
39
|
+
secure: true,
|
|
40
|
+
...options
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
delete(name) {
|
|
45
|
+
new_cookies.push({ name, value: '', options: { expires: new Date(0) } });
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return { cookies, new_cookies };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @param {string} hostname
|
|
54
|
+
* @param {string} [constraint]
|
|
55
|
+
*/
|
|
56
|
+
export function domain_matches(hostname, constraint) {
|
|
57
|
+
if (!constraint) return true;
|
|
58
|
+
|
|
59
|
+
const normalized = constraint[0] === '.' ? constraint.slice(1) : constraint;
|
|
60
|
+
|
|
61
|
+
if (hostname === normalized) return true;
|
|
62
|
+
return hostname.endsWith('.' + normalized);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {string} path
|
|
67
|
+
* @param {string} [constraint]
|
|
68
|
+
*/
|
|
69
|
+
export function path_matches(path, constraint) {
|
|
70
|
+
if (!constraint) return true;
|
|
71
|
+
|
|
72
|
+
const normalized = constraint.endsWith('/') ? constraint.slice(0, -1) : constraint;
|
|
73
|
+
|
|
74
|
+
if (path === normalized) return true;
|
|
75
|
+
return path.startsWith(normalized + '/');
|
|
76
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { json } from '../../exports/index.js';
|
|
2
|
+
import { Redirect, ValidationError } from '../control.js';
|
|
2
3
|
import { check_method_names, method_not_allowed } from './utils.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -56,6 +57,8 @@ export async function render_endpoint(event, mod, state) {
|
|
|
56
57
|
status: error.status,
|
|
57
58
|
headers: { location: error.location }
|
|
58
59
|
});
|
|
60
|
+
} else if (error instanceof ValidationError) {
|
|
61
|
+
return json(error.data, { status: error.status });
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
throw error;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as cookie from 'cookie';
|
|
1
2
|
import { render_endpoint } from './endpoint.js';
|
|
2
3
|
import { render_page } from './page/index.js';
|
|
3
4
|
import { render_response } from './page/render.js';
|
|
@@ -8,6 +9,7 @@ import { decode_params, disable_search, normalize_path } from '../../utils/url.j
|
|
|
8
9
|
import { exec } from '../../utils/routing.js';
|
|
9
10
|
import { render_data } from './data/index.js';
|
|
10
11
|
import { DATA_SUFFIX } from '../../constants.js';
|
|
12
|
+
import { get_cookies } from './cookie.js';
|
|
11
13
|
|
|
12
14
|
/* global __SVELTEKIT_ADAPTER_NAME__ */
|
|
13
15
|
|
|
@@ -35,31 +37,6 @@ export async function respond(request, options, state) {
|
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
const { parameter, allowed } = options.method_override;
|
|
39
|
-
const method_override = url.searchParams.get(parameter)?.toUpperCase();
|
|
40
|
-
|
|
41
|
-
if (method_override) {
|
|
42
|
-
if (request.method === 'POST') {
|
|
43
|
-
if (allowed.includes(method_override)) {
|
|
44
|
-
request = new Proxy(request, {
|
|
45
|
-
get: (target, property, _receiver) => {
|
|
46
|
-
if (property === 'method') return method_override;
|
|
47
|
-
return Reflect.get(target, property, target);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
} else {
|
|
51
|
-
const verb = allowed.length === 0 ? 'enabled' : 'allowed';
|
|
52
|
-
const body = `${parameter}=${method_override} is not ${verb}. See https://kit.svelte.dev/docs/configuration#methodoverride`;
|
|
53
|
-
|
|
54
|
-
return new Response(body, {
|
|
55
|
-
status: 400
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
} else {
|
|
59
|
-
throw new Error(`${parameter}=${method_override} is only allowed with POST requests`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
40
|
let decoded;
|
|
64
41
|
try {
|
|
65
42
|
decoded = decodeURI(url.pathname);
|
|
@@ -116,16 +93,16 @@ export async function respond(request, options, state) {
|
|
|
116
93
|
}
|
|
117
94
|
}
|
|
118
95
|
|
|
119
|
-
/** @type {
|
|
96
|
+
/** @type {Record<string, string>} */
|
|
120
97
|
const headers = {};
|
|
121
98
|
|
|
122
|
-
|
|
123
|
-
const cookies = [];
|
|
99
|
+
const { cookies, new_cookies } = get_cookies(request, url);
|
|
124
100
|
|
|
125
101
|
if (state.prerendering) disable_search(url);
|
|
126
102
|
|
|
127
103
|
/** @type {import('types').RequestEvent} */
|
|
128
104
|
const event = {
|
|
105
|
+
cookies,
|
|
129
106
|
getClientAddress:
|
|
130
107
|
state.getClientAddress ||
|
|
131
108
|
(() => {
|
|
@@ -144,15 +121,9 @@ export async function respond(request, options, state) {
|
|
|
144
121
|
const value = new_headers[key];
|
|
145
122
|
|
|
146
123
|
if (lower === 'set-cookie') {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (cookies.includes(cookie)) {
|
|
151
|
-
throw new Error(`"${key}" header already has cookie with same value`);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
cookies.push(cookie);
|
|
155
|
-
}
|
|
124
|
+
throw new Error(
|
|
125
|
+
`Use \`event.cookie.set(name, value, options)\` instead of \`event.setHeaders\` to set cookies`
|
|
126
|
+
);
|
|
156
127
|
} else if (lower in headers) {
|
|
157
128
|
throw new Error(`"${key}" header is already set`);
|
|
158
129
|
} else {
|
|
@@ -244,7 +215,6 @@ export async function respond(request, options, state) {
|
|
|
244
215
|
error: null,
|
|
245
216
|
branch: [],
|
|
246
217
|
fetched: [],
|
|
247
|
-
validation_errors: undefined,
|
|
248
218
|
cookies: [],
|
|
249
219
|
resolve_opts
|
|
250
220
|
});
|
|
@@ -275,8 +245,11 @@ export async function respond(request, options, state) {
|
|
|
275
245
|
}
|
|
276
246
|
}
|
|
277
247
|
|
|
278
|
-
for (const
|
|
279
|
-
response.headers.append(
|
|
248
|
+
for (const new_cookie of new_cookies) {
|
|
249
|
+
response.headers.append(
|
|
250
|
+
'set-cookie',
|
|
251
|
+
cookie.serialize(new_cookie.name, new_cookie.value, new_cookie.options)
|
|
252
|
+
);
|
|
280
253
|
}
|
|
281
254
|
|
|
282
255
|
// respond with 304 if etag matches
|
|
@@ -338,6 +311,14 @@ export async function respond(request, options, state) {
|
|
|
338
311
|
} catch (e) {
|
|
339
312
|
const error = coalesce_to_error(e);
|
|
340
313
|
return handle_fatal_error(event, options, error);
|
|
314
|
+
} finally {
|
|
315
|
+
event.cookies.set = () => {
|
|
316
|
+
throw new Error('Cannot use `cookies.set(...)` after the response has been generated');
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
event.setHeaders = () => {
|
|
320
|
+
throw new Error('Cannot use `setHeaders(...)` after the response has been generated');
|
|
321
|
+
};
|
|
341
322
|
}
|
|
342
323
|
}
|
|
343
324
|
|