@remix-run/cookie 0.2.0 → 0.3.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,4 +1,4 @@
1
- # @remix-run/cookie
1
+ # cookie
2
2
 
3
3
  Simplify HTTP cookie management in JavaScript with type-safe, secure cookie handling. `@remix-run/cookie` provides a clean, intuitive API for creating, parsing, and serializing HTTP cookies with built-in support for signing, secret rotation, and comprehensive cookie attribute management.
4
4
 
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { type CookieOptions, Cookie } from './lib/cookie.ts';
1
+ export { createCookie, type CookieOptions, Cookie } from './lib/cookie.ts';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA"}
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export { Cookie } from "./lib/cookie.js";
1
+ export { createCookie, Cookie } from "./lib/cookie.js";
@@ -1,5 +1,12 @@
1
1
  import { type CookieProperties } from '@remix-run/headers';
2
- export interface CookieOptions {
2
+ /**
3
+ * Creates a new `Cookie` object.
4
+ * @param name The name of the cookie
5
+ * @param options The options for the cookie
6
+ * @returns A new cookie instance
7
+ */
8
+ export declare function createCookie(name: string, options?: CookieOptions): Cookie;
9
+ export interface CookieOptions extends CookieProperties {
3
10
  /**
4
11
  * A function that decodes the cookie value.
5
12
  *
@@ -34,12 +41,15 @@ export interface CookieOptions {
34
41
  */
35
42
  export declare class Cookie {
36
43
  #private;
37
- readonly name: string;
38
44
  constructor(name: string, options?: CookieOptions);
45
+ /**
46
+ * The name of the cookie.
47
+ */
48
+ readonly name: string;
39
49
  /**
40
50
  * True if this cookie uses one or more secrets for verification.
41
51
  */
42
- get isSigned(): boolean;
52
+ get signed(): boolean;
43
53
  /**
44
54
  * Extracts the value of this cookie from a `Cookie` header value.
45
55
  * @param headerValue The value of the `Cookie` header to parse
@@ -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,MAAM,WAAW,aAAa;IAC5B;;;;;;;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;;;GAGG;AACH,qBAAa,MAAM;;IACjB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;gBAKT,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa;IAOjD;;OAEG;IACH,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;;OAIG;IACG,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAa/D;;;;;OAKG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;CAY1E"}
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;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAE1E;AAED,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;;;GAGG;AACH,qBAAa,MAAM;;gBACL,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa;IAejD;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IAOrB;;OAEG;IACH,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED;;;;OAIG;IACG,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAa/D;;;;;OAKG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;CAa1E"}
@@ -1,24 +1,39 @@
1
1
  import { Cookie as CookieHeader, SetCookie as SetCookieHeader, } from '@remix-run/headers';
2
2
  import { sign, unsign } from "./crypto.js";
3
+ /**
4
+ * Creates a new `Cookie` object.
5
+ * @param name The name of the cookie
6
+ * @param options The options for the cookie
7
+ * @returns A new cookie instance
8
+ */
9
+ export function createCookie(name, options) {
10
+ return new Cookie(name, options);
11
+ }
3
12
  /**
4
13
  * A container for metadata about a HTTP cookie; its name and secrets that may be used
5
14
  * to sign/unsign the value of the cookie to ensure it's not tampered with.
6
15
  */
7
16
  export class Cookie {
17
+ constructor(name, options) {
18
+ let { decode = decodeURIComponent, encode = encodeURIComponent, secrets = [], ...props } = options ?? {};
19
+ this.name = name;
20
+ this.#decode = decode;
21
+ this.#encode = encode;
22
+ this.#secrets = secrets;
23
+ this.#props = props;
24
+ }
25
+ /**
26
+ * The name of the cookie.
27
+ */
8
28
  name;
9
29
  #decode;
10
30
  #encode;
11
31
  #secrets;
12
- constructor(name, options) {
13
- this.name = name;
14
- this.#decode = options?.decode;
15
- this.#encode = options?.encode;
16
- this.#secrets = options?.secrets ?? [];
17
- }
32
+ #props;
18
33
  /**
19
34
  * True if this cookie uses one or more secrets for verification.
20
35
  */
21
- get isSigned() {
36
+ get signed() {
22
37
  return this.#secrets.length > 0;
23
38
  }
24
39
  /**
@@ -51,22 +66,13 @@ export class Cookie {
51
66
  // sane defaults
52
67
  path: '/',
53
68
  sameSite: 'Lax',
69
+ ...this.#props,
54
70
  ...props,
55
71
  });
56
72
  return header.toString();
57
73
  }
58
74
  }
59
- async function encodeCookieValue(value, secrets, encode = encodeURIComponent) {
60
- let encoded = encodeValue(value, encode);
61
- if (secrets.length > 0) {
62
- encoded = await sign(encoded, secrets[0]);
63
- }
64
- return encoded;
65
- }
66
- function encodeValue(value, encode) {
67
- return btoa(myUnescape(encode(value)));
68
- }
69
- async function decodeCookieValue(value, secrets, decode = decodeURIComponent) {
75
+ async function decodeCookieValue(value, secrets, decode) {
70
76
  if (secrets.length > 0) {
71
77
  for (let secret of secrets) {
72
78
  let unsignedValue = await unsign(value, secret);
@@ -115,6 +121,16 @@ function hex(code, length) {
115
121
  result = '0' + result;
116
122
  return result;
117
123
  }
124
+ async function encodeCookieValue(value, secrets, encode) {
125
+ let encoded = encodeValue(value, encode);
126
+ if (secrets.length > 0) {
127
+ encoded = await sign(encoded, secrets[0]);
128
+ }
129
+ return encoded;
130
+ }
131
+ function encodeValue(value, encode) {
132
+ return btoa(myUnescape(encode(value)));
133
+ }
118
134
  // See: https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.unescape.js
119
135
  function myUnescape(value) {
120
136
  let str = value.toString();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remix-run/cookie",
3
- "version": "0.2.0",
3
+ "version": "0.3.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.15.0"
32
+ "@remix-run/headers": "^0.16.0"
33
33
  },
34
34
  "keywords": [
35
35
  "http",
package/src/index.ts CHANGED
@@ -1 +1 @@
1
- export { type CookieOptions, Cookie } from './lib/cookie.ts'
1
+ export { createCookie, type CookieOptions, Cookie } from './lib/cookie.ts'
package/src/lib/cookie.ts CHANGED
@@ -6,7 +6,17 @@ import {
6
6
 
7
7
  import { sign, unsign } from './crypto.ts'
8
8
 
9
- export interface CookieOptions {
9
+ /**
10
+ * Creates a new `Cookie` object.
11
+ * @param name The name of the cookie
12
+ * @param options The options for the cookie
13
+ * @returns A new cookie instance
14
+ */
15
+ export function createCookie(name: string, options?: CookieOptions): Cookie {
16
+ return new Cookie(name, options)
17
+ }
18
+
19
+ export interface CookieOptions extends CookieProperties {
10
20
  /**
11
21
  * A function that decodes the cookie value.
12
22
  *
@@ -41,22 +51,35 @@ export interface CookieOptions {
41
51
  * to sign/unsign the value of the cookie to ensure it's not tampered with.
42
52
  */
43
53
  export class Cookie {
44
- readonly name: string
45
- readonly #decode?: (value: string) => string
46
- readonly #encode?: (value: string) => string
47
- readonly #secrets: string[]
48
-
49
54
  constructor(name: string, options?: CookieOptions) {
55
+ let {
56
+ decode = decodeURIComponent,
57
+ encode = encodeURIComponent,
58
+ secrets = [],
59
+ ...props
60
+ } = options ?? {}
61
+
50
62
  this.name = name
51
- this.#decode = options?.decode
52
- this.#encode = options?.encode
53
- this.#secrets = options?.secrets ?? []
63
+ this.#decode = decode
64
+ this.#encode = encode
65
+ this.#secrets = secrets
66
+ this.#props = props
54
67
  }
55
68
 
69
+ /**
70
+ * The name of the cookie.
71
+ */
72
+ readonly name: string
73
+
74
+ readonly #decode: (value: string) => string
75
+ readonly #encode: (value: string) => string
76
+ readonly #secrets: string[]
77
+ readonly #props: CookieProperties
78
+
56
79
  /**
57
80
  * True if this cookie uses one or more secrets for verification.
58
81
  */
59
- get isSigned(): boolean {
82
+ get signed(): boolean {
60
83
  return this.#secrets.length > 0
61
84
  }
62
85
 
@@ -91,6 +114,7 @@ export class Cookie {
91
114
  // sane defaults
92
115
  path: '/',
93
116
  sameSite: 'Lax',
117
+ ...this.#props,
94
118
  ...props,
95
119
  })
96
120
 
@@ -98,28 +122,10 @@ export class Cookie {
98
122
  }
99
123
  }
100
124
 
101
- async function encodeCookieValue(
102
- value: string,
103
- secrets: string[],
104
- encode: (value: string) => string = encodeURIComponent,
105
- ): Promise<string> {
106
- let encoded = encodeValue(value, encode)
107
-
108
- if (secrets.length > 0) {
109
- encoded = await sign(encoded, secrets[0])
110
- }
111
-
112
- return encoded
113
- }
114
-
115
- function encodeValue(value: string, encode: (value: string) => string): string {
116
- return btoa(myUnescape(encode(value)))
117
- }
118
-
119
125
  async function decodeCookieValue(
120
126
  value: string,
121
127
  secrets: string[],
122
- decode: (value: string) => string = decodeURIComponent,
128
+ decode: (value: string) => string,
123
129
  ): Promise<string | null> {
124
130
  if (secrets.length > 0) {
125
131
  for (let secret of secrets) {
@@ -171,6 +177,24 @@ function hex(code: number, length: number): string {
171
177
  return result
172
178
  }
173
179
 
180
+ async function encodeCookieValue(
181
+ value: string,
182
+ secrets: string[],
183
+ encode: (value: string) => string,
184
+ ): Promise<string> {
185
+ let encoded = encodeValue(value, encode)
186
+
187
+ if (secrets.length > 0) {
188
+ encoded = await sign(encoded, secrets[0])
189
+ }
190
+
191
+ return encoded
192
+ }
193
+
194
+ function encodeValue(value: string, encode: (value: string) => string): string {
195
+ return btoa(myUnescape(encode(value)))
196
+ }
197
+
174
198
  // See: https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.unescape.js
175
199
  function myUnescape(value: string): string {
176
200
  let str = value.toString()
package/src/lib/crypto.ts CHANGED
@@ -40,7 +40,7 @@ async function createKey(secret: string, usages: CryptoKey['usages']): Promise<C
40
40
  )
41
41
  }
42
42
 
43
- function byteStringToUint8Array(byteString: string): Uint8Array {
43
+ function byteStringToUint8Array(byteString: string): Uint8Array<ArrayBuffer> {
44
44
  let array = new Uint8Array(byteString.length)
45
45
 
46
46
  for (let i = 0; i < byteString.length; i++) {