@sveltejs/kit 1.0.0-next.482 → 1.0.0-next.484

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "1.0.0-next.482",
3
+ "version": "1.0.0-next.484",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -11,6 +11,7 @@
11
11
  "type": "module",
12
12
  "dependencies": {
13
13
  "@sveltejs/vite-plugin-svelte": "^1.0.5",
14
+ "@types/cookie": "^0.5.1",
14
15
  "cookie": "^0.5.0",
15
16
  "devalue": "^3.1.2",
16
17
  "kleur": "^4.1.4",
@@ -26,7 +27,6 @@
26
27
  "devDependencies": {
27
28
  "@playwright/test": "^1.25.0",
28
29
  "@types/connect": "^3.4.35",
29
- "@types/cookie": "^0.5.1",
30
30
  "@types/marked": "^4.0.3",
31
31
  "@types/mime": "^3.0.0",
32
32
  "@types/node": "^16.11.36",
@@ -38,7 +38,7 @@
38
38
  "svelte-preprocess": "^4.10.6",
39
39
  "typescript": "^4.8.2",
40
40
  "uvu": "^0.5.3",
41
- "vite": "^3.1.0"
41
+ "vite": "^3.1.1"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "svelte": "^3.44.0",
@@ -68,7 +68,7 @@ export async function write_types(config, manifest_data, file) {
68
68
  return;
69
69
  }
70
70
 
71
- const id = path.relative(config.kit.files.routes, path.dirname(file));
71
+ const id = path.posix.relative(config.kit.files.routes, path.dirname(file));
72
72
 
73
73
  const route = manifest_data.routes.find((route) => route.id === id);
74
74
  if (!route) return; // this shouldn't ever happen
@@ -1,49 +1,76 @@
1
- import * as cookie from 'cookie';
1
+ import { parse, serialize } from 'cookie';
2
+
3
+ /** @type {import('cookie').CookieSerializeOptions} */
4
+ const DEFAULT_SERIALIZE_OPTIONS = {
5
+ httpOnly: true,
6
+ secure: true,
7
+ sameSite: 'lax'
8
+ };
2
9
 
3
10
  /**
4
11
  * @param {Request} request
5
12
  * @param {URL} url
6
13
  */
7
14
  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 = [];
15
+ /** @type {Map<string, import('./page/types').Cookie>} */
16
+ const new_cookies = new Map();
12
17
 
13
18
  /** @type {import('types').Cookies} */
14
19
  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];
20
+ // The JSDoc param annotations appearing below for get, set and delete
21
+ // are necessary to expose the `cookie` library types to
22
+ // typescript users. `@type {import('types').Cookies}` above is not
23
+ // sufficient to do so.
21
24
 
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
- }
25
+ /**
26
+ * @param {string} name
27
+ * @param {import('cookie').CookieParseOptions} opts
28
+ */
29
+ get(name, opts) {
30
+ const c = new_cookies.get(name);
31
+ if (
32
+ c &&
33
+ domain_matches(url.hostname, c.options.domain) &&
34
+ path_matches(url.pathname, c.options.path)
35
+ ) {
36
+ return c.value;
29
37
  }
30
38
 
31
- return name in initial_cookies ? decode(initial_cookies[name]) : undefined;
39
+ const decode = opts?.decode || decodeURIComponent;
40
+ const req_cookies = parse(request.headers.get('cookie') ?? '', { decode });
41
+ return req_cookies[name]; // the decoded string or undefined
32
42
  },
