@rexezuge/shared 1.0.9 → 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.
@@ -40,12 +40,20 @@ declare class CursorCodec {
40
40
  */
41
41
  static decode<T>(cursor: string | undefined | null): T | undefined;
42
42
  /**
43
- * Decode a cursor, throwing a plain `Error` on malformed input.
43
+ * Decode a cursor that is known to be present, throwing a plain `Error` on
44
+ * malformed input.
44
45
  *
45
46
  * For a call site that treats a bad cursor as a client error; the HTTP mapping
46
47
  * to `BadRequest` is the caller's layer's job (see the file header).
48
+ *
49
+ * The parameter is `string`, not `string | undefined | null`, and there is no
50
+ * `undefined` in the return. Two behaviours in one signature — "absent" and
51
+ * "malformed" — meant a caller could not tell them apart without re-reading
52
+ * the input it had already passed. Deciding absence belongs to the caller;
53
+ * this function's whole contract is that a cursor which *is* present either
54
+ * decodes or throws. Use {@link decode} when absence is an ordinary answer.
47
55
  */
48
- static decodeOrThrow<T>(cursor: string | undefined | null): T | undefined;
56
+ static decodeOrThrow<T>(cursor: string): T;
49
57
  /**
50
58
  * Whether a cursor is well-formed. A missing cursor is valid (first page).
51
59
  */
@@ -1 +1 @@
1
- {"version":3,"file":"CursorCodec.d.ts","sourceRoot":"","sources":["../src/CursorCodec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,UAAU,IAAI,CAAC,CAAC;IACd,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,cAAM,WAAW;WACD,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM;IAI5C;;;;;OAKG;WACW,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,SAAS;IASzE;;;;;OAKG;WACW,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,SAAS;IAShF;;OAEG;WACW,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO;IAUvE;;;OAGG;WACW,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;CAGzE;AAED,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,YAAY,EAAE,IAAI,EAAE,CAAC"}
1
+ {"version":3,"file":"CursorCodec.d.ts","sourceRoot":"","sources":["../src/CursorCodec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,UAAU,IAAI,CAAC,CAAC;IACd,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,cAAM,WAAW;WACD,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM;IAI5C;;;;;OAKG;WACW,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,SAAS;IASzE;;;;;;;;;;;;;OAaG;WACW,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,CAAC;IAQjD;;OAEG;WACW,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,OAAO;IAUvE;;;OAGG;WACW,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;CAGzE;AAED,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,YAAY,EAAE,IAAI,EAAE,CAAC"}
@@ -19,14 +19,20 @@ class CursorCodec {
19
19
  }
20
20
  }
21
21
  /**
22
- * Decode a cursor, throwing a plain `Error` on malformed input.
22
+ * Decode a cursor that is known to be present, throwing a plain `Error` on
23
+ * malformed input.
23
24
  *
24
25
  * For a call site that treats a bad cursor as a client error; the HTTP mapping
25
26
  * to `BadRequest` is the caller's layer's job (see the file header).
27
+ *
28
+ * The parameter is `string`, not `string | undefined | null`, and there is no
29
+ * `undefined` in the return. Two behaviours in one signature — "absent" and
30
+ * "malformed" — meant a caller could not tell them apart without re-reading
31
+ * the input it had already passed. Deciding absence belongs to the caller;
32
+ * this function's whole contract is that a cursor which *is* present either
33
+ * decodes or throws. Use {@link decode} when absence is an ordinary answer.
26
34
  */
27
35
  static decodeOrThrow(cursor) {
28
- if (!cursor)
29
- return undefined;
30
36
  try {
31
37
  return JSON.parse(atob(cursor));
32
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CursorCodec.js","sourceRoot":"","sources":["../src/CursorCodec.ts"],"names":[],"mappings":"AAiCA,MAAM,WAAW;IACR,MAAM,CAAC,MAAM,CAAC,KAAc;QACjC,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IACrC,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,MAAM,CAAI,MAAiC;QACvD,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAC9B,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAM,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,aAAa,CAAI,MAAiC;QAC9D,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAC9B,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAM,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED;;OAEG;IACI,MAAM,CAAC,aAAa,CAAC,MAAiC;QAC3D,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,MAAM,CAAC,IAAI,CAAI,KAAmB,EAAE,UAAmB;QAC5D,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;IACxD,CAAC;CACF;AAED,OAAO,EAAE,WAAW,EAAE,CAAC","sourcesContent":["/**\n * Canonical opaque-cursor codec, plus the `Page<T>` shape it produces.\n *\n * ## Provenance\n *\n * Converged from ChordDHT-Tracker's `utils/Cursor.ts` (a `Cursor` class + a\n * `Page<T>` type), Edge-Git's `backend-data/.../CursorUtil.ts` (a `CursorUtil`\n * class that adds `decodeOrThrow` + `isValidCursor`) and Mail-Otter's\n * `CursorUtil.ts` (the minimal `encode`/`decode` pair). All three hand-rolled\n * DAO pagination cursors and all three converged on `btoa(JSON.stringify(...))`\n * / `JSON.parse(atob(...))`. Canonical name is **`CursorCodec`**; the cursor is an\n * opaque token, so callers pass it through and never inspect it.\n *\n * ## Design decisions\n *\n * - The cursor is `btoa`/`atob`, which works in workerd *and* Node 24 (both\n * expose browser globals now). No `Buffer` is used, so the same bundle runs in\n * both.\n * - `decode` never throws: a malformed or tampered cursor is `undefined`, which a\n * DAO reads as \"no cursor, start from the beginning\" rather than an error an\n * attacker can provoke. `decodeOrThrow` and `isValidCursor` are kept from\n * Edge-Git for the call sites that *want* the strict behaviour.\n * - **Layer 0 stays dependency-free.** Edge-Git's `decodeOrThrow` threw its\n * `@edge-git/backend-errors` `BadRequestError`; the shared package imports\n * nothing, so it throws a plain `Error` and leaves the HTTP mapping to the\n * layer that owns the error taxonomy (the same \"throw plain `Error`, map above\"\n * rule `Identity`/`EmailUtil` follow).\n */\ninterface Page<T> {\n readonly items: readonly T[];\n readonly nextCursor?: string;\n}\n\nclass CursorCodec {\n public static encode(value: unknown): string {\n return btoa(JSON.stringify(value));\n }\n\n /**\n * Decode a cursor, or `undefined` for a missing/malformed one.\n *\n * `atob`/`JSON.parse` throw on bad input; the `try` turns that into `undefined`\n * so a corrupt cursor degrades to a first page instead of an uncaught throw.\n */\n public static decode<T>(cursor: string | undefined | null): T | undefined {\n if (!cursor) return undefined;\n try {\n return JSON.parse(atob(cursor)) as T;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Decode a cursor, throwing a plain `Error` on malformed input.\n *\n * For a call site that treats a bad cursor as a client error; the HTTP mapping\n * to `BadRequest` is the caller's layer's job (see the file header).\n */\n public static decodeOrThrow<T>(cursor: string | undefined | null): T | undefined {\n if (!cursor) return undefined;\n try {\n return JSON.parse(atob(cursor)) as T;\n } catch {\n throw new Error('Invalid cursor');\n }\n }\n\n /**\n * Whether a cursor is well-formed. A missing cursor is valid (first page).\n */\n public static isValidCursor(cursor: string | undefined | null): boolean {\n if (!cursor) return true;\n try {\n JSON.parse(atob(cursor));\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Build a `Page`, omitting `nextCursor` entirely on the last page rather than\n * carrying an explicit `undefined`.\n */\n public static page<T>(items: readonly T[], nextCursor?: string): Page<T> {\n return nextCursor ? { items, nextCursor } : { items };\n }\n}\n\nexport { CursorCodec };\nexport type { Page };\n"]}
1
+ {"version":3,"file":"CursorCodec.js","sourceRoot":"","sources":["../src/CursorCodec.ts"],"names":[],"mappings":"AAiCA,MAAM,WAAW;IACR,MAAM,CAAC,MAAM,CAAC,KAAc;QACjC,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IACrC,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,MAAM,CAAI,MAAiC;QACvD,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAC9B,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAM,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;OAaG;IACI,MAAM,CAAC,aAAa,CAAI,MAAc;QAC3C,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAM,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED;;OAEG;IACI,MAAM,CAAC,aAAa,CAAC,MAAiC;QAC3D,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,IAAI,CAAC;YACH,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,MAAM,CAAC,IAAI,CAAI,KAAmB,EAAE,UAAmB;QAC5D,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;IACxD,CAAC;CACF;AAED,OAAO,EAAE,WAAW,EAAE,CAAC","sourcesContent":["/**\n * Canonical opaque-cursor codec, plus the `Page<T>` shape it produces.\n *\n * ## Provenance\n *\n * Converged from ChordDHT-Tracker's `utils/Cursor.ts` (a `Cursor` class + a\n * `Page<T>` type), Edge-Git's `backend-data/.../CursorUtil.ts` (a `CursorUtil`\n * class that adds `decodeOrThrow` + `isValidCursor`) and Mail-Otter's\n * `CursorUtil.ts` (the minimal `encode`/`decode` pair). All three hand-rolled\n * DAO pagination cursors and all three converged on `btoa(JSON.stringify(...))`\n * / `JSON.parse(atob(...))`. Canonical name is **`CursorCodec`**; the cursor is an\n * opaque token, so callers pass it through and never inspect it.\n *\n * ## Design decisions\n *\n * - The cursor is `btoa`/`atob`, which works in workerd *and* Node 24 (both\n * expose browser globals now). No `Buffer` is used, so the same bundle runs in\n * both.\n * - `decode` never throws: a malformed or tampered cursor is `undefined`, which a\n * DAO reads as \"no cursor, start from the beginning\" rather than an error an\n * attacker can provoke. `decodeOrThrow` and `isValidCursor` are kept from\n * Edge-Git for the call sites that *want* the strict behaviour.\n * - **Layer 0 stays dependency-free.** Edge-Git's `decodeOrThrow` threw its\n * `@edge-git/backend-errors` `BadRequestError`; the shared package imports\n * nothing, so it throws a plain `Error` and leaves the HTTP mapping to the\n * layer that owns the error taxonomy (the same \"throw plain `Error`, map above\"\n * rule `Identity`/`EmailUtil` follow).\n */\ninterface Page<T> {\n readonly items: readonly T[];\n readonly nextCursor?: string;\n}\n\nclass CursorCodec {\n public static encode(value: unknown): string {\n return btoa(JSON.stringify(value));\n }\n\n /**\n * Decode a cursor, or `undefined` for a missing/malformed one.\n *\n * `atob`/`JSON.parse` throw on bad input; the `try` turns that into `undefined`\n * so a corrupt cursor degrades to a first page instead of an uncaught throw.\n */\n public static decode<T>(cursor: string | undefined | null): T | undefined {\n if (!cursor) return undefined;\n try {\n return JSON.parse(atob(cursor)) as T;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Decode a cursor that is known to be present, throwing a plain `Error` on\n * malformed input.\n *\n * For a call site that treats a bad cursor as a client error; the HTTP mapping\n * to `BadRequest` is the caller's layer's job (see the file header).\n *\n * The parameter is `string`, not `string | undefined | null`, and there is no\n * `undefined` in the return. Two behaviours in one signature — \"absent\" and\n * \"malformed\" — meant a caller could not tell them apart without re-reading\n * the input it had already passed. Deciding absence belongs to the caller;\n * this function's whole contract is that a cursor which *is* present either\n * decodes or throws. Use {@link decode} when absence is an ordinary answer.\n */\n public static decodeOrThrow<T>(cursor: string): T {\n try {\n return JSON.parse(atob(cursor)) as T;\n } catch {\n throw new Error('Invalid cursor');\n }\n }\n\n /**\n * Whether a cursor is well-formed. A missing cursor is valid (first page).\n */\n public static isValidCursor(cursor: string | undefined | null): boolean {\n if (!cursor) return true;\n try {\n JSON.parse(atob(cursor));\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Build a `Page`, omitting `nextCursor` entirely on the last page rather than\n * carrying an explicit `undefined`.\n */\n public static page<T>(items: readonly T[], nextCursor?: string): Page<T> {\n return nextCursor ? { items, nextCursor } : { items };\n }\n}\n\nexport { CursorCodec };\nexport type { Page };\n"]}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Generate an OAuth2 `state` value.
3
+ *
4
+ * 32 random bytes, base64url. Long enough that guessing one is not a route to a
5
+ * forged callback.
6
+ */
7
+ declare function generateState(): string;
8
+ /**
9
+ * Generate a PKCE `code_verifier`.
10
+ *
11
+ * RFC 7636 §4.1 asks for 43–128 characters of unreserved alphabet; 64 random
12
+ * bytes is base64url-encoded to 86, comfortably inside the range.
13
+ */
14
+ declare function generateCodeVerifier(): string;
15
+ /**
16
+ * Hash a `state` for storage.
17
+ *
18
+ * Stored rather than the raw value so a read of the authorization-session table
19
+ * does not hand out a live callback token. Compared with a constant-time compare
20
+ * at the call site — this is the digest, not the comparison.
21
+ */
22
+ declare function getStateHash(state: string): Promise<string>;
23
+ /**
24
+ * The PKCE `code_challenge` for a verifier: `BASE64URL(SHA-256(verifier))`.
25
+ *
26
+ * The S256 method, not `plain`. `plain` sends the verifier itself, which
27
+ * defeats the point of PKCE for anyone who can see the authorization request.
28
+ */
29
+ declare function getCodeChallenge(codeVerifier: string): Promise<string>;
30
+ export { generateCodeVerifier, generateState, getCodeChallenge, getStateHash };
31
+ //# sourceMappingURL=OAuth2Pkce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OAuth2Pkce.d.ts","sourceRoot":"","sources":["../src/OAuth2Pkce.ts"],"names":[],"mappings":"AAsBA;;;;;GAKG;AACH,iBAAS,aAAa,IAAI,MAAM,CAE/B;AAED;;;;;GAKG;AACH,iBAAS,oBAAoB,IAAI,MAAM,CAEtC;AAED;;;;;;GAMG;AACH,iBAAe,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE1D;AAED;;;;;GAKG;AACH,iBAAe,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGrE;AAED,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * OAuth2 PKCE and state generation.
3
+ *
4
+ * Converged from `oauth2/OAuth2StateUtil.ts` in Mail-Meow and Mail-Otter, which
5
+ * were byte-identical, plus the same four methods inline in CalDAV-Bridge's
6
+ * `oauth2/OAuth2ProviderUtil.ts`.
7
+ *
8
+ * ## Why this is in `shared` and the providers are not
9
+ *
10
+ * These four operations are pure — no `fetch`, no credential, no state — so
11
+ * every rule is testable from the Node suite with no Workers runtime and no
12
+ * double. The provider *transports* (Google's token endpoint, Microsoft Graph,
13
+ * Fastmail JMAP) are business logic and stay in the consumer repos, which is the
14
+ * line `shared`'s header draws: platform-generic helpers in, product-specific
15
+ * protocol clients out.
16
+ *
17
+ * The `state` hash is the security-relevant one: it is what binds a callback to
18
+ * the authorization request that started it, so it is generated here rather than
19
+ * at each call site where three repos had each grown their own.
20
+ */
21
+ import { CryptoUtil } from './CryptoUtil';
22
+ /**
23
+ * Generate an OAuth2 `state` value.
24
+ *
25
+ * 32 random bytes, base64url. Long enough that guessing one is not a route to a
26
+ * forged callback.
27
+ */
28
+ function generateState() {
29
+ return CryptoUtil.randomBase64Url(32);
30
+ }
31
+ /**
32
+ * Generate a PKCE `code_verifier`.
33
+ *
34
+ * RFC 7636 §4.1 asks for 43–128 characters of unreserved alphabet; 64 random
35
+ * bytes is base64url-encoded to 86, comfortably inside the range.
36
+ */
37
+ function generateCodeVerifier() {
38
+ return CryptoUtil.randomBase64Url(64);
39
+ }
40
+ /**
41
+ * Hash a `state` for storage.
42
+ *
43
+ * Stored rather than the raw value so a read of the authorization-session table
44
+ * does not hand out a live callback token. Compared with a constant-time compare
45
+ * at the call site — this is the digest, not the comparison.
46
+ */
47
+ async function getStateHash(state) {
48
+ return CryptoUtil.sha256Hex(state);
49
+ }
50
+ /**
51
+ * The PKCE `code_challenge` for a verifier: `BASE64URL(SHA-256(verifier))`.
52
+ *
53
+ * The S256 method, not `plain`. `plain` sends the verifier itself, which
54
+ * defeats the point of PKCE for anyone who can see the authorization request.
55
+ */
56
+ async function getCodeChallenge(codeVerifier) {
57
+ const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier));
58
+ return CryptoUtil.toBase64Url(new Uint8Array(digest));
59
+ }
60
+ export { generateCodeVerifier, generateState, getCodeChallenge, getStateHash };
61
+ //# sourceMappingURL=OAuth2Pkce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OAuth2Pkce.js","sourceRoot":"","sources":["../src/OAuth2Pkce.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C;;;;;GAKG;AACH,SAAS,aAAa;IACpB,OAAO,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;AACxC,CAAC;AAED;;;;;GAKG;AACH,SAAS,oBAAoB;IAC3B,OAAO,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,YAAY,CAAC,KAAa;IACvC,OAAO,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AACrC,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,gBAAgB,CAAC,YAAoB;IAClD,MAAM,MAAM,GAAgB,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;IAC1G,OAAO,UAAU,CAAC,WAAW,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,CAAC","sourcesContent":["/**\n * OAuth2 PKCE and state generation.\n *\n * Converged from `oauth2/OAuth2StateUtil.ts` in Mail-Meow and Mail-Otter, which\n * were byte-identical, plus the same four methods inline in CalDAV-Bridge's\n * `oauth2/OAuth2ProviderUtil.ts`.\n *\n * ## Why this is in `shared` and the providers are not\n *\n * These four operations are pure — no `fetch`, no credential, no state — so\n * every rule is testable from the Node suite with no Workers runtime and no\n * double. The provider *transports* (Google's token endpoint, Microsoft Graph,\n * Fastmail JMAP) are business logic and stay in the consumer repos, which is the\n * line `shared`'s header draws: platform-generic helpers in, product-specific\n * protocol clients out.\n *\n * The `state` hash is the security-relevant one: it is what binds a callback to\n * the authorization request that started it, so it is generated here rather than\n * at each call site where three repos had each grown their own.\n */\nimport { CryptoUtil } from './CryptoUtil';\n\n/**\n * Generate an OAuth2 `state` value.\n *\n * 32 random bytes, base64url. Long enough that guessing one is not a route to a\n * forged callback.\n */\nfunction generateState(): string {\n return CryptoUtil.randomBase64Url(32);\n}\n\n/**\n * Generate a PKCE `code_verifier`.\n *\n * RFC 7636 §4.1 asks for 43–128 characters of unreserved alphabet; 64 random\n * bytes is base64url-encoded to 86, comfortably inside the range.\n */\nfunction generateCodeVerifier(): string {\n return CryptoUtil.randomBase64Url(64);\n}\n\n/**\n * Hash a `state` for storage.\n *\n * Stored rather than the raw value so a read of the authorization-session table\n * does not hand out a live callback token. Compared with a constant-time compare\n * at the call site — this is the digest, not the comparison.\n */\nasync function getStateHash(state: string): Promise<string> {\n return CryptoUtil.sha256Hex(state);\n}\n\n/**\n * The PKCE `code_challenge` for a verifier: `BASE64URL(SHA-256(verifier))`.\n *\n * The S256 method, not `plain`. `plain` sends the verifier itself, which\n * defeats the point of PKCE for anyone who can see the authorization request.\n */\nasync function getCodeChallenge(codeVerifier: string): Promise<string> {\n const digest: ArrayBuffer = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier));\n return CryptoUtil.toBase64Url(new Uint8Array(digest));\n}\n\nexport { generateCodeVerifier, generateState, getCodeChallenge, getStateHash };\n"]}
package/dist/index.d.ts CHANGED
@@ -35,4 +35,5 @@ export { SubrequestCounter, UNMETERED_SUBREQUESTS, SUBREQUEST_KINDS, subrequestS
35
35
  export type { SubrequestMeter, SubrequestKind, SubrequestSpend } from './SubrequestMeter';
36
36
  export { passwordFingerprint } from './PasswordFingerprint';
37
37
  export { TokenHashUtil } from './TokenHashUtil';
38
+ export { generateCodeVerifier, generateState, getCodeChallenge, getStateHash } from './OAuth2Pkce';
38
39
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGxD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChE,YAAY,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGjE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACnG,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGjD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG7C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACpE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGjD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAChE,YAAY,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGhD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,aAAa,EACb,gBAAgB,EAChB,sBAAsB,EACtB,qBAAqB,EACrB,aAAa,EACb,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAG1C,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACtI,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAG1F,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAG5D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGxD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChE,YAAY,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGjE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACnG,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGjD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG7C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACpE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGjD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAChE,YAAY,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGhD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,aAAa,EACb,gBAAgB,EAChB,sBAAsB,EACtB,qBAAqB,EACrB,aAAa,EACb,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAG1C,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACtI,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAG1F,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAG5D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -39,4 +39,6 @@ export { SubrequestCounter, UNMETERED_SUBREQUESTS, SUBREQUEST_KINDS, subrequestS
39
39
  export { passwordFingerprint } from './PasswordFingerprint';
40
40
  // Token hashing.
41
41
  export { TokenHashUtil } from './TokenHashUtil';
42
+ // OAuth2 PKCE and state.
43
+ export { generateCodeVerifier, generateState, getCodeChallenge, getStateHash } from './OAuth2Pkce';
42
44
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,eAAe;AACf,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAExD,QAAQ;AACR,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEhE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEjE,UAAU;AACV,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEnG,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAGxD,oBAAoB;AACpB,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,eAAe;AACf,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGpE,oBAAoB;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,kBAAkB;AAClB,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAGhE,kBAAkB;AAClB,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,aAAa,EACb,gBAAgB,EAChB,sBAAsB,EACtB,qBAAqB,EACrB,aAAa,EACb,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAG3B,cAAc;AACd,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,oCAAoC;AACpC,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAGtI,yBAAyB;AACzB,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,iBAAiB;AACjB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC","sourcesContent":["/**\n * `@rexezuge/shared` — the converged, platform-generic utilities distilled from\n * the nine Rexezuge Cloudflare Worker monorepos. One concept per file; this barrel\n * only re-exports. Business-specific helpers (money, git oids, calendar sync,\n * credential sharding, …) are deliberately left in their consumer repos.\n *\n * `isValidEmailFormat` / `MAX_EMAIL_LENGTH` are defined in `Identity.ts` and\n * re-exported by `EmailUtil.ts`; the barrel sources each name **once** (from\n * `EmailUtil`, the one-stop email surface) so an `export *` merge cannot silently\n * drop them to ambiguity.\n */\n\n// Byte codecs.\nexport { bytesToBase64, base64ToBytes } from './Base64';\n\n// Time.\nexport { SystemClock, FixedClock, SYSTEM_CLOCK } from './Clock';\nexport type { Clock } from './Clock';\nexport { TimestampUtil } from './TimestampUtil';\nexport { TimeZoneUtil, DEFAULT_TIME_ZONE } from './TimeZoneUtil';\n\n// Locale.\nexport { LocaleUtil, DEFAULT_LOCALE, SUPPORTED_LOCALES, LOCALE_DISPLAY_NAMES } from './LocaleUtil';\nexport type { SupportedLocale } from './LocaleUtil';\nexport { canonicalizeLanguageTag } from './LanguageTag';\nexport type { LanguageTag } from './LanguageTag';\n\n// Identity + email.\nexport { normalizeEmail, isValidEmailFormat, MAX_EMAIL_LENGTH } from './EmailUtil';\nexport { EMAIL_FORMAT_RE } from './Identity';\n\n// Identifiers.\nexport { UUIDUtil } from './UUIDUtil';\nexport { CryptoIdGenerator, FixedIdGenerator } from './IdGenerator';\nexport type { IdGenerator } from './IdGenerator';\n\n// Crypto + secrets.\nexport { CryptoUtil } from './CryptoUtil';\n\n// Error handling.\nexport { ErrorSanitizationUtil } from './ErrorSanitizationUtil';\nexport { err, getOrThrow, isOk, mapResult, ok } from './Result';\nexport type { Err, Ok, Result } from './Result';\n\n// Network policy.\nexport {\n normalizeRemoteUrl,\n resolveRedirectUrl,\n parseAllowedHosts,\n isPrivateOrInternalHost,\n stripBrackets,\n stripTrailingDot,\n RemoteUrlRejectedError,\n MAX_REMOTE_URL_LENGTH,\n MAX_REDIRECTS,\n BLOCKED_HOST_SUFFIXES,\n BLOCKED_HOSTNAMES,\n} from './RemoteUrlPolicy';\nexport type { UrlPolicyOptions } from './RemoteUrlPolicy';\n\n// Pagination.\nexport { CursorCodec } from './CursorCodec';\nexport type { Page } from './CursorCodec';\n\n// Cloudflare subrequest accounting.\nexport { SubrequestCounter, UNMETERED_SUBREQUESTS, SUBREQUEST_KINDS, subrequestSpend, NO_SUBREQUESTS_SPENT } from './SubrequestMeter';\nexport type { SubrequestMeter, SubrequestKind, SubrequestSpend } from './SubrequestMeter';\n\n// Auth-side memo helper.\nexport { passwordFingerprint } from './PasswordFingerprint';\n\n// Token hashing.\nexport { TokenHashUtil } from './TokenHashUtil';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,eAAe;AACf,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAExD,QAAQ;AACR,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEhE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEjE,UAAU;AACV,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEnG,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAGxD,oBAAoB;AACpB,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,eAAe;AACf,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGpE,oBAAoB;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,kBAAkB;AAClB,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAGhE,kBAAkB;AAClB,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,aAAa,EACb,gBAAgB,EAChB,sBAAsB,EACtB,qBAAqB,EACrB,aAAa,EACb,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAG3B,cAAc;AACd,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,oCAAoC;AACpC,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAGtI,yBAAyB;AACzB,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,iBAAiB;AACjB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,yBAAyB;AACzB,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC","sourcesContent":["/**\n * `@rexezuge/shared` — the converged, platform-generic utilities distilled from\n * the nine Rexezuge Cloudflare Worker monorepos. One concept per file; this barrel\n * only re-exports. Business-specific helpers (money, git oids, calendar sync,\n * credential sharding, …) are deliberately left in their consumer repos.\n *\n * `isValidEmailFormat` / `MAX_EMAIL_LENGTH` are defined in `Identity.ts` and\n * re-exported by `EmailUtil.ts`; the barrel sources each name **once** (from\n * `EmailUtil`, the one-stop email surface) so an `export *` merge cannot silently\n * drop them to ambiguity.\n */\n\n// Byte codecs.\nexport { bytesToBase64, base64ToBytes } from './Base64';\n\n// Time.\nexport { SystemClock, FixedClock, SYSTEM_CLOCK } from './Clock';\nexport type { Clock } from './Clock';\nexport { TimestampUtil } from './TimestampUtil';\nexport { TimeZoneUtil, DEFAULT_TIME_ZONE } from './TimeZoneUtil';\n\n// Locale.\nexport { LocaleUtil, DEFAULT_LOCALE, SUPPORTED_LOCALES, LOCALE_DISPLAY_NAMES } from './LocaleUtil';\nexport type { SupportedLocale } from './LocaleUtil';\nexport { canonicalizeLanguageTag } from './LanguageTag';\nexport type { LanguageTag } from './LanguageTag';\n\n// Identity + email.\nexport { normalizeEmail, isValidEmailFormat, MAX_EMAIL_LENGTH } from './EmailUtil';\nexport { EMAIL_FORMAT_RE } from './Identity';\n\n// Identifiers.\nexport { UUIDUtil } from './UUIDUtil';\nexport { CryptoIdGenerator, FixedIdGenerator } from './IdGenerator';\nexport type { IdGenerator } from './IdGenerator';\n\n// Crypto + secrets.\nexport { CryptoUtil } from './CryptoUtil';\n\n// Error handling.\nexport { ErrorSanitizationUtil } from './ErrorSanitizationUtil';\nexport { err, getOrThrow, isOk, mapResult, ok } from './Result';\nexport type { Err, Ok, Result } from './Result';\n\n// Network policy.\nexport {\n normalizeRemoteUrl,\n resolveRedirectUrl,\n parseAllowedHosts,\n isPrivateOrInternalHost,\n stripBrackets,\n stripTrailingDot,\n RemoteUrlRejectedError,\n MAX_REMOTE_URL_LENGTH,\n MAX_REDIRECTS,\n BLOCKED_HOST_SUFFIXES,\n BLOCKED_HOSTNAMES,\n} from './RemoteUrlPolicy';\nexport type { UrlPolicyOptions } from './RemoteUrlPolicy';\n\n// Pagination.\nexport { CursorCodec } from './CursorCodec';\nexport type { Page } from './CursorCodec';\n\n// Cloudflare subrequest accounting.\nexport { SubrequestCounter, UNMETERED_SUBREQUESTS, SUBREQUEST_KINDS, subrequestSpend, NO_SUBREQUESTS_SPENT } from './SubrequestMeter';\nexport type { SubrequestMeter, SubrequestKind, SubrequestSpend } from './SubrequestMeter';\n\n// Auth-side memo helper.\nexport { passwordFingerprint } from './PasswordFingerprint';\n\n// Token hashing.\nexport { TokenHashUtil } from './TokenHashUtil';\n\n// OAuth2 PKCE and state.\nexport { generateCodeVerifier, generateState, getCodeChallenge, getStateHash } from './OAuth2Pkce';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rexezuge/shared",
3
- "version": "1.0.9",
3
+ "version": "1.1.0",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "repository": {