@remix-run/cookie 0.4.1 → 0.5.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/LICENSE +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/cookie.d.ts +42 -40
- package/dist/lib/cookie.d.ts.map +1 -1
- package/dist/lib/cookie.js +160 -66
- package/package.json +2 -2
- package/src/index.ts +1 -1
- package/src/lib/cookie.ts +151 -126
package/LICENSE
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { type
|
|
1
|
+
export { type CookieOptions, Cookie, createCookie } from './lib/cookie.ts';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { createCookie } from "./lib/cookie.js";
|
|
1
|
+
export { Cookie, createCookie } from "./lib/cookie.js";
|
package/dist/lib/cookie.d.ts
CHANGED
|
@@ -1,4 +1,33 @@
|
|
|
1
1
|
import { type CookieProperties } from '@remix-run/headers';
|
|
2
|
+
export interface CookieOptions extends CookieProperties {
|
|
3
|
+
/**
|
|
4
|
+
* A function that decodes the cookie value.
|
|
5
|
+
*
|
|
6
|
+
* Defaults to `decodeURIComponent`, which decodes any URL-encoded sequences into their original
|
|
7
|
+
* characters.
|
|
8
|
+
*
|
|
9
|
+
* See [RFC 6265](https://tools.ietf.org/html/rfc6265#section-4.1.1) for more details.
|
|
10
|
+
*/
|
|
11
|
+
decode?: (value: string) => string;
|
|
12
|
+
/**
|
|
13
|
+
* A function that encodes the cookie value.
|
|
14
|
+
*
|
|
15
|
+
* Defaults to `encodeURIComponent`, which percent-encodes all characters that are not allowed
|
|
16
|
+
* in a cookie value.
|
|
17
|
+
*
|
|
18
|
+
* See [RFC 6265](https://tools.ietf.org/html/rfc6265#section-4.1.1) for more details.
|
|
19
|
+
*/
|
|
20
|
+
encode?: (value: string) => string;
|
|
21
|
+
/**
|
|
22
|
+
* An array of secrets that may be used to sign/unsign the value of a cookie.
|
|
23
|
+
*
|
|
24
|
+
* The array makes it easy to rotate secrets. New secrets should be added to
|
|
25
|
+
* the beginning of the array. `cookie.serialize()` will always use the first
|
|
26
|
+
* value in the array, but `cookie.parse()` may use any of them so that
|
|
27
|
+
* cookies that were signed with older secrets still work.
|
|
28
|
+
*/
|
|
29
|
+
secrets?: string[];
|
|
30
|
+
}
|
|
2
31
|
type SameSiteValue = 'Strict' | 'Lax' | 'None';
|
|
3
32
|
/**
|
|
4
33
|
* Represents a HTTP cookie.
|
|
@@ -8,37 +37,39 @@ type SameSiteValue = 'Strict' | 'Lax' | 'None';
|
|
|
8
37
|
* Also supports cryptographic signing of the cookie value to ensure it's not tampered with, and
|
|
9
38
|
* secret rotation to easily rotate secrets without breaking existing cookies.
|
|
10
39
|
*/
|
|
11
|
-
export
|
|
40
|
+
export declare class Cookie {
|
|
41
|
+
#private;
|
|
42
|
+
constructor(name: string, options?: CookieOptions);
|
|
12
43
|
/**
|
|
13
44
|
* The domain of the cookie.
|
|
14
45
|
*
|
|
15
46
|
* [MDN Reference](https://developer.mozilla.org/en-US/Web/HTTP/Headers/Set-Cookie#domaindomain-value)
|
|
16
47
|
*/
|
|
17
|
-
|
|
48
|
+
get domain(): string | undefined;
|
|
18
49
|
/**
|
|
19
50
|
* The expiration date of the cookie.
|
|
20
51
|
*
|
|
21
52
|
* [MDN Reference](https://developer.mozilla.org/en-US/Web/HTTP/Headers/Set-Cookie#expiresdate)
|
|
22
53
|
*/
|
|
23
|
-
|
|
54
|
+
get expires(): Date | undefined;
|
|
24
55
|
/**
|
|
25
56
|
* True if the cookie is HTTP-only.
|
|
26
57
|
*
|
|
27
58
|
* [MDN Reference](https://developer.mozilla.org/en-US/Web/HTTP/Headers/Set-Cookie#httponly)
|
|
28
59
|
*/
|
|
29
|
-
|
|
60
|
+
get httpOnly(): boolean;
|
|
30
61
|
/**
|
|
31
62
|
* The maximum age of the cookie in seconds.
|
|
32
63
|
*
|
|
33
64
|
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#max-agenumber)
|
|
34
65
|
*/
|
|
35
|
-
|
|
66
|
+
get maxAge(): number | undefined;
|
|
36
67
|
/**
|
|
37
68
|
* The name of the cookie.
|
|
38
69
|
*
|
|
39
70
|
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#cookie-namecookie-value)
|
|
40
71
|
*/
|
|
41
|
-
|
|
72
|
+
get name(): string;
|
|
42
73
|
/**
|
|
43
74
|
* Extracts the value of this cookie from a `Cookie` header value.
|
|
44
75
|
* @param headerValue The `Cookie` header to parse
|
|
@@ -50,25 +81,25 @@ export interface Cookie {
|
|
|
50
81
|
*
|
|
51
82
|
* [MDN Reference](https://developer.mozilla.org/en-US/Web/HTTP/Headers/Set-Cookie#partitioned)
|
|
52
83
|
*/
|
|
53
|
-
|
|
84
|
+
get partitioned(): boolean;
|
|
54
85
|
/**
|
|
55
86
|
* The path of the cookie. Defaults to `/`.
|
|
56
87
|
*
|
|
57
88
|
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#pathpath-value)
|
|
58
89
|
*/
|
|
59
|
-
|
|
90
|
+
get path(): string;
|
|
60
91
|
/**
|
|
61
92
|
* The `SameSite` attribute of the cookie. Defaults to `Lax`.
|
|
62
93
|
*
|
|
63
94
|
* [MDN Reference](https://developer.mozilla.org/en-US/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value)
|
|
64
95
|
*/
|
|
65
|
-
|
|
96
|
+
get sameSite(): SameSiteValue;
|
|
66
97
|
/**
|
|
67
98
|
* True if the cookie is secure (only sent over HTTPS).
|
|
68
99
|
*
|
|
69
100
|
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#secure)
|
|
70
101
|
*/
|
|
71
|
-
|
|
102
|
+
get secure(): boolean;
|
|
72
103
|
/**
|
|
73
104
|
* Returns the value to use in a `Set-Cookie` header for this cookie.
|
|
74
105
|
* @param value The value to serialize
|
|
@@ -79,36 +110,7 @@ export interface Cookie {
|
|
|
79
110
|
/**
|
|
80
111
|
* True if this cookie uses one or more secrets for verification.
|
|
81
112
|
*/
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
export interface CookieOptions extends CookieProperties {
|
|
85
|
-
/**
|
|
86
|
-
* A function that decodes the cookie value.
|
|
87
|
-
*
|
|
88
|
-
* Defaults to `decodeURIComponent`, which decodes any URL-encoded sequences into their original
|
|
89
|
-
* characters.
|
|
90
|
-
*
|
|
91
|
-
* See [RFC 6265](https://tools.ietf.org/html/rfc6265#section-4.1.1) for more details.
|
|
92
|
-
*/
|
|
93
|
-
decode?: (value: string) => string;
|
|
94
|
-
/**
|
|
95
|
-
* A function that encodes the cookie value.
|
|
96
|
-
*
|
|
97
|
-
* Defaults to `encodeURIComponent`, which percent-encodes all characters that are not allowed
|
|
98
|
-
* in a cookie value.
|
|
99
|
-
*
|
|
100
|
-
* See [RFC 6265](https://tools.ietf.org/html/rfc6265#section-4.1.1) for more details.
|
|
101
|
-
*/
|
|
102
|
-
encode?: (value: string) => string;
|
|
103
|
-
/**
|
|
104
|
-
* An array of secrets that may be used to sign/unsign the value of a cookie.
|
|
105
|
-
*
|
|
106
|
-
* The array makes it easy to rotate secrets. New secrets should be added to
|
|
107
|
-
* the beginning of the array. `cookie.serialize()` will always use the first
|
|
108
|
-
* value in the array, but `cookie.parse()` may use any of them so that
|
|
109
|
-
* cookies that were signed with older secrets still work.
|
|
110
|
-
*/
|
|
111
|
-
secrets?: string[];
|
|
113
|
+
get signed(): boolean;
|
|
112
114
|
}
|
|
113
115
|
/**
|
|
114
116
|
* Creates a new cookie object.
|
package/dist/lib/cookie.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookie.d.ts","sourceRoot":"","sources":["../../src/lib/cookie.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAA;AAI3B,KAAK,aAAa,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"cookie.d.ts","sourceRoot":"","sources":["../../src/lib/cookie.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAA;AAI3B,MAAM,WAAW,aAAc,SAAQ,gBAAgB;IACrD;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IAClC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IAClC;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,KAAK,aAAa,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAA;AAG9C;;;;;;;GAOG;AACH,qBAAa,MAAM;;gBAcL,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa;IAmCjD;;;;OAIG;IACH,IAAI,MAAM,IAAI,MAAM,GAAG,SAAS,CAE/B;IAED;;;;OAIG;IACH,IAAI,OAAO,IAAI,IAAI,GAAG,SAAS,CAE9B;IAED;;;;OAIG;IACH,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;;OAIG;IACH,IAAI,MAAM,IAAI,MAAM,GAAG,SAAS,CAE/B;IAED;;;;OAIG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;OAIG;IACG,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAa/D;;;;OAIG;IACH,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;;;OAIG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;OAIG;IACH,IAAI,QAAQ,IAAI,aAAa,CAE5B;IAED;;;;OAIG;IACH,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED;;;;;OAKG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAkBzE;;OAEG;IACH,IAAI,MAAM,IAAI,OAAO,CAEpB;CACF;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAE1E"}
|
package/dist/lib/cookie.js
CHANGED
|
@@ -1,5 +1,164 @@
|
|
|
1
1
|
import { Cookie as CookieHeader, SetCookie as SetCookieHeader, } from '@remix-run/headers';
|
|
2
2
|
import { sign, unsign } from "./cookie-signing.js";
|
|
3
|
+
/**
|
|
4
|
+
* Represents a HTTP cookie.
|
|
5
|
+
*
|
|
6
|
+
* Supports parsing and serializing the cookie to/from `Cookie` and `Set-Cookie` headers.
|
|
7
|
+
*
|
|
8
|
+
* Also supports cryptographic signing of the cookie value to ensure it's not tampered with, and
|
|
9
|
+
* secret rotation to easily rotate secrets without breaking existing cookies.
|
|
10
|
+
*/
|
|
11
|
+
export class Cookie {
|
|
12
|
+
#name;
|
|
13
|
+
#decode;
|
|
14
|
+
#encode;
|
|
15
|
+
#secrets;
|
|
16
|
+
#domain;
|
|
17
|
+
#expires;
|
|
18
|
+
#httpOnly;
|
|
19
|
+
#maxAge;
|
|
20
|
+
#partitioned;
|
|
21
|
+
#path;
|
|
22
|
+
#sameSite;
|
|
23
|
+
#secure;
|
|
24
|
+
constructor(name, options) {
|
|
25
|
+
let { decode = decodeURIComponent, encode = encodeURIComponent, secrets = [], domain, expires, httpOnly, maxAge, path = '/', partitioned, secure, sameSite = 'Lax', } = options ?? {};
|
|
26
|
+
if (partitioned === true) {
|
|
27
|
+
// Partitioned cookies must be set with Secure
|
|
28
|
+
// See https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies
|
|
29
|
+
secure = true;
|
|
30
|
+
}
|
|
31
|
+
this.#name = name;
|
|
32
|
+
this.#decode = decode;
|
|
33
|
+
this.#encode = encode;
|
|
34
|
+
this.#secrets = secrets;
|
|
35
|
+
this.#domain = domain;
|
|
36
|
+
this.#expires = expires;
|
|
37
|
+
this.#httpOnly = httpOnly;
|
|
38
|
+
this.#maxAge = maxAge;
|
|
39
|
+
this.#partitioned = partitioned;
|
|
40
|
+
this.#path = path;
|
|
41
|
+
this.#sameSite = sameSite;
|
|
42
|
+
this.#secure = secure;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The domain of the cookie.
|
|
46
|
+
*
|
|
47
|
+
* [MDN Reference](https://developer.mozilla.org/en-US/Web/HTTP/Headers/Set-Cookie#domaindomain-value)
|
|
48
|
+
*/
|
|
49
|
+
get domain() {
|
|
50
|
+
return this.#domain;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The expiration date of the cookie.
|
|
54
|
+
*
|
|
55
|
+
* [MDN Reference](https://developer.mozilla.org/en-US/Web/HTTP/Headers/Set-Cookie#expiresdate)
|
|
56
|
+
*/
|
|
57
|
+
get expires() {
|
|
58
|
+
return this.#expires;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* True if the cookie is HTTP-only.
|
|
62
|
+
*
|
|
63
|
+
* [MDN Reference](https://developer.mozilla.org/en-US/Web/HTTP/Headers/Set-Cookie#httponly)
|
|
64
|
+
*/
|
|
65
|
+
get httpOnly() {
|
|
66
|
+
return this.#httpOnly ?? false;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The maximum age of the cookie in seconds.
|
|
70
|
+
*
|
|
71
|
+
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#max-agenumber)
|
|
72
|
+
*/
|
|
73
|
+
get maxAge() {
|
|
74
|
+
return this.#maxAge;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The name of the cookie.
|
|
78
|
+
*
|
|
79
|
+
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#cookie-namecookie-value)
|
|
80
|
+
*/
|
|
81
|
+
get name() {
|
|
82
|
+
return this.#name;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Extracts the value of this cookie from a `Cookie` header value.
|
|
86
|
+
* @param headerValue The `Cookie` header to parse
|
|
87
|
+
* @returns The value of this cookie, or `null` if it's not present
|
|
88
|
+
*/
|
|
89
|
+
async parse(headerValue) {
|
|
90
|
+
if (!headerValue)
|
|
91
|
+
return null;
|
|
92
|
+
let header = new CookieHeader(headerValue);
|
|
93
|
+
if (!header.has(this.#name))
|
|
94
|
+
return null;
|
|
95
|
+
let value = header.get(this.#name);
|
|
96
|
+
if (value === '')
|
|
97
|
+
return '';
|
|
98
|
+
let decoded = await decodeCookieValue(value, this.#secrets, this.#decode);
|
|
99
|
+
return decoded;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* True if the cookie is partitioned.
|
|
103
|
+
*
|
|
104
|
+
* [MDN Reference](https://developer.mozilla.org/en-US/Web/HTTP/Headers/Set-Cookie#partitioned)
|
|
105
|
+
*/
|
|
106
|
+
get partitioned() {
|
|
107
|
+
return this.#partitioned ?? false;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The path of the cookie. Defaults to `/`.
|
|
111
|
+
*
|
|
112
|
+
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#pathpath-value)
|
|
113
|
+
*/
|
|
114
|
+
get path() {
|
|
115
|
+
return this.#path;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The `SameSite` attribute of the cookie. Defaults to `Lax`.
|
|
119
|
+
*
|
|
120
|
+
* [MDN Reference](https://developer.mozilla.org/en-US/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value)
|
|
121
|
+
*/
|
|
122
|
+
get sameSite() {
|
|
123
|
+
return this.#sameSite;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* True if the cookie is secure (only sent over HTTPS).
|
|
127
|
+
*
|
|
128
|
+
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#secure)
|
|
129
|
+
*/
|
|
130
|
+
get secure() {
|
|
131
|
+
return this.#secure ?? false;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Returns the value to use in a `Set-Cookie` header for this cookie.
|
|
135
|
+
* @param value The value to serialize
|
|
136
|
+
* @param props (optional) Additional properties to use when serializing the cookie
|
|
137
|
+
* @returns The `Set-Cookie` header for this cookie
|
|
138
|
+
*/
|
|
139
|
+
async serialize(value, props) {
|
|
140
|
+
let header = new SetCookieHeader({
|
|
141
|
+
name: this.#name,
|
|
142
|
+
value: value === '' ? '' : await encodeCookieValue(value, this.#secrets, this.#encode),
|
|
143
|
+
domain: this.#domain,
|
|
144
|
+
expires: this.#expires,
|
|
145
|
+
httpOnly: this.#httpOnly,
|
|
146
|
+
maxAge: this.#maxAge,
|
|
147
|
+
partitioned: this.#partitioned,
|
|
148
|
+
path: this.#path,
|
|
149
|
+
sameSite: this.#sameSite,
|
|
150
|
+
secure: this.#secure,
|
|
151
|
+
...props,
|
|
152
|
+
});
|
|
153
|
+
return header.toString();
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* True if this cookie uses one or more secrets for verification.
|
|
157
|
+
*/
|
|
158
|
+
get signed() {
|
|
159
|
+
return this.#secrets.length > 0;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
3
162
|
/**
|
|
4
163
|
* Creates a new cookie object.
|
|
5
164
|
* @param name The name of the cookie
|
|
@@ -7,72 +166,7 @@ import { sign, unsign } from "./cookie-signing.js";
|
|
|
7
166
|
* @returns A cookie object
|
|
8
167
|
*/
|
|
9
168
|
export function createCookie(name, options) {
|
|
10
|
-
|
|
11
|
-
if (partitioned === true) {
|
|
12
|
-
// Partitioned cookies must be set with Secure
|
|
13
|
-
// See https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies
|
|
14
|
-
secure = true;
|
|
15
|
-
}
|
|
16
|
-
return {
|
|
17
|
-
get domain() {
|
|
18
|
-
return domain;
|
|
19
|
-
},
|
|
20
|
-
get expires() {
|
|
21
|
-
return expires;
|
|
22
|
-
},
|
|
23
|
-
get httpOnly() {
|
|
24
|
-
return httpOnly ?? false;
|
|
25
|
-
},
|
|
26
|
-
get maxAge() {
|
|
27
|
-
return maxAge;
|
|
28
|
-
},
|
|
29
|
-
get name() {
|
|
30
|
-
return name;
|
|
31
|
-
},
|
|
32
|
-
async parse(headerValue) {
|
|
33
|
-
if (!headerValue)
|
|
34
|
-
return null;
|
|
35
|
-
let header = new CookieHeader(headerValue);
|
|
36
|
-
if (!header.has(name))
|
|
37
|
-
return null;
|
|
38
|
-
let value = header.get(name);
|
|
39
|
-
if (value === '')
|
|
40
|
-
return '';
|
|
41
|
-
let decoded = await decodeCookieValue(value, secrets, decode);
|
|
42
|
-
return decoded;
|
|
43
|
-
},
|
|
44
|
-
get partitioned() {
|
|
45
|
-
return partitioned ?? false;
|
|
46
|
-
},
|
|
47
|
-
get path() {
|
|
48
|
-
return path;
|
|
49
|
-
},
|
|
50
|
-
get sameSite() {
|
|
51
|
-
return sameSite;
|
|
52
|
-
},
|
|
53
|
-
get secure() {
|
|
54
|
-
return secure ?? false;
|
|
55
|
-
},
|
|
56
|
-
async serialize(value, props) {
|
|
57
|
-
let header = new SetCookieHeader({
|
|
58
|
-
name: name,
|
|
59
|
-
value: value === '' ? '' : await encodeCookieValue(value, secrets, encode),
|
|
60
|
-
domain,
|
|
61
|
-
expires,
|
|
62
|
-
httpOnly,
|
|
63
|
-
maxAge,
|
|
64
|
-
partitioned,
|
|
65
|
-
path,
|
|
66
|
-
sameSite,
|
|
67
|
-
secure,
|
|
68
|
-
...props,
|
|
69
|
-
});
|
|
70
|
-
return header.toString();
|
|
71
|
-
},
|
|
72
|
-
get signed() {
|
|
73
|
-
return secrets.length > 0;
|
|
74
|
-
},
|
|
75
|
-
};
|
|
169
|
+
return new Cookie(name, options);
|
|
76
170
|
}
|
|
77
171
|
async function decodeCookieValue(value, secrets, decode) {
|
|
78
172
|
if (secrets.length > 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remix-run/cookie",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "A toolkit for working with cookies in JavaScript",
|
|
5
5
|
"author": "Michael Jackson <mjijackson@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"@types/node": "^24.6.0"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"@remix-run/headers": "^0.17.
|
|
32
|
+
"@remix-run/headers": "^0.17.1"
|
|
33
33
|
},
|
|
34
34
|
"keywords": [
|
|
35
35
|
"http",
|
package/src/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { type
|
|
1
|
+
export { type CookieOptions, Cookie, createCookie } from './lib/cookie.ts'
|
package/src/lib/cookie.ts
CHANGED
|
@@ -6,7 +6,38 @@ import {
|
|
|
6
6
|
|
|
7
7
|
import { sign, unsign } from './cookie-signing.ts'
|
|
8
8
|
|
|
9
|
+
export interface CookieOptions extends CookieProperties {
|
|
10
|
+
/**
|
|
11
|
+
* A function that decodes the cookie value.
|
|
12
|
+
*
|
|
13
|
+
* Defaults to `decodeURIComponent`, which decodes any URL-encoded sequences into their original
|
|
14
|
+
* characters.
|
|
15
|
+
*
|
|
16
|
+
* See [RFC 6265](https://tools.ietf.org/html/rfc6265#section-4.1.1) for more details.
|
|
17
|
+
*/
|
|
18
|
+
decode?: (value: string) => string
|
|
19
|
+
/**
|
|
20
|
+
* A function that encodes the cookie value.
|
|
21
|
+
*
|
|
22
|
+
* Defaults to `encodeURIComponent`, which percent-encodes all characters that are not allowed
|
|
23
|
+
* in a cookie value.
|
|
24
|
+
*
|
|
25
|
+
* See [RFC 6265](https://tools.ietf.org/html/rfc6265#section-4.1.1) for more details.
|
|
26
|
+
*/
|
|
27
|
+
encode?: (value: string) => string
|
|
28
|
+
/**
|
|
29
|
+
* An array of secrets that may be used to sign/unsign the value of a cookie.
|
|
30
|
+
*
|
|
31
|
+
* The array makes it easy to rotate secrets. New secrets should be added to
|
|
32
|
+
* the beginning of the array. `cookie.serialize()` will always use the first
|
|
33
|
+
* value in the array, but `cookie.parse()` may use any of them so that
|
|
34
|
+
* cookies that were signed with older secrets still work.
|
|
35
|
+
*/
|
|
36
|
+
secrets?: string[]
|
|
37
|
+
}
|
|
38
|
+
|
|
9
39
|
type SameSiteValue = 'Strict' | 'Lax' | 'None'
|
|
40
|
+
type Coder = (value: string) => string
|
|
10
41
|
|
|
11
42
|
/**
|
|
12
43
|
* Represents a HTTP cookie.
|
|
@@ -16,108 +47,184 @@ type SameSiteValue = 'Strict' | 'Lax' | 'None'
|
|
|
16
47
|
* Also supports cryptographic signing of the cookie value to ensure it's not tampered with, and
|
|
17
48
|
* secret rotation to easily rotate secrets without breaking existing cookies.
|
|
18
49
|
*/
|
|
19
|
-
export
|
|
50
|
+
export class Cookie {
|
|
51
|
+
#name: string
|
|
52
|
+
#decode: Coder
|
|
53
|
+
#encode: Coder
|
|
54
|
+
#secrets: string[]
|
|
55
|
+
#domain: string | undefined
|
|
56
|
+
#expires: Date | undefined
|
|
57
|
+
#httpOnly: boolean | undefined
|
|
58
|
+
#maxAge: number | undefined
|
|
59
|
+
#partitioned: boolean | undefined
|
|
60
|
+
#path: string
|
|
61
|
+
#sameSite: SameSiteValue
|
|
62
|
+
#secure: boolean | undefined
|
|
63
|
+
|
|
64
|
+
constructor(name: string, options?: CookieOptions) {
|
|
65
|
+
let {
|
|
66
|
+
decode = decodeURIComponent,
|
|
67
|
+
encode = encodeURIComponent,
|
|
68
|
+
secrets = [],
|
|
69
|
+
domain,
|
|
70
|
+
expires,
|
|
71
|
+
httpOnly,
|
|
72
|
+
maxAge,
|
|
73
|
+
path = '/',
|
|
74
|
+
partitioned,
|
|
75
|
+
secure,
|
|
76
|
+
sameSite = 'Lax',
|
|
77
|
+
} = options ?? {}
|
|
78
|
+
|
|
79
|
+
if (partitioned === true) {
|
|
80
|
+
// Partitioned cookies must be set with Secure
|
|
81
|
+
// See https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies
|
|
82
|
+
secure = true
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
this.#name = name
|
|
86
|
+
this.#decode = decode
|
|
87
|
+
this.#encode = encode
|
|
88
|
+
this.#secrets = secrets
|
|
89
|
+
this.#domain = domain
|
|
90
|
+
this.#expires = expires
|
|
91
|
+
this.#httpOnly = httpOnly
|
|
92
|
+
this.#maxAge = maxAge
|
|
93
|
+
this.#partitioned = partitioned
|
|
94
|
+
this.#path = path
|
|
95
|
+
this.#sameSite = sameSite
|
|
96
|
+
this.#secure = secure
|
|
97
|
+
}
|
|
98
|
+
|
|
20
99
|
/**
|
|
21
100
|
* The domain of the cookie.
|
|
22
101
|
*
|
|
23
102
|
* [MDN Reference](https://developer.mozilla.org/en-US/Web/HTTP/Headers/Set-Cookie#domaindomain-value)
|
|
24
103
|
*/
|
|
25
|
-
|
|
104
|
+
get domain(): string | undefined {
|
|
105
|
+
return this.#domain
|
|
106
|
+
}
|
|
107
|
+
|
|
26
108
|
/**
|
|
27
109
|
* The expiration date of the cookie.
|
|
28
110
|
*
|
|
29
111
|
* [MDN Reference](https://developer.mozilla.org/en-US/Web/HTTP/Headers/Set-Cookie#expiresdate)
|
|
30
112
|
*/
|
|
31
|
-
|
|
113
|
+
get expires(): Date | undefined {
|
|
114
|
+
return this.#expires
|
|
115
|
+
}
|
|
116
|
+
|
|
32
117
|
/**
|
|
33
118
|
* True if the cookie is HTTP-only.
|
|
34
119
|
*
|
|
35
120
|
* [MDN Reference](https://developer.mozilla.org/en-US/Web/HTTP/Headers/Set-Cookie#httponly)
|
|
36
121
|
*/
|
|
37
|
-
|
|
122
|
+
get httpOnly(): boolean {
|
|
123
|
+
return this.#httpOnly ?? false
|
|
124
|
+
}
|
|
125
|
+
|
|
38
126
|
/**
|
|
39
127
|
* The maximum age of the cookie in seconds.
|
|
40
128
|
*
|
|
41
129
|
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#max-agenumber)
|
|
42
130
|
*/
|
|
43
|
-
|
|
131
|
+
get maxAge(): number | undefined {
|
|
132
|
+
return this.#maxAge
|
|
133
|
+
}
|
|
134
|
+
|
|
44
135
|
/**
|
|
45
136
|
* The name of the cookie.
|
|
46
137
|
*
|
|
47
138
|
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#cookie-namecookie-value)
|
|
48
139
|
*/
|
|
49
|
-
|
|
140
|
+
get name(): string {
|
|
141
|
+
return this.#name
|
|
142
|
+
}
|
|
143
|
+
|
|
50
144
|
/**
|
|
51
145
|
* Extracts the value of this cookie from a `Cookie` header value.
|
|
52
146
|
* @param headerValue The `Cookie` header to parse
|
|
53
147
|
* @returns The value of this cookie, or `null` if it's not present
|
|
54
148
|
*/
|
|
55
|
-
parse(headerValue: string | null): Promise<string | null>
|
|
149
|
+
async parse(headerValue: string | null): Promise<string | null> {
|
|
150
|
+
if (!headerValue) return null
|
|
151
|
+
|
|
152
|
+
let header = new CookieHeader(headerValue)
|
|
153
|
+
if (!header.has(this.#name)) return null
|
|
154
|
+
|
|
155
|
+
let value = header.get(this.#name)!
|
|
156
|
+
if (value === '') return ''
|
|
157
|
+
|
|
158
|
+
let decoded = await decodeCookieValue(value, this.#secrets, this.#decode)
|
|
159
|
+
return decoded
|
|
160
|
+
}
|
|
161
|
+
|
|
56
162
|
/**
|
|
57
163
|
* True if the cookie is partitioned.
|
|
58
164
|
*
|
|
59
165
|
* [MDN Reference](https://developer.mozilla.org/en-US/Web/HTTP/Headers/Set-Cookie#partitioned)
|
|
60
166
|
*/
|
|
61
|
-
|
|
167
|
+
get partitioned(): boolean {
|
|
168
|
+
return this.#partitioned ?? false
|
|
169
|
+
}
|
|
170
|
+
|
|
62
171
|
/**
|
|
63
172
|
* The path of the cookie. Defaults to `/`.
|
|
64
173
|
*
|
|
65
174
|
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#pathpath-value)
|
|
66
175
|
*/
|
|
67
|
-
|
|
176
|
+
get path(): string {
|
|
177
|
+
return this.#path
|
|
178
|
+
}
|
|
179
|
+
|
|
68
180
|
/**
|
|
69
181
|
* The `SameSite` attribute of the cookie. Defaults to `Lax`.
|
|
70
182
|
*
|
|
71
183
|
* [MDN Reference](https://developer.mozilla.org/en-US/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value)
|
|
72
184
|
*/
|
|
73
|
-
|
|
185
|
+
get sameSite(): SameSiteValue {
|
|
186
|
+
return this.#sameSite
|
|
187
|
+
}
|
|
188
|
+
|
|
74
189
|
/**
|
|
75
190
|
* True if the cookie is secure (only sent over HTTPS).
|
|
76
191
|
*
|
|
77
192
|
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#secure)
|
|
78
193
|
*/
|
|
79
|
-
|
|
194
|
+
get secure(): boolean {
|
|
195
|
+
return this.#secure ?? false
|
|
196
|
+
}
|
|
197
|
+
|
|
80
198
|
/**
|
|
81
199
|
* Returns the value to use in a `Set-Cookie` header for this cookie.
|
|
82
200
|
* @param value The value to serialize
|
|
83
201
|
* @param props (optional) Additional properties to use when serializing the cookie
|
|
84
202
|
* @returns The `Set-Cookie` header for this cookie
|
|
85
203
|
*/
|
|
86
|
-
serialize(value: string, props?: CookieProperties): Promise<string>
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
204
|
+
async serialize(value: string, props?: CookieProperties): Promise<string> {
|
|
205
|
+
let header = new SetCookieHeader({
|
|
206
|
+
name: this.#name,
|
|
207
|
+
value: value === '' ? '' : await encodeCookieValue(value, this.#secrets, this.#encode),
|
|
208
|
+
domain: this.#domain,
|
|
209
|
+
expires: this.#expires,
|
|
210
|
+
httpOnly: this.#httpOnly,
|
|
211
|
+
maxAge: this.#maxAge,
|
|
212
|
+
partitioned: this.#partitioned,
|
|
213
|
+
path: this.#path,
|
|
214
|
+
sameSite: this.#sameSite,
|
|
215
|
+
secure: this.#secure,
|
|
216
|
+
...props,
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
return header.toString()
|
|
220
|
+
}
|
|
92
221
|
|
|
93
|
-
export interface CookieOptions extends CookieProperties {
|
|
94
|
-
/**
|
|
95
|
-
* A function that decodes the cookie value.
|
|
96
|
-
*
|
|
97
|
-
* Defaults to `decodeURIComponent`, which decodes any URL-encoded sequences into their original
|
|
98
|
-
* characters.
|
|
99
|
-
*
|
|
100
|
-
* See [RFC 6265](https://tools.ietf.org/html/rfc6265#section-4.1.1) for more details.
|
|
101
|
-
*/
|
|
102
|
-
decode?: (value: string) => string
|
|
103
|
-
/**
|
|
104
|
-
* A function that encodes the cookie value.
|
|
105
|
-
*
|
|
106
|
-
* Defaults to `encodeURIComponent`, which percent-encodes all characters that are not allowed
|
|
107
|
-
* in a cookie value.
|
|
108
|
-
*
|
|
109
|
-
* See [RFC 6265](https://tools.ietf.org/html/rfc6265#section-4.1.1) for more details.
|
|
110
|
-
*/
|
|
111
|
-
encode?: (value: string) => string
|
|
112
222
|
/**
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* The array makes it easy to rotate secrets. New secrets should be added to
|
|
116
|
-
* the beginning of the array. `cookie.serialize()` will always use the first
|
|
117
|
-
* value in the array, but `cookie.parse()` may use any of them so that
|
|
118
|
-
* cookies that were signed with older secrets still work.
|
|
223
|
+
* True if this cookie uses one or more secrets for verification.
|
|
119
224
|
*/
|
|
120
|
-
|
|
225
|
+
get signed(): boolean {
|
|
226
|
+
return this.#secrets.length > 0
|
|
227
|
+
}
|
|
121
228
|
}
|
|
122
229
|
|
|
123
230
|
/**
|
|
@@ -127,91 +234,9 @@ export interface CookieOptions extends CookieProperties {
|
|
|
127
234
|
* @returns A cookie object
|
|
128
235
|
*/
|
|
129
236
|
export function createCookie(name: string, options?: CookieOptions): Cookie {
|
|
130
|
-
|
|
131
|
-
decode = decodeURIComponent,
|
|
132
|
-
encode = encodeURIComponent,
|
|
133
|
-
secrets = [],
|
|
134
|
-
domain,
|
|
135
|
-
expires,
|
|
136
|
-
httpOnly,
|
|
137
|
-
maxAge,
|
|
138
|
-
path = '/',
|
|
139
|
-
partitioned,
|
|
140
|
-
secure,
|
|
141
|
-
sameSite = 'Lax',
|
|
142
|
-
} = options ?? {}
|
|
143
|
-
|
|
144
|
-
if (partitioned === true) {
|
|
145
|
-
// Partitioned cookies must be set with Secure
|
|
146
|
-
// See https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies
|
|
147
|
-
secure = true
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return {
|
|
151
|
-
get domain() {
|
|
152
|
-
return domain
|
|
153
|
-
},
|
|
154
|
-
get expires() {
|
|
155
|
-
return expires
|
|
156
|
-
},
|
|
157
|
-
get httpOnly() {
|
|
158
|
-
return httpOnly ?? false
|
|
159
|
-
},
|
|
160
|
-
get maxAge() {
|
|
161
|
-
return maxAge
|
|
162
|
-
},
|
|
163
|
-
get name() {
|
|
164
|
-
return name
|
|
165
|
-
},
|
|
166
|
-
async parse(headerValue: string | null): Promise<string | null> {
|
|
167
|
-
if (!headerValue) return null
|
|
168
|
-
|
|
169
|
-
let header = new CookieHeader(headerValue)
|
|
170
|
-
if (!header.has(name)) return null
|
|
171
|
-
|
|
172
|
-
let value = header.get(name)!
|
|
173
|
-
if (value === '') return ''
|
|
174
|
-
|
|
175
|
-
let decoded = await decodeCookieValue(value, secrets, decode)
|
|
176
|
-
return decoded
|
|
177
|
-
},
|
|
178
|
-
get partitioned() {
|
|
179
|
-
return partitioned ?? false
|
|
180
|
-
},
|
|
181
|
-
get path() {
|
|
182
|
-
return path
|
|
183
|
-
},
|
|
184
|
-
get sameSite() {
|
|
185
|
-
return sameSite
|
|
186
|
-
},
|
|
187
|
-
get secure() {
|
|
188
|
-
return secure ?? false
|
|
189
|
-
},
|
|
190
|
-
async serialize(value: string, props?: CookieProperties): Promise<string> {
|
|
191
|
-
let header = new SetCookieHeader({
|
|
192
|
-
name: name,
|
|
193
|
-
value: value === '' ? '' : await encodeCookieValue(value, secrets, encode),
|
|
194
|
-
domain,
|
|
195
|
-
expires,
|
|
196
|
-
httpOnly,
|
|
197
|
-
maxAge,
|
|
198
|
-
partitioned,
|
|
199
|
-
path,
|
|
200
|
-
sameSite,
|
|
201
|
-
secure,
|
|
202
|
-
...props,
|
|
203
|
-
})
|
|
204
|
-
|
|
205
|
-
return header.toString()
|
|
206
|
-
},
|
|
207
|
-
get signed() {
|
|
208
|
-
return secrets.length > 0
|
|
209
|
-
},
|
|
210
|
-
}
|
|
237
|
+
return new Cookie(name, options)
|
|
211
238
|
}
|
|
212
239
|
|
|
213
|
-
type Coder = (value: string) => string
|
|
214
|
-
|
|
215
240
|
async function decodeCookieValue(
|
|
216
241
|
value: string,
|
|
217
242
|
secrets: string[],
|