cookie-es 0.5.0 → 1.1.0

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/README.md CHANGED
@@ -1,7 +1,13 @@
1
1
 
2
2
  # cookie-es
3
3
 
4
- [![bundle size](https://flat.badgen.net/bundlephobia/minzip/cookie-es)](https://bundlephobia.com/package/cookie-es)
4
+ <!-- automd:badges bundlejs -->
5
+
6
+ [![npm version](https://img.shields.io/npm/v/cookie-es)](https://npmjs.com/package/cookie-es)
7
+ [![npm downloads](https://img.shields.io/npm/dm/cookie-es)](https://npmjs.com/package/cookie-es)
8
+ [![bundle size](https://img.shields.io/bundlejs/size/cookie-es)](https://bundlejs.com/?q=cookie-es)
9
+
10
+ <!-- /automd -->
5
11
 
6
12
  ESM build of [cookie](https://www.npmjs.com/package/cookie) with bundled types.
7
13
 
@@ -9,24 +15,53 @@ ESM build of [cookie](https://www.npmjs.com/package/cookie) with bundled types.
9
15
 
10
16
  Install:
11
17
 
18
+ <!-- automd:pm-install -->
19
+
12
20
  ```sh
21
+ # ✨ Auto-detect
22
+ npx nypm install cookie-es
23
+
13
24
  # npm
14
- npm i cookie-es
25
+ npm install cookie-es
15
26
 
16
27
  # yarn
17
28
  yarn add cookie-es
29
+
30
+ # pnpm
31
+ pnpm install cookie-es
32
+
33
+ # bun
34
+ bun install cookie-es
18
35
  ```
19
36
 
37
+ <!-- /automd-->
38
+
20
39
  Import:
21
40
 
41
+
42
+ <!-- automd:jsimport cdn cjs src=./src/index.ts -->
43
+
44
+ **ESM** (Node.js, Bun)
45
+
22
46
  ```js
23
- // ESM
24
- import { parse, serialize } from 'cookie-es'
47
+ import { parse, serialize } from "cookie-es";
48
+ ```
49
+
50
+ **CommonJS** (Legacy Node.js)
25
51
 
26
- // CommonJS
27
- const { parse, serialize } = require('cookie-es')
52
+ ```js
53
+ const { parse, serialize } = require("cookie-es");
28
54
  ```
29
55
 
56
+ **CDN** (Deno, Bun and Browsers)
57
+
58
+ ```js
59
+ import { parse, serialize } from "https://esm.sh/cookie-es";
60
+ ```
61
+
62
+ <!-- /automd -->
63
+
64
+
30
65
  ## License
31
66
 
32
67
  [MIT](./LICENSE)
package/dist/index.cjs CHANGED
@@ -1,53 +1,55 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- const decode = decodeURIComponent;
6
- const encode = encodeURIComponent;
7
- const pairSplitRegExp = /; */;
8
- const fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
3
+ const fieldContentRegExp = /^[\u0009\u0020-\u007E\u0080-\u00FF]+$/;
9
4
  function parse(str, options) {
10
5
  if (typeof str !== "string") {
11
6
  throw new TypeError("argument str must be a string");
12
7
  }
13
- let obj = {};
14
- let opt = options || {};
15
- let pairs = str.split(pairSplitRegExp);
16
- let dec = opt.decode || decode;
17
- for (let i = 0; i < pairs.length; i++) {
18
- let pair = pairs[i];
19
- let eq_idx = pair.indexOf("=");
20
- if (eq_idx < 0) {
21
- continue;
8
+ const obj = {};
9
+ const opt = options || {};
10
+ const dec = opt.decode || decode;
11
+ let index = 0;
12
+ while (index < str.length) {
13
+ const eqIdx = str.indexOf("=", index);
14
+ if (eqIdx === -1) {
15
+ break;
22
16
  }
23
- let key = pair.substr(0, eq_idx).trim();
24
- let val = pair.substr(++eq_idx, pair.length).trim();
25
- if (val[0] == '"') {
26
- val = val.slice(1, -1);
17
+ let endIdx = str.indexOf(";", index);
18
+ if (endIdx === -1) {
19
+ endIdx = str.length;
20
+ } else if (endIdx < eqIdx) {
21
+ index = str.lastIndexOf(";", eqIdx - 1) + 1;
22
+ continue;
27
23
  }
28
- if (obj[key] == void 0) {
24
+ const key = str.slice(index, eqIdx).trim();
25
+ if (void 0 === obj[key]) {
26
+ let val = str.slice(eqIdx + 1, endIdx).trim();
27
+ if (val.codePointAt(0) === 34) {
28
+ val = val.slice(1, -1);
29
+ }
29
30
  obj[key] = tryDecode(val, dec);
30
31
  }
32
+ index = endIdx + 1;
31
33
  }
32
34
  return obj;
33
35
  }
34
36
  function serialize(name, value, options) {
35
- let opt = options || {};
36
- let enc = opt.encode || encode;
37
+ const opt = options || {};
38
+ const enc = opt.encode || encode;
37
39
  if (typeof enc !== "function") {
38
40
  throw new TypeError("option encode is invalid");
39
41
  }
40
42
  if (!fieldContentRegExp.test(name)) {
41
43
  throw new TypeError("argument name is invalid");
42
44
  }
43
- let encodedValue = enc(value);
45
+ const encodedValue = enc(value);
44
46
  if (encodedValue && !fieldContentRegExp.test(encodedValue)) {
45
47
  throw new TypeError("argument val is invalid");
46
48
  }
47
49
  let str = name + "=" + encodedValue;
48
- if (opt.maxAge != null) {
49
- let maxAge = opt.maxAge - 0;
50
- if (isNaN(maxAge) || !isFinite(maxAge)) {
50
+ if (void 0 !== opt.maxAge && opt.maxAge !== null) {
51
+ const maxAge = opt.maxAge - 0;
52
+ if (Number.isNaN(maxAge) || !Number.isFinite(maxAge)) {
51
53
  throw new TypeError("option maxAge is invalid");
52
54
  }
53
55
  str += "; Max-Age=" + Math.floor(maxAge);
@@ -65,7 +67,7 @@ function serialize(name, value, options) {
65
67
  str += "; Path=" + opt.path;
66
68
  }
67
69
  if (opt.expires) {
68
- if (typeof opt.expires.toUTCString !== "function") {
70
+ if (!isDate(opt.expires) || Number.isNaN(opt.expires.valueOf())) {
69
71
  throw new TypeError("option expires is invalid");
70
72
  }
71
73
  str += "; Expires=" + opt.expires.toUTCString();
@@ -76,34 +78,71 @@ function serialize(name, value, options) {
76
78
  if (opt.secure) {
77
79
  str += "; Secure";
78
80
  }
81
+ if (opt.priority) {
82
+ const priority = typeof opt.priority === "string" ? opt.priority.toLowerCase() : opt.priority;
83
+ switch (priority) {
84
+ case "low": {
85
+ str += "; Priority=Low";
86
+ break;
87
+ }
88
+ case "medium": {
89
+ str += "; Priority=Medium";
90
+ break;
91
+ }
92
+ case "high": {
93
+ str += "; Priority=High";
94
+ break;
95
+ }
96
+ default: {
97
+ throw new TypeError("option priority is invalid");
98
+ }
99
+ }
100
+ }
79
101
  if (opt.sameSite) {
80
- let sameSite = typeof opt.sameSite === "string" ? opt.sameSite.toLowerCase() : opt.sameSite;
102
+ const sameSite = typeof opt.sameSite === "string" ? opt.sameSite.toLowerCase() : opt.sameSite;
81
103
  switch (sameSite) {
82
- case true:
104
+ case true: {
83
105
  str += "; SameSite=Strict";
84
106
  break;
85
- case "lax":
107
+ }
108
+ case "lax": {
86
109
  str += "; SameSite=Lax";
87
110
  break;
88
- case "strict":
111
+ }
112
+ case "strict": {
89
113
  str += "; SameSite=Strict";
90
114
  break;
91
- case "none":
115
+ }
116
+ case "none": {
92
117
  str += "; SameSite=None";
93
118
  break;
94
- default:
119
+ }
120
+ default: {
95
121
  throw new TypeError("option sameSite is invalid");
122
+ }
96
123
  }
97
124
  }
125
+ if (opt.partitioned) {
126
+ str += "; Partitioned";
127
+ }
98
128
  return str;
99
129
  }
130
+ function isDate(val) {
131
+ return Object.prototype.toString.call(val) === "[object Date]" || val instanceof Date;
132
+ }
100
133
  function tryDecode(str, decode2) {
101
134
  try {
102
135
  return decode2(str);
103
- } catch (e) {
136
+ } catch {
104
137
  return str;
105
138
  }
106
139
  }
140
+ function decode(str) {
141
+ return str.includes("%") ? decodeURIComponent(str) : str;
142
+ }
143
+ function encode(val) {
144
+ return encodeURIComponent(val);
145
+ }
107
146
 
108
147
  exports.parse = parse;
109
148
  exports.serialize = serialize;
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Basic HTTP cookie parser and serializer for HTTP servers.
3
+ */
4
+ /**
5
+ * Additional serialization options
6
+ */
7
+ interface CookieSerializeOptions {
8
+ /**
9
+ * Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.3|Domain Set-Cookie attribute}. By default, no
10
+ * domain is set, and most clients will consider the cookie to apply to only
11
+ * the current domain.
12
+ */
13
+ domain?: string | undefined;
14
+ /**
15
+ * Specifies a function that will be used to encode a cookie's value. Since
16
+ * value of a cookie has a limited character set (and must be a simple
17
+ * string), this function can be used to encode a value into a string suited
18
+ * for a cookie's value.
19
+ *
20
+ * The default function is the global `encodeURIComponent`, which will
21
+ * encode a JavaScript string into UTF-8 byte sequences and then URL-encode
22
+ * any that fall outside of the cookie range.
23
+ */
24
+ encode?(value: string): string;
25
+ /**
26
+ * Specifies the `Date` object to be the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.1|`Expires` `Set-Cookie` attribute}. By default,
27
+ * no expiration is set, and most clients will consider this a "non-persistent cookie" and will delete
28
+ * it on a condition like exiting a web browser application.
29
+ *
30
+ * *Note* the {@link https://tools.ietf.org/html/rfc6265#section-5.3|cookie storage model specification}
31
+ * states that if both `expires` and `maxAge` are set, then `maxAge` takes precedence, but it is
32
+ * possible not all clients by obey this, so if both are set, they should
33
+ * point to the same date and time.
34
+ */
35
+ expires?: Date | undefined;
36
+ /**
37
+ * Specifies the boolean value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.6|`HttpOnly` `Set-Cookie` attribute}.
38
+ * When truthy, the `HttpOnly` attribute is set, otherwise it is not. By
39
+ * default, the `HttpOnly` attribute is not set.
40
+ *
41
+ * *Note* be careful when setting this to true, as compliant clients will
42
+ * not allow client-side JavaScript to see the cookie in `document.cookie`.
43
+ */
44
+ httpOnly?: boolean | undefined;
45
+ /**
46
+ * Specifies the number (in seconds) to be the value for the `Max-Age`
47
+ * `Set-Cookie` attribute. The given number will be converted to an integer
48
+ * by rounding down. By default, no maximum age is set.
49
+ *
50
+ * *Note* the {@link https://tools.ietf.org/html/rfc6265#section-5.3|cookie storage model specification}
51
+ * states that if both `expires` and `maxAge` are set, then `maxAge` takes precedence, but it is
52
+ * possible not all clients by obey this, so if both are set, they should
53
+ * point to the same date and time.
54
+ */
55
+ maxAge?: number | undefined;
56
+ /**
57
+ * Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4|`Path` `Set-Cookie` attribute}.
58
+ * By default, the path is considered the "default path".
59
+ */
60
+ path?: string | undefined;
61
+ /**
62
+ * Specifies the `string` to be the value for the [`Priority` `Set-Cookie` attribute][rfc-west-cookie-priority-00-4.1].
63
+ *
64
+ * - `'low'` will set the `Priority` attribute to `Low`.
65
+ * - `'medium'` will set the `Priority` attribute to `Medium`, the default priority when not set.
66
+ * - `'high'` will set the `Priority` attribute to `High`.
67
+ *
68
+ * More information about the different priority levels can be found in
69
+ * [the specification][rfc-west-cookie-priority-00-4.1].
70
+ *
71
+ * **note** This is an attribute that has not yet been fully standardized, and may change in the future.
72
+ * This also means many clients may ignore this attribute until they understand it.
73
+ */
74
+ priority?: "low" | "medium" | "high" | undefined;
75
+ /**
76
+ * Specifies the boolean or string to be the value for the {@link https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7|`SameSite` `Set-Cookie` attribute}.
77
+ *
78
+ * - `true` will set the `SameSite` attribute to `Strict` for strict same
79
+ * site enforcement.
80
+ * - `false` will not set the `SameSite` attribute.
81
+ * - `'lax'` will set the `SameSite` attribute to Lax for lax same site
82
+ * enforcement.
83
+ * - `'strict'` will set the `SameSite` attribute to Strict for strict same
84
+ * site enforcement.
85
+ * - `'none'` will set the SameSite attribute to None for an explicit
86
+ * cross-site cookie.
87
+ *
88
+ * More information about the different enforcement levels can be found in {@link https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7|the specification}.
89
+ *
90
+ * *note* This is an attribute that has not yet been fully standardized, and may change in the future. This also means many clients may ignore this attribute until they understand it.
91
+ */
92
+ sameSite?: true | false | "lax" | "strict" | "none" | undefined;
93
+ /**
94
+ * Specifies the boolean value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.5|`Secure` `Set-Cookie` attribute}. When truthy, the
95
+ * `Secure` attribute is set, otherwise it is not. By default, the `Secure` attribute is not set.
96
+ *
97
+ * *Note* be careful when setting this to `true`, as compliant clients will
98
+ * not send the cookie back to the server in the future if the browser does
99
+ * not have an HTTPS connection.
100
+ */
101
+ secure?: boolean | undefined;
102
+ /**
103
+ * Specifies the `boolean` value for the [`Partitioned` `Set-Cookie`](rfc-cutler-httpbis-partitioned-cookies)
104
+ * attribute. When truthy, the `Partitioned` attribute is set, otherwise it is not. By default, the
105
+ * `Partitioned` attribute is not set.
106
+ *
107
+ * **note** This is an attribute that has not yet been fully standardized, and may change in the future.
108
+ * This also means many clients may ignore this attribute until they understand it.
109
+ *
110
+ * More information about can be found in [the proposal](https://github.com/privacycg/CHIPS)
111
+ */
112
+ partitioned?: boolean;
113
+ }
114
+ /**
115
+ * Additional parsing options
116
+ */
117
+ interface CookieParseOptions {
118
+ /**
119
+ * Specifies a function that will be used to decode a cookie's value. Since
120
+ * the value of a cookie has a limited character set (and must be a simple
121
+ * string), this function can be used to decode a previously-encoded cookie
122
+ * value into a JavaScript string or other object.
123
+ *
124
+ * The default function is the global `decodeURIComponent`, which will decode
125
+ * any URL-encoded sequences into their byte representations.
126
+ *
127
+ * *Note* if an error is thrown from this function, the original, non-decoded
128
+ * cookie value will be returned as the cookie's value.
129
+ */
130
+ decode?(value: string): string;
131
+ }
132
+
133
+ /**
134
+ * Parse an HTTP Cookie header string and returning an object of all cookie
135
+ * name-value pairs.
136
+ *
137
+ * @param str the string representing a `Cookie` header value
138
+ * @param [options] object containing parsing options
139
+ */
140
+ declare function parse(str: string, options?: CookieParseOptions): Record<string, string>;
141
+ /**
142
+ * Serialize a cookie name-value pair into a `Set-Cookie` header string.
143
+ *
144
+ * @param name the name for the cookie
145
+ * @param value value to set the cookie to
146
+ * @param [options] object containing serialization options
147
+ * @throws {TypeError} when `maxAge` options is invalid
148
+ */
149
+ declare function serialize(name: string, value: string, options?: CookieSerializeOptions): string;
150
+
151
+ export { type CookieParseOptions, type CookieSerializeOptions, parse, serialize };
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Basic HTTP cookie parser and serializer for HTTP servers.
3
+ */
4
+ /**
5
+ * Additional serialization options
6
+ */
7
+ interface CookieSerializeOptions {
8
+ /**
9
+ * Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.3|Domain Set-Cookie attribute}. By default, no
10
+ * domain is set, and most clients will consider the cookie to apply to only
11
+ * the current domain.
12
+ */
13
+ domain?: string | undefined;
14
+ /**
15
+ * Specifies a function that will be used to encode a cookie's value. Since
16
+ * value of a cookie has a limited character set (and must be a simple
17
+ * string), this function can be used to encode a value into a string suited
18
+ * for a cookie's value.
19
+ *
20
+ * The default function is the global `encodeURIComponent`, which will
21
+ * encode a JavaScript string into UTF-8 byte sequences and then URL-encode
22
+ * any that fall outside of the cookie range.
23
+ */
24
+ encode?(value: string): string;
25
+ /**
26
+ * Specifies the `Date` object to be the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.1|`Expires` `Set-Cookie` attribute}. By default,
27
+ * no expiration is set, and most clients will consider this a "non-persistent cookie" and will delete
28
+ * it on a condition like exiting a web browser application.
29
+ *
30
+ * *Note* the {@link https://tools.ietf.org/html/rfc6265#section-5.3|cookie storage model specification}
31
+ * states that if both `expires` and `maxAge` are set, then `maxAge` takes precedence, but it is
32
+ * possible not all clients by obey this, so if both are set, they should
33
+ * point to the same date and time.
34
+ */
35
+ expires?: Date | undefined;
36
+ /**
37
+ * Specifies the boolean value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.6|`HttpOnly` `Set-Cookie` attribute}.
38
+ * When truthy, the `HttpOnly` attribute is set, otherwise it is not. By
39
+ * default, the `HttpOnly` attribute is not set.
40
+ *
41
+ * *Note* be careful when setting this to true, as compliant clients will
42
+ * not allow client-side JavaScript to see the cookie in `document.cookie`.
43
+ */
44
+ httpOnly?: boolean | undefined;
45
+ /**
46
+ * Specifies the number (in seconds) to be the value for the `Max-Age`
47
+ * `Set-Cookie` attribute. The given number will be converted to an integer
48
+ * by rounding down. By default, no maximum age is set.
49
+ *
50
+ * *Note* the {@link https://tools.ietf.org/html/rfc6265#section-5.3|cookie storage model specification}
51
+ * states that if both `expires` and `maxAge` are set, then `maxAge` takes precedence, but it is
52
+ * possible not all clients by obey this, so if both are set, they should
53
+ * point to the same date and time.
54
+ */
55
+ maxAge?: number | undefined;
56
+ /**
57
+ * Specifies the value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.4|`Path` `Set-Cookie` attribute}.
58
+ * By default, the path is considered the "default path".
59
+ */
60
+ path?: string | undefined;
61
+ /**
62
+ * Specifies the `string` to be the value for the [`Priority` `Set-Cookie` attribute][rfc-west-cookie-priority-00-4.1].
63
+ *
64
+ * - `'low'` will set the `Priority` attribute to `Low`.
65
+ * - `'medium'` will set the `Priority` attribute to `Medium`, the default priority when not set.
66
+ * - `'high'` will set the `Priority` attribute to `High`.
67
+ *
68
+ * More information about the different priority levels can be found in
69
+ * [the specification][rfc-west-cookie-priority-00-4.1].
70
+ *
71
+ * **note** This is an attribute that has not yet been fully standardized, and may change in the future.
72
+ * This also means many clients may ignore this attribute until they understand it.
73
+ */
74
+ priority?: "low" | "medium" | "high" | undefined;
75
+ /**
76
+ * Specifies the boolean or string to be the value for the {@link https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7|`SameSite` `Set-Cookie` attribute}.
77
+ *
78
+ * - `true` will set the `SameSite` attribute to `Strict` for strict same
79
+ * site enforcement.
80
+ * - `false` will not set the `SameSite` attribute.
81
+ * - `'lax'` will set the `SameSite` attribute to Lax for lax same site
82
+ * enforcement.
83
+ * - `'strict'` will set the `SameSite` attribute to Strict for strict same
84
+ * site enforcement.
85
+ * - `'none'` will set the SameSite attribute to None for an explicit
86
+ * cross-site cookie.
87
+ *
88
+ * More information about the different enforcement levels can be found in {@link https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7|the specification}.
89
+ *
90
+ * *note* This is an attribute that has not yet been fully standardized, and may change in the future. This also means many clients may ignore this attribute until they understand it.
91
+ */
92
+ sameSite?: true | false | "lax" | "strict" | "none" | undefined;
93
+ /**
94
+ * Specifies the boolean value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.5|`Secure` `Set-Cookie` attribute}. When truthy, the
95
+ * `Secure` attribute is set, otherwise it is not. By default, the `Secure` attribute is not set.
96
+ *
97
+ * *Note* be careful when setting this to `true`, as compliant clients will
98
+ * not send the cookie back to the server in the future if the browser does
99
+ * not have an HTTPS connection.
100
+ */
101
+ secure?: boolean | undefined;
102
+ /**
103
+ * Specifies the `boolean` value for the [`Partitioned` `Set-Cookie`](rfc-cutler-httpbis-partitioned-cookies)
104
+ * attribute. When truthy, the `Partitioned` attribute is set, otherwise it is not. By default, the
105
+ * `Partitioned` attribute is not set.
106
+ *
107
+ * **note** This is an attribute that has not yet been fully standardized, and may change in the future.
108
+ * This also means many clients may ignore this attribute until they understand it.
109
+ *
110
+ * More information about can be found in [the proposal](https://github.com/privacycg/CHIPS)
111
+ */
112
+ partitioned?: boolean;
113
+ }
114
+ /**
115
+ * Additional parsing options
116
+ */
117
+ interface CookieParseOptions {
118
+ /**
119
+ * Specifies a function that will be used to decode a cookie's value. Since
120
+ * the value of a cookie has a limited character set (and must be a simple
121
+ * string), this function can be used to decode a previously-encoded cookie
122
+ * value into a JavaScript string or other object.
123
+ *
124
+ * The default function is the global `decodeURIComponent`, which will decode
125
+ * any URL-encoded sequences into their byte representations.
126
+ *
127
+ * *Note* if an error is thrown from this function, the original, non-decoded
128
+ * cookie value will be returned as the cookie's value.
129
+ */
130
+ decode?(value: string): string;
131
+ }
132
+
133
+ /**
134
+ * Parse an HTTP Cookie header string and returning an object of all cookie
135
+ * name-value pairs.
136
+ *
137
+ * @param str the string representing a `Cookie` header value
138
+ * @param [options] object containing parsing options
139
+ */
140
+ declare function parse(str: string, options?: CookieParseOptions): Record<string, string>;
141
+ /**
142
+ * Serialize a cookie name-value pair into a `Set-Cookie` header string.
143
+ *
144
+ * @param name the name for the cookie
145
+ * @param value value to set the cookie to
146
+ * @param [options] object containing serialization options
147
+ * @throws {TypeError} when `maxAge` options is invalid
148
+ */
149
+ declare function serialize(name: string, value: string, options?: CookieSerializeOptions): string;
150
+
151
+ export { type CookieParseOptions, type CookieSerializeOptions, parse, serialize };
package/dist/index.d.ts CHANGED
@@ -58,6 +58,20 @@ interface CookieSerializeOptions {
58
58
  * By default, the path is considered the "default path".
59
59
  */
60
60
  path?: string | undefined;
61
+ /**
62
+ * Specifies the `string` to be the value for the [`Priority` `Set-Cookie` attribute][rfc-west-cookie-priority-00-4.1].
63
+ *
64
+ * - `'low'` will set the `Priority` attribute to `Low`.
65
+ * - `'medium'` will set the `Priority` attribute to `Medium`, the default priority when not set.
66
+ * - `'high'` will set the `Priority` attribute to `High`.
67
+ *
68
+ * More information about the different priority levels can be found in
69
+ * [the specification][rfc-west-cookie-priority-00-4.1].
70
+ *
71
+ * **note** This is an attribute that has not yet been fully standardized, and may change in the future.
72
+ * This also means many clients may ignore this attribute until they understand it.
73
+ */
74
+ priority?: "low" | "medium" | "high" | undefined;
61
75
  /**
62
76
  * Specifies the boolean or string to be the value for the {@link https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7|`SameSite` `Set-Cookie` attribute}.
63
77
  *
@@ -75,7 +89,7 @@ interface CookieSerializeOptions {
75
89
  *
76
90
  * *note* This is an attribute that has not yet been fully standardized, and may change in the future. This also means many clients may ignore this attribute until they understand it.
77
91
  */
78
- sameSite?: true | false | 'lax' | 'strict' | 'none' | undefined;
92
+ sameSite?: true | false | "lax" | "strict" | "none" | undefined;
79
93
  /**
80
94
  * Specifies the boolean value for the {@link https://tools.ietf.org/html/rfc6265#section-5.2.5|`Secure` `Set-Cookie` attribute}. When truthy, the
81
95
  * `Secure` attribute is set, otherwise it is not. By default, the `Secure` attribute is not set.
@@ -85,10 +99,21 @@ interface CookieSerializeOptions {
85
99
  * not have an HTTPS connection.
86
100
  */
87
101
  secure?: boolean | undefined;
102
+ /**
103
+ * Specifies the `boolean` value for the [`Partitioned` `Set-Cookie`](rfc-cutler-httpbis-partitioned-cookies)
104
+ * attribute. When truthy, the `Partitioned` attribute is set, otherwise it is not. By default, the
105
+ * `Partitioned` attribute is not set.
106
+ *
107
+ * **note** This is an attribute that has not yet been fully standardized, and may change in the future.
108
+ * This also means many clients may ignore this attribute until they understand it.
109
+ *
110
+ * More information about can be found in [the proposal](https://github.com/privacycg/CHIPS)
111
+ */
112
+ partitioned?: boolean;
88
113
  }
89
114
  /**
90
- * Additional parsing options
91
- */
115
+ * Additional parsing options
116
+ */
92
117
  interface CookieParseOptions {
93
118
  /**
94
119
  * Specifies a function that will be used to decode a cookie's value. Since
@@ -106,21 +131,21 @@ interface CookieParseOptions {
106
131
  }
107
132
 
108
133
  /**
109
- * Parse an HTTP Cookie header string and returning an object of all cookie
110
- * name-value pairs.
111
- *
112
- * @param str the string representing a `Cookie` header value
113
- * @param [options] object containing parsing options
114
- */
115
- declare function parse(str: string, options?: CookieParseOptions): {};
134
+ * Parse an HTTP Cookie header string and returning an object of all cookie
135
+ * name-value pairs.
136
+ *
137
+ * @param str the string representing a `Cookie` header value
138
+ * @param [options] object containing parsing options
139
+ */
140
+ declare function parse(str: string, options?: CookieParseOptions): Record<string, string>;
116
141
  /**
117
- * Serialize a cookie name-value pair into a `Set-Cookie` header string.
118
- *
119
- * @param name the name for the cookie
120
- * @param value value to set the cookie to
121
- * @param [options] object containing serialization options
122
- * @throws {TypeError} when `maxAge` options is invalid
123
- */
142
+ * Serialize a cookie name-value pair into a `Set-Cookie` header string.
143
+ *
144
+ * @param name the name for the cookie
145
+ * @param value value to set the cookie to
146
+ * @param [options] object containing serialization options
147
+ * @throws {TypeError} when `maxAge` options is invalid
148
+ */
124
149
  declare function serialize(name: string, value: string, options?: CookieSerializeOptions): string;
125
150
 
126
- export { CookieParseOptions, CookieSerializeOptions, parse, serialize };
151
+ export { type CookieParseOptions, type CookieSerializeOptions, parse, serialize };
package/dist/index.mjs CHANGED
@@ -1,49 +1,53 @@
1
- const decode = decodeURIComponent;
2
- const encode = encodeURIComponent;
3
- const pairSplitRegExp = /; */;
4
- const fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
1
+ const fieldContentRegExp = /^[\u0009\u0020-\u007E\u0080-\u00FF]+$/;
5
2
  function parse(str, options) {
6
3
  if (typeof str !== "string") {
7
4
  throw new TypeError("argument str must be a string");
8
5
  }
9
- let obj = {};
10
- let opt = options || {};
11
- let pairs = str.split(pairSplitRegExp);
12
- let dec = opt.decode || decode;
13
- for (let i = 0; i < pairs.length; i++) {
14
- let pair = pairs[i];
15
- let eq_idx = pair.indexOf("=");
16
- if (eq_idx < 0) {
17
- continue;
6
+ const obj = {};
7
+ const opt = options || {};
8
+ const dec = opt.decode || decode;
9
+ let index = 0;
10
+ while (index < str.length) {
11
+ const eqIdx = str.indexOf("=", index);
12
+ if (eqIdx === -1) {
13
+ break;
18
14
  }
19
- let key = pair.substr(0, eq_idx).trim();
20
- let val = pair.substr(++eq_idx, pair.length).trim();
21
- if (val[0] == '"') {
22
- val = val.slice(1, -1);
15
+ let endIdx = str.indexOf(";", index);
16
+ if (endIdx === -1) {
17
+ endIdx = str.length;
18
+ } else if (endIdx < eqIdx) {
19
+ index = str.lastIndexOf(";", eqIdx - 1) + 1;
20
+ continue;
23
21
  }
24
- if (obj[key] == void 0) {
22
+ const key = str.slice(index, eqIdx).trim();
23
+ if (void 0 === obj[key]) {
24
+ let val = str.slice(eqIdx + 1, endIdx).trim();
25
+ if (val.codePointAt(0) === 34) {
26
+ val = val.slice(1, -1);
27
+ }
25
28
  obj[key] = tryDecode(val, dec);
26
29
  }
30
+ index = endIdx + 1;
27
31
  }
28
32
  return obj;
29
33
  }
30
34
  function serialize(name, value, options) {
31
- let opt = options || {};
32
- let enc = opt.encode || encode;
35
+ const opt = options || {};
36
+ const enc = opt.encode || encode;
33
37
  if (typeof enc !== "function") {
34
38
  throw new TypeError("option encode is invalid");
35
39
  }
36
40
  if (!fieldContentRegExp.test(name)) {
37
41
  throw new TypeError("argument name is invalid");
38
42
  }
39
- let encodedValue = enc(value);
43
+ const encodedValue = enc(value);
40
44
  if (encodedValue && !fieldContentRegExp.test(encodedValue)) {
41
45
  throw new TypeError("argument val is invalid");
42
46
  }
43
47
  let str = name + "=" + encodedValue;
44
- if (opt.maxAge != null) {
45
- let maxAge = opt.maxAge - 0;
46
- if (isNaN(maxAge) || !isFinite(maxAge)) {
48
+ if (void 0 !== opt.maxAge && opt.maxAge !== null) {
49
+ const maxAge = opt.maxAge - 0;
50
+ if (Number.isNaN(maxAge) || !Number.isFinite(maxAge)) {
47
51
  throw new TypeError("option maxAge is invalid");
48
52
  }
49
53
  str += "; Max-Age=" + Math.floor(maxAge);
@@ -61,7 +65,7 @@ function serialize(name, value, options) {
61
65
  str += "; Path=" + opt.path;
62
66
  }
63
67
  if (opt.expires) {
64
- if (typeof opt.expires.toUTCString !== "function") {
68
+ if (!isDate(opt.expires) || Number.isNaN(opt.expires.valueOf())) {
65
69
  throw new TypeError("option expires is invalid");
66
70
  }
67
71
  str += "; Expires=" + opt.expires.toUTCString();
@@ -72,33 +76,70 @@ function serialize(name, value, options) {
72
76
  if (opt.secure) {
73
77
  str += "; Secure";
74
78
  }
79
+ if (opt.priority) {
80
+ const priority = typeof opt.priority === "string" ? opt.priority.toLowerCase() : opt.priority;
81
+ switch (priority) {
82
+ case "low": {
83
+ str += "; Priority=Low";
84
+ break;
85
+ }
86
+ case "medium": {
87
+ str += "; Priority=Medium";
88
+ break;
89
+ }
90
+ case "high": {
91
+ str += "; Priority=High";
92
+ break;
93
+ }
94
+ default: {
95
+ throw new TypeError("option priority is invalid");
96
+ }
97
+ }
98
+ }
75
99
  if (opt.sameSite) {
76
- let sameSite = typeof opt.sameSite === "string" ? opt.sameSite.toLowerCase() : opt.sameSite;
100
+ const sameSite = typeof opt.sameSite === "string" ? opt.sameSite.toLowerCase() : opt.sameSite;
77
101
  switch (sameSite) {
78
- case true:
102
+ case true: {
79
103
  str += "; SameSite=Strict";
80
104
  break;
81
- case "lax":
105
+ }
106
+ case "lax": {
82
107
  str += "; SameSite=Lax";
83
108
  break;
84
- case "strict":
109
+ }
110
+ case "strict": {
85
111
  str += "; SameSite=Strict";
86
112
  break;
87
- case "none":
113
+ }
114
+ case "none": {
88
115
  str += "; SameSite=None";
89
116
  break;
90
- default:
117
+ }
118
+ default: {
91
119
  throw new TypeError("option sameSite is invalid");
120
+ }
92
121
  }
93
122
  }
123
+ if (opt.partitioned) {
124
+ str += "; Partitioned";
125
+ }
94
126
  return str;
95
127
  }
128
+ function isDate(val) {
129
+ return Object.prototype.toString.call(val) === "[object Date]" || val instanceof Date;
130
+ }
96
131
  function tryDecode(str, decode2) {
97
132
  try {
98
133
  return decode2(str);
99
- } catch (e) {
134
+ } catch {
100
135
  return str;
101
136
  }
102
137
  }
138
+ function decode(str) {
139
+ return str.includes("%") ? decodeURIComponent(str) : str;
140
+ }
141
+ function encode(val) {
142
+ return encodeURIComponent(val);
143
+ }
103
144
 
104
145
  export { parse, serialize };
package/package.json CHANGED
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "name": "cookie-es",
3
- "version": "0.5.0",
3
+ "version": "1.1.0",
4
4
  "repository": "unjs/cookie-es",
5
5
  "license": "MIT",
6
6
  "sideEffects": false,
7
7
  "type": "module",
8
8
  "exports": {
9
- "require": "./dist/index.cjs",
10
- "import": "./dist/index.mjs"
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "require": "./dist/index.cjs",
12
+ "import": "./dist/index.mjs"
13
+ }
11
14
  },
12
15
  "main": "./dist/index.cjs",
13
16
  "module": "./dist/index.mjs",
@@ -17,11 +20,22 @@
17
20
  ],
18
21
  "scripts": {
19
22
  "build": "unbuild",
20
- "release": "yarn build && standard-version && git push --follow-tags && npm publish",
21
- "test": "node test/index.mjs"
23
+ "dev": "vitest",
24
+ "lint": "eslint --cache --ext .ts,.js,.mjs,.cjs . && prettier -c src test",
25
+ "lint:fix": "automd && eslint --cache --ext .ts,.js,.mjs,.cjs . --fix && prettier -c src test -w",
26
+ "release": "pnpm test && pnpm build && changelogen --release --push && npm publish",
27
+ "test": "pnpm lint && vitest run --coverage"
22
28
  },
23
29
  "devDependencies": {
24
- "standard-version": "latest",
25
- "unbuild": "latest"
26
- }
27
- }
30
+ "@vitest/coverage-v8": "^1.4.0",
31
+ "automd": "^0.3.7",
32
+ "changelogen": "^0.5.5",
33
+ "eslint": "^8.57.0",
34
+ "eslint-config-unjs": "^0.2.1",
35
+ "prettier": "^3.2.5",
36
+ "typescript": "^5.4.3",
37
+ "unbuild": "^2.0.0",
38
+ "vitest": "^1.4.0"
39
+ },
40
+ "packageManager": "pnpm@8.15.5"
41
+ }