33
- set(name, value, options = {}) {
34
- new_cookies.push({
43
+
44
+ /**
45
+ * @param {string} name
46
+ * @param {string} value
47
+ * @param {import('cookie').CookieSerializeOptions} opts
48
+ */
49
+ set(name, value, opts = {}) {
50
+ new_cookies.set(name, {
35
51
  name,
36
52
  value,
37
53
  options: {
38
- httpOnly: true,
39
- secure: true,
40
- ...options
54
+ ...DEFAULT_SERIALIZE_OPTIONS,
55
+ ...opts
41
56
  }
42
57
  });
43
58
  },
44
- delete(name) {
45
- new_cookies.push({ name, value: '', options: { expires: new Date(0) } });
46
- delete initial_cookies[name];
59
+
60
+ /**
61
+ * @param {string} name
62
+ * @param {import('cookie').CookieSerializeOptions} opts
63
+ */
64
+ delete(name, opts = {}) {
65
+ new_cookies.set(name, {
66
+ name,
67
+ value: '',
68
+ options: {
69
+ ...DEFAULT_SERIALIZE_OPTIONS,
70
+ ...opts,
71
+ maxAge: 0
72
+ }
73
+ });
47
74
  }
48
75
  };
49
76
 
@@ -75,3 +102,14 @@ export function path_matches(path, constraint) {
75
102
  if (path === normalized) return true;
76
103
  return path.startsWith(normalized + '/');
77
104
  }
105
+
106
+ /**
107
+ * @param {Headers} headers
108
+ * @param {import('./page/types').Cookie[]} cookies
109
+ */
110
+ export function add_cookies_to_headers(headers, cookies) {
111
+ for (const new_cookie of cookies) {
112
+ const { name, value, options } = new_cookie;
113
+ headers.append('set-cookie', serialize(name, value, options));
114
+ }
115
+ }
@@ -1,4 +1,3 @@
1
- import * as cookie from 'cookie';
2
1
  import { render_endpoint } from './endpoint.js';
3
2
  import { render_page } from './page/index.js';
4
3
  import { render_response } from './page/render.js';
@@ -9,7 +8,7 @@ import { decode_params, disable_search, normalize_path } from '../../utils/url.j
9
8
  import { exec } from '../../utils/routing.js';
10
9
  import { render_data } from './data/index.js';
11
10
  import { DATA_SUFFIX } from '../../constants.js';
12
- import { get_cookies } from './cookie.js';
11
+ import { add_cookies_to_headers, get_cookies } from './cookie.js';
13
12
  import { HttpError } from '../control.js';
14
13
 
15
14
  /* global __SVELTEKIT_ADAPTER_NAME__ */
@@ -246,12 +245,7 @@ export async function respond(request, options, state) {
246
245
  }
247
246
  }
248
247
 
249
- for (const new_cookie of new_cookies) {
250
- response.headers.append(
251
- 'set-cookie',
252
- cookie.serialize(new_cookie.name, new_cookie.value, new_cookie.options)
253
- );
254
- }
248
+ add_cookies_to_headers(response.headers, Array.from(new_cookies.values()));
255
249
 
256
250
  return response;
257
251
  }
@@ -19,7 +19,7 @@ export function create_fetch({ event, options, state, route, prerender_default,
19
19
 
20
20
  const initial_cookies = cookie.parse(event.request.headers.get('cookie') || '');
21
21
 
22
- /** @type {import('set-cookie-parser').Cookie[]} */
22
+ /** @type {import('./types').Cookie[]} */
23
23
  const set_cookies = [];
24
24
 
25
25
  /**
@@ -31,8 +31,8 @@ export function create_fetch({ event, options, state, route, prerender_default,
31
31
  const new_cookies = {};
32
32
 
33
33
  for (const cookie of set_cookies) {
34
- if (!domain_matches(url.hostname, cookie.domain)) continue;
35
- if (!path_matches(url.pathname, cookie.path)) continue;
34
+ if (!domain_matches(url.hostname, cookie.options.domain)) continue;
35
+ if (!path_matches(url.pathname, cookie.options.path)) continue;
36
36
 
37
37
  new_cookies[cookie.name] = cookie.value;
38
38
  }
@@ -179,9 +179,11 @@ export function create_fetch({ event, options, state, route, prerender_default,
179
179
  const set_cookie = response.headers.get('set-cookie');
180
180
  if (set_cookie) {
181
181
  set_cookies.push(
182
- ...set_cookie_parser
183
- .splitCookiesString(set_cookie)
184
- .map((str) => set_cookie_parser.parseString(str))
182
+ ...set_cookie_parser.splitCookiesString(set_cookie).map((str) => {
183
+ const { name, value, ...options } = set_cookie_parser.parseString(str);
184
+ // options.sameSite is string, something more specific is required - type cast is safe
185
+ return /** @type{import('./types').Cookie} */ ({ name, value, options });
186
+ })
185
187
  );
186
188
  }
187
189
 
@@ -217,7 +217,7 @@ export async function render_page(event, route, page, options, state, resolve_op
217
217
  });
218
218
  }
219
219
 
220
- return redirect_response(err.status, err.location);
220
+ return redirect_response(err.status, err.location, cookies);
221
221
  }
222
222
 
223
223
  const status = err instanceof HttpError ? err.status : 500;
@@ -1,10 +1,10 @@
1
1
  import { devalue } from 'devalue';
2
2
  import { readable, writable } from 'svelte/store';
3
- import * as cookie from 'cookie';
4
3
  import { hash } from '../../hash.js';
