cookie-es 0.5.0 → 1.0.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/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,8 +78,24 @@ 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
+ case "medium":
88
+ str += "; Priority=Medium";
89
+ break;
90
+ case "high":
91
+ str += "; Priority=High";
92
+ break;
93
+ default:
94
+ throw new TypeError("option priority is invalid");
95
+ }
96
+ }
79
97
  if (opt.sameSite) {
80
- let sameSite = typeof opt.sameSite === "string" ? opt.sameSite.toLowerCase() : opt.sameSite;
98
+ const sameSite = typeof opt.sameSite === "string" ? opt.sameSite.toLowerCase() : opt.sameSite;
81
99
  switch (sameSite) {
82
100
  case true:
83
101
  str += "; SameSite=Strict";
@@ -97,13 +115,22 @@ function serialize(name, value, options) {
97
115
  }
98
116
  return str;
99
117
  }
118
+ function isDate(val) {
119
+ return Object.prototype.toString.call(val) === "[object Date]" || val instanceof Date;
120
+ }
100
121
  function tryDecode(str, decode2) {
101
122
  try {
102
123
  return decode2(str);
103
- } catch (e) {
124
+ } catch {
104
125
  return str;
105
126
  }
106
127
  }
128
+ function decode(str) {
129
+ return str.includes("%") ? decodeURIComponent(str) : str;
130
+ }
131
+ function encode(val) {
132
+ return encodeURIComponent(val);
133
+ }
107
134
 
108
135
  exports.parse = parse;
109
136
  exports.serialize = 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.
@@ -87,8 +101,8 @@ interface CookieSerializeOptions {
87
101
  secure?: boolean | undefined;
88
102
  }
89
103
  /**
90
- * Additional parsing options
91
- */
104
+ * Additional parsing options
105
+ */
92
106
  interface CookieParseOptions {
93
107
  /**
94
108
  * Specifies a function that will be used to decode a cookie's value. Since
@@ -106,21 +120,21 @@ interface CookieParseOptions {
106
120
  }
107
121
 
108
122
  /**
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): {};
123
+ * Parse an HTTP Cookie header string and returning an object of all cookie
124
+ * name-value pairs.
125
+ *
126
+ * @param str the string representing a `Cookie` header value
127
+ * @param [options] object containing parsing options
128
+ */
129
+ declare function parse(str: string, options?: CookieParseOptions): Record<string, string>;
116
130
  /**
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
- */
131
+ * Serialize a cookie name-value pair into a `Set-Cookie` header string.
132
+ *
133
+ * @param name the name for the cookie
134
+ * @param value value to set the cookie to
135
+ * @param [options] object containing serialization options
136
+ * @throws {TypeError} when `maxAge` options is invalid
137
+ */
124
138
  declare function serialize(name: string, value: string, options?: CookieSerializeOptions): string;
125
139
 
126
140
  export { CookieParseOptions, 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,8 +76,24 @@ 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
+ case "medium":
86
+ str += "; Priority=Medium";
87
+ break;
88
+ case "high":
89
+ str += "; Priority=High";
90
+ break;
91
+ default:
92
+ throw new TypeError("option priority is invalid");
93
+ }
94
+ }
75
95
  if (opt.sameSite) {
76
- let sameSite = typeof opt.sameSite === "string" ? opt.sameSite.toLowerCase() : opt.sameSite;
96
+ const sameSite = typeof opt.sameSite === "string" ? opt.sameSite.toLowerCase() : opt.sameSite;
77
97
  switch (sameSite) {
78
98
  case true:
79
99
  str += "; SameSite=Strict";
@@ -93,12 +113,21 @@ function serialize(name, value, options) {
93
113
  }
94
114
  return str;
95
115
  }
116
+ function isDate(val) {
117
+ return Object.prototype.toString.call(val) === "[object Date]" || val instanceof Date;
118
+ }
96
119
  function tryDecode(str, decode2) {
97
120
  try {
98
121
  return decode2(str);
99
- } catch (e) {
122
+ } catch {
100
123
  return str;
101
124
  }
102
125
  }
126
+ function decode(str) {
127
+ return str.includes("%") ? decodeURIComponent(str) : str;
128
+ }
129
+ function encode(val) {
130
+ return encodeURIComponent(val);
131
+ }
103
132
 
104
133
  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.0.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,21 @@
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": "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-c8": "^0.31.0",
31
+ "changelogen": "^0.5.3",
32
+ "eslint": "^8.39.0",
33
+ "eslint-config-unjs": "^0.1.0",
34
+ "prettier": "^2.8.8",
35
+ "typescript": "^5.0.4",
36
+ "unbuild": "^1.2.1",
37
+ "vitest": "^0.31.0"
38
+ },
39
+ "packageManager": "pnpm@8.4.0"
40
+ }