@sveltejs/kit 1.0.0-next.526 → 1.0.0-next.528
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/constants.js +0 -2
- package/src/exports/vite/dev/index.js +2 -0
- package/src/runtime/client/client.js +16 -7
- package/src/runtime/server/cookie.js +55 -12
- package/src/runtime/server/data/index.js +2 -6
- package/src/runtime/server/index.js +10 -5
- package/src/runtime/server/page/index.js +2 -2
- package/src/runtime/server/utils.js +2 -2
- package/src/utils/url.js +17 -0
- package/types/ambient.d.ts +23 -5
- package/types/index.d.ts +2 -0
package/package.json
CHANGED
package/src/constants.js
CHANGED
|
@@ -291,6 +291,8 @@ export async function dev(vite, vite_config, svelte_config) {
|
|
|
291
291
|
remove_static_middlewares(vite.middlewares);
|
|
292
292
|
|
|
293
293
|
vite.middlewares.use(async (req, res) => {
|
|
294
|
+
// Vite's base middleware strips out the base path. Restore it
|
|
295
|
+
req.url = req.originalUrl;
|
|
294
296
|
try {
|
|
295
297
|
const base = `${vite.config.server.https ? 'https' : 'http'}://${
|
|
296
298
|
req.headers[':authority'] || req.headers.host
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { onMount, tick } from 'svelte';
|
|
2
|
-
import { make_trackable, decode_params, normalize_path } from '../../utils/url.js';
|
|
2
|
+
import { make_trackable, decode_params, normalize_path, add_data_suffix } from '../../utils/url.js';
|
|
3
3
|
import { find_anchor, get_base_uri, scroll_state } from './utils.js';
|
|
4
4
|
import {
|
|
5
5
|
lock_fetch,
|
|
@@ -14,7 +14,6 @@ import Root from '__GENERATED__/root.svelte';
|
|
|
14
14
|
import { nodes, server_loads, dictionary, matchers, hooks } from '__GENERATED__/client-manifest.js';
|
|
15
15
|
import { HttpError, Redirect } from '../control.js';
|
|
16
16
|
import { stores } from './singletons.js';
|
|
17
|
-
import { DATA_SUFFIX } from '../../constants.js';
|
|
18
17
|
import { unwrap_promises } from '../../utils/promises.js';
|
|
19
18
|
import * as devalue from 'devalue';
|
|
20
19
|
|
|
@@ -164,13 +163,19 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
164
163
|
|
|
165
164
|
/**
|
|
166
165
|
* @param {string | URL} url
|
|
167
|
-
* @param {{ noscroll?: boolean; replaceState?: boolean; keepfocus?: boolean; state?: any }} opts
|
|
166
|
+
* @param {{ noscroll?: boolean; replaceState?: boolean; keepfocus?: boolean; state?: any; invalidateAll?: boolean }} opts
|
|
168
167
|
* @param {string[]} redirect_chain
|
|
169
168
|
* @param {{}} [nav_token]
|
|
170
169
|
*/
|
|
171
170
|
async function goto(
|
|
172
171
|
url,
|
|
173
|
-
{
|
|
172
|
+
{
|
|
173
|
+
noscroll = false,
|
|
174
|
+
replaceState = false,
|
|
175
|
+
keepfocus = false,
|
|
176
|
+
state = {},
|
|
177
|
+
invalidateAll = false
|
|
178
|
+
},
|
|
174
179
|
redirect_chain,
|
|
175
180
|
nav_token
|
|
176
181
|
) {
|
|
@@ -188,7 +193,11 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
188
193
|
replaceState
|
|
189
194
|
},
|
|
190
195
|
nav_token,
|
|
191
|
-
accepted: () => {
|
|
196
|
+
accepted: () => {
|
|
197
|
+
if (invalidateAll) {
|
|
198
|
+
force_invalidation = true;
|
|
199
|
+
}
|
|
200
|
+
},
|
|
192
201
|
blocked: () => {},
|
|
193
202
|
type: 'goto'
|
|
194
203
|
});
|
|
@@ -1212,7 +1221,7 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
1212
1221
|
post_update();
|
|
1213
1222
|
}
|
|
1214
1223
|
} else if (result.type === 'redirect') {
|
|
1215
|
-
goto(result.location, {}, []);
|
|
1224
|
+
goto(result.location, { invalidateAll: true }, []);
|
|
1216
1225
|
} else {
|
|
1217
1226
|
/** @type {Record<string, any>} */
|
|
1218
1227
|
const props = {
|
|
@@ -1501,7 +1510,7 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
1501
1510
|
*/
|
|
1502
1511
|
async function load_data(url, invalid) {
|
|
1503
1512
|
const data_url = new URL(url);
|
|
1504
|
-
data_url.pathname = url.pathname
|
|
1513
|
+
data_url.pathname = add_data_suffix(url.pathname);
|
|
1505
1514
|
|
|
1506
1515
|
const res = await native_fetch(data_url.href, {
|
|
1507
1516
|
headers: {
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { parse, serialize } from 'cookie';
|
|
2
|
+
import { has_data_suffix, normalize_path, strip_data_suffix } from '../../utils/url.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tracks all cookies set during dev mode so we can emit warnings
|
|
6
|
+
* when we detect that there's likely cookie misusage due to wrong paths
|
|
7
|
+
*
|
|
8
|
+
* @type {Record<string, Set<string>>} */
|
|
9
|
+
const cookie_paths = {};
|
|
2
10
|
|
|
3
11
|
/**
|
|
4
12
|
* @param {Request} request
|
|
5
13
|
* @param {URL} url
|
|
14
|
+
* @param {Pick<import('types').SSROptions, 'dev' | 'trailing_slash'>} options
|
|
6
15
|
*/
|
|
7
|
-
export function get_cookies(request, url) {
|
|
16
|
+
export function get_cookies(request, url, options) {
|
|
8
17
|
const header = request.headers.get('cookie') ?? '';
|
|
9
18
|
|
|
10
19
|
const initial_cookies = parse(header);
|
|
@@ -42,7 +51,17 @@ export function get_cookies(request, url) {
|
|
|
42
51
|
|
|
43
52
|
const decode = opts?.decode || decodeURIComponent;
|
|
44
53
|
const req_cookies = parse(header, { decode });
|
|
45
|
-
|
|
54
|
+
const cookie = req_cookies[name]; // the decoded string or undefined
|
|
55
|
+
|
|
56
|
+
if (!options.dev || cookie) {
|
|
57
|
+
return cookie;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (c || cookie_paths[name]?.size > 0) {
|
|
61
|
+
console.warn(
|
|
62
|
+
`Cookie with name '${name}' was not found, but a cookie with that name exists at a sub path. Did you mean to set its 'path' to '/'?`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
46
65
|
},
|
|
47
66
|
|
|
48
67
|
/**
|
|
@@ -51,14 +70,43 @@ export function get_cookies(request, url) {
|
|
|
51
70
|
* @param {import('cookie').CookieSerializeOptions} opts
|
|
52
71
|
*/
|
|
53
72
|
set(name, value, opts = {}) {
|
|
73
|
+
let path = opts.path;
|
|
74
|
+
if (!path) {
|
|
75
|
+
const normalized = normalize_path(
|
|
76
|
+
// Remove suffix: 'foo/__data.json' would mean the cookie path is '/foo',
|
|
77
|
+
// whereas a direct hit of /foo would mean the cookie path is '/'
|
|
78
|
+
has_data_suffix(url.pathname) ? strip_data_suffix(url.pathname) : url.pathname,
|
|
79
|
+
options.trailing_slash
|
|
80
|
+
);
|
|
81
|
+
// Emulate browser-behavior: if the cookie is set at '/foo/bar', its path is '/foo'
|
|
82
|
+
path = normalized.split('/').slice(0, -1).join('/') || '/';
|
|
83
|
+
}
|
|
84
|
+
|
|
54
85
|
new_cookies[name] = {
|
|
55
86
|
name,
|
|
56
87
|
value,
|
|
57
88
|
options: {
|
|
58
89
|
...defaults,
|
|
59
|
-
...opts
|
|
90
|
+
...opts,
|
|
91
|
+
path
|
|
60
92
|
}
|
|
61
93
|
};
|
|
94
|
+
|
|
95
|
+
if (options.dev) {
|
|
96
|
+
cookie_paths[name] = cookie_paths[name] || new Set();
|
|
97
|
+
if (!value) {
|
|
98
|
+
if (!cookie_paths[name].has(path) && cookie_paths[name].size > 0) {
|
|
99
|
+
console.warn(
|
|
100
|
+
`Trying to delete cookie '${name}' at path '${path}', but a cookie with that name only exists at a different path.`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
cookie_paths[name].delete(path);
|
|
104
|
+
} else {
|
|
105
|
+
// We could also emit a warning here if the cookie already exists at a different path,
|
|
106
|
+
// but that's more likely a false positive because it's valid to set the same name at different paths
|
|
107
|
+
cookie_paths[name].add(path);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
62
110
|
},
|
|
63
111
|
|
|
64
112
|
/**
|
|
@@ -66,15 +114,10 @@ export function get_cookies(request, url) {
|
|
|
66
114
|
* @param {import('cookie').CookieSerializeOptions} opts
|
|
67
115
|
*/
|
|
68
116
|
delete(name, opts = {}) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
...defaults,
|
|
74
|
-
...opts,
|
|
75
|
-
maxAge: 0
|
|
76
|
-
}
|
|
77
|
-
};
|
|
117
|
+
cookies.set(name, '', {
|
|
118
|
+
...opts,
|
|
119
|
+
maxAge: 0
|
|
120
|
+
});
|
|
78
121
|
},
|
|
79
122
|
|
|
80
123
|
/**
|
|
@@ -3,8 +3,7 @@ import { normalize_error } from '../../../utils/error.js';
|
|
|
3
3
|
import { once } from '../../../utils/functions.js';
|
|
4
4
|
import { load_server_data } from '../page/load_data.js';
|
|
5
5
|
import { data_response, handle_error_and_jsonify } from '../utils.js';
|
|
6
|
-
import { normalize_path } from '../../../utils/url.js';
|
|
7
|
-
import { DATA_SUFFIX } from '../../../constants.js';
|
|
6
|
+
import { normalize_path, strip_data_suffix } from '../../../utils/url.js';
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* @param {import('types').RequestEvent} event
|
|
@@ -31,10 +30,7 @@ export async function render_data(event, route, options, state) {
|
|
|
31
30
|
let aborted = false;
|
|
32
31
|
|
|
33
32
|
const url = new URL(event.url);
|
|
34
|
-
url.pathname = normalize_path(
|
|
35
|
-
url.pathname.slice(0, -DATA_SUFFIX.length),
|
|
36
|
-
options.trailing_slash
|
|
37
|
-
);
|
|
33
|
+
url.pathname = normalize_path(strip_data_suffix(url.pathname), options.trailing_slash);
|
|
38
34
|
|
|
39
35
|
const new_event = { ...event, url };
|
|
40
36
|
|
|
@@ -5,10 +5,15 @@ import { respond_with_error } from './page/respond_with_error.js';
|
|
|
5
5
|
import { coalesce_to_error } from '../../utils/error.js';
|
|
6
6
|
import { is_form_content_type } from '../../utils/http.js';
|
|
7
7
|
import { GENERIC_ERROR, handle_fatal_error } from './utils.js';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
decode_params,
|
|
10
|
+
disable_search,
|
|
11
|
+
has_data_suffix,
|
|
12
|
+
normalize_path,
|
|
13
|
+
strip_data_suffix
|
|
14
|
+
} from '../../utils/url.js';
|
|
9
15
|
import { exec } from '../../utils/routing.js';
|
|
10
16
|
import { render_data } from './data/index.js';
|
|
11
|
-
import { DATA_SUFFIX } from '../../constants.js';
|
|
12
17
|
import { add_cookies_to_headers, get_cookies } from './cookie.js';
|
|
13
18
|
import { HttpError } from '../control.js';
|
|
14
19
|
import { create_fetch } from './fetch.js';
|
|
@@ -57,8 +62,8 @@ export async function respond(request, options, state) {
|
|
|
57
62
|
decoded = decoded.slice(options.paths.base.length) || '/';
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
const is_data_request = decoded
|
|
61
|
-
if (is_data_request) decoded = decoded
|
|
65
|
+
const is_data_request = has_data_suffix(decoded);
|
|
66
|
+
if (is_data_request) decoded = strip_data_suffix(decoded);
|
|
62
67
|
|
|
63
68
|
if (!state.prerendering?.fallback) {
|
|
64
69
|
const matchers = await options.manifest._.matchers();
|
|
@@ -96,7 +101,7 @@ export async function respond(request, options, state) {
|
|
|
96
101
|
/** @type {Record<string, string>} */
|
|
97
102
|
const headers = {};
|
|
98
103
|
|
|
99
|
-
const { cookies, new_cookies, get_cookie_header } = get_cookies(request, url);
|
|
104
|
+
const { cookies, new_cookies, get_cookie_header } = get_cookies(request, url, options);
|
|
100
105
|
|
|
101
106
|
if (state.prerendering) disable_search(url);
|
|
102
107
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as devalue from 'devalue';
|
|
2
|
-
import { DATA_SUFFIX } from '../../../constants.js';
|
|
3
2
|
import { compact } from '../../../utils/array.js';
|
|
4
3
|
import { normalize_error } from '../../../utils/error.js';
|
|
4
|
+
import { add_data_suffix } from '../../../utils/url.js';
|
|
5
5
|
import { HttpError, Redirect } from '../../control.js';
|
|
6
6
|
import {
|
|
7
7
|
get_option,
|
|
@@ -76,7 +76,7 @@ export async function render_page(event, route, page, options, state, resolve_op
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
const should_prerender_data = nodes.some((node) => node?.server);
|
|
79
|
-
const data_pathname = event.url.pathname
|
|
79
|
+
const data_pathname = add_data_suffix(event.url.pathname);
|
|
80
80
|
|
|
81
81
|
// it's crucial that we do this before returning the non-SSR response, otherwise
|
|
82
82
|
// SvelteKit will erroneously believe that the path has been prerendered,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as devalue from 'devalue';
|
|
2
|
-
import { DATA_SUFFIX } from '../../constants.js';
|
|
3
2
|
import { negotiate } from '../../utils/http.js';
|
|
3
|
+
import { has_data_suffix } from '../../utils/url.js';
|
|
4
4
|
import { HttpError } from '../control.js';
|
|
5
5
|
|
|
6
6
|
/** @param {any} body */
|
|
@@ -144,7 +144,7 @@ export function handle_fatal_error(event, options, error) {
|
|
|
144
144
|
'text/html'
|
|
145
145
|
]);
|
|
146
146
|
|
|
147
|
-
if (event.url.pathname
|
|
147
|
+
if (has_data_suffix(event.url.pathname) || type === 'application/json') {
|
|
148
148
|
return new Response(JSON.stringify(body), {
|
|
149
149
|
status,
|
|
150
150
|
headers: { 'content-type': 'application/json; charset=utf-8' }
|
package/src/utils/url.js
CHANGED
|
@@ -142,3 +142,20 @@ export function disable_search(url) {
|
|
|
142
142
|
});
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
|
+
|
|
146
|
+
const DATA_SUFFIX = '/__data.json';
|
|
147
|
+
|
|
148
|
+
/** @param {string} pathname */
|
|
149
|
+
export function has_data_suffix(pathname) {
|
|
150
|
+
return pathname.endsWith(DATA_SUFFIX);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** @param {string} pathname */
|
|
154
|
+
export function add_data_suffix(pathname) {
|
|
155
|
+
return pathname.replace(/\/$/, '') + DATA_SUFFIX;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** @param {string} pathname */
|
|
159
|
+
export function strip_data_suffix(pathname) {
|
|
160
|
+
return pathname.slice(0, -DATA_SUFFIX.length);
|
|
161
|
+
}
|
package/types/ambient.d.ts
CHANGED
|
@@ -182,14 +182,32 @@ declare module '$app/navigation' {
|
|
|
182
182
|
* Returns a Promise that resolves when SvelteKit navigates (or fails to navigate, in which case the promise rejects) to the specified `url`.
|
|
183
183
|
*
|
|
184
184
|
* @param url Where to navigate to. Note that if you've set [`config.kit.paths.base`](https://kit.svelte.dev/docs/configuration#paths) and the URL is root-relative, you need to prepend the base path if you want to navigate within the app.
|
|
185
|
-
* @param opts
|
|
186
|
-
* @param opts.noscroll If `true`, the browser will maintain its scroll position rather than scrolling to the top of the page after navigation
|
|
187
|
-
* @param opts.keepfocus If `true`, the currently focused element will retain focus after navigation. Otherwise, focus will be reset to the body
|
|
188
|
-
* @param opts.state The state of the new/updated history entry
|
|
185
|
+
* @param opts Options related to the navigation
|
|
189
186
|
*/
|
|
190
187
|
export function goto(
|
|
191
188
|
url: string | URL,
|
|
192
|
-
opts?: {
|
|
189
|
+
opts?: {
|
|
190
|
+
/**
|
|
191
|
+
* If `true`, will replace the current `history` entry rather than creating a new one with `pushState`
|
|
192
|
+
*/
|
|
193
|
+
replaceState?: boolean;
|
|
194
|
+
/**
|
|
195
|
+
* If `true`, the browser will maintain its scroll position rather than scrolling to the top of the page after navigation
|
|
196
|
+
*/
|
|
197
|
+
noscroll?: boolean;
|
|
198
|
+
/**
|
|
199
|
+
* If `true`, the currently focused element will retain focus after navigation. Otherwise, focus will be reset to the body
|
|
200
|
+
*/
|
|
201
|
+
keepfocus?: boolean;
|
|
202
|
+
/**
|
|
203
|
+
* The state of the new/updated history entry
|
|
204
|
+
*/
|
|
205
|
+
state?: any;
|
|
206
|
+
/**
|
|
207
|
+
* If `true`, all `load` functions of the page will be rerun. See https://kit.svelte.dev/docs/load#invalidation for more info on invalidation.
|
|
208
|
+
*/
|
|
209
|
+
invalidateAll?: boolean;
|
|
210
|
+
}
|
|
193
211
|
): Promise<void>;
|
|
194
212
|
/**
|
|
195
213
|
* Causes any `load` functions belonging to the currently active page to re-run if they depend on the `url` in question, via `fetch` or `depends`. Returns a `Promise` that resolves when the page is subsequently updated.
|
package/types/index.d.ts
CHANGED
|
@@ -161,6 +161,8 @@ export interface Cookies {
|
|
|
161
161
|
|
|
162
162
|
/**
|
|
163
163
|
* Deletes a cookie by setting its value to an empty string and setting the expiry date in the past.
|
|
164
|
+
*
|
|
165
|
+
* By default, the `path` of a cookie is the 'directory' of the current pathname. In most cases you should explicitly set `path: '/'` to make the cookie available throughout your app.
|
|
164
166
|
*/
|
|
165
167
|
delete(name: string, opts?: import('cookie').CookieSerializeOptions): void;
|
|
166
168
|
|