5
4
  import { serialize_data } from './serialize_data.js';
6
5
  import { s } from '../../../utils/misc.js';
7
6
  import { Csp } from './csp.js';
7
+ import { add_cookies_to_headers } from '../cookie.js';
8
8
 
9
9
  // TODO rename this function/module
10
10
 
@@ -18,7 +18,7 @@ const updated = {
18
18
  * @param {{
19
19
  * branch: Array<import('./types').Loaded>;
20
20
  * fetched: Array<import('./types').Fetched>;
21
- * cookies: import('set-cookie-parser').Cookie[];
21
+ * cookies: import('./types').Cookie[];
22
22
  * options: import('types').SSROptions;
23
23
  * state: import('types').SSRState;
24
24
  * page_config: { ssr: boolean; csr: boolean };
@@ -328,11 +328,7 @@ export async function render_response({
328
328
  headers.set('content-security-policy-report-only', report_only_header);
329
329
  }
330
330
 
331
- for (const new_cookie of cookies) {
332
- const { name, value, ...options } = new_cookie;
333
- // @ts-expect-error
334
- headers.append('set-cookie', cookie.serialize(name, value, options));
335
- }
331
+ add_cookies_to_headers(headers, cookies);
336
332
 
337
333
  if (link_header_preloads.size) {
338
334
  headers.set('link', Array.from(link_header_preloads).join(', '));
@@ -1,5 +1,5 @@
1
+ import { CookieSerializeOptions } from 'cookie';
1
2
  import { SSRNode, CspDirectives } from 'types';
2
- import { HttpError } from '../../control.js';
3
3
 
4
4
  export interface Fetched {
5
5
  url: string;
@@ -33,3 +33,9 @@ export interface CspOpts {
33
33
  dev: boolean;
34
34
  prerender: boolean;
35
35
  }
36
+
37
+ export interface Cookie {
38
+ name: string;
39
+ value: string;
40
+ options: CookieSerializeOptions;
41
+ }
@@ -2,6 +2,7 @@ import { devalue } from 'devalue';
2
2
  import { DATA_SUFFIX } from '../../constants.js';
3
3
  import { negotiate } from '../../utils/http.js';
4
4
  import { HttpError } from '../control.js';
5
+ import { add_cookies_to_headers } from './cookie.js';
5
6
 
6
7
  /** @param {any} body */
7
8
  export function is_pojo(body) {
@@ -169,10 +170,13 @@ export function handle_error_and_jsonify(event, options, error) {
169
170
  /**
170
171
  * @param {number} status
171
172
  * @param {string} location
173
+ * @param {import('./page/types.js').Cookie[]} [cookies]
172
174
  */
173
- export function redirect_response(status, location) {
174
- return new Response(undefined, {
175
+ export function redirect_response(status, location, cookies = []) {
176
+ const response = new Response(undefined, {
175
177
  status,
176
178
  headers: { location }
177
179
  });
180
+ add_cookies_to_headers(response.headers, cookies);
181
+ return response;
178
182
  }
package/types/index.d.ts CHANGED
@@ -128,21 +128,21 @@ export interface Cookies {
128
128
  /**
129
129
  * Gets a cookie that was previously set with `cookies.set`, or from the request headers.
130
130
  */
131
- get(name: string, opts?: import('cookie').CookieParseOptions): string | undefined;
131
+ get(name: string, opts?: import('cookie').CookieParseOptions): string | void;
132
132
 
133
133
  /**
134
- * Sets a cookie. This will add a `set-cookie` header to the response, but also make
135
- * the cookie available via `cookies.get` during the current request.
134
+ * Sets a cookie. This will add a `set-cookie` header to the response, but also make the cookie available via `cookies.get` during the current request.
136
135
  *
137
- * The `httpOnly` and `secure` options are `true` by default, and must be explicitly
138
- * disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP
136
+ * The `httpOnly` and `secure` options are `true` by default, and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP. The `sameSite` option defaults to `lax`.
137
+ *
138
+ * 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.
139
139
  */
140
140
  set(name: string, value: string, opts?: import('cookie').CookieSerializeOptions): void;
141
141
 
142
142
  /**
143
143
  * Deletes a cookie by setting its value to an empty string and setting the expiry date in the past.
144
144
  */
145
- delete(name: string): void;
145
+ delete(name: string, opts?: import('cookie').CookieSerializeOptions): void;
146
146
  }
147
147
 
148
148
  export interface KitConfig {