From 3779c80201c278943757f538eaed204853a33790 Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Fri, 12 Sep 2025 14:40:55 -0700 Subject: [PATCH 1/4] fix: remove `uncrypto` --- package.json | 9 ++------- pnpm-lock.yaml | 9 --------- src/ecdsa.ts | 21 ++++++++++----------- src/hash.ts | 4 ++-- src/hmac.ts | 8 ++++---- src/index.ts | 7 ++++++- src/random.test.ts | 5 ++--- src/random.ts | 4 ++-- src/rsa.ts | 20 ++++++++++++-------- 9 files changed, 40 insertions(+), 47 deletions(-) diff --git a/package.json b/package.json index cf42f2f..d1fb6a2 100644 --- a/package.json +++ b/package.json @@ -23,9 +23,6 @@ "type": "git", "url": "https://github.com/better-auth/utils" }, - "dependencies": { - "uncrypto": "^0.1.3" - }, "devDependencies": { "@biomejs/biome": "^1.9.4", "@types/node": "^22.10.1", @@ -81,7 +78,5 @@ "require": "./dist/rsa.cjs" } }, - "files": [ - "dist" - ] -} \ No newline at end of file + "files": ["dist"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bab6683..bf525d2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,10 +7,6 @@ settings: importers: .: - dependencies: - uncrypto: - specifier: ^0.1.3 - version: 0.1.3 devDependencies: '@biomejs/biome': specifier: ^1.9.4 @@ -1693,9 +1689,6 @@ packages: typescript: optional: true - uncrypto@0.1.3: - resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} - undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} @@ -3280,8 +3273,6 @@ snapshots: - supports-color - vue-tsc - uncrypto@0.1.3: {} - undici-types@6.20.0: {} untyped@1.5.1: diff --git a/src/ecdsa.ts b/src/ecdsa.ts index 8320104..db254ad 100644 --- a/src/ecdsa.ts +++ b/src/ecdsa.ts @@ -4,10 +4,12 @@ import type { SHAFamily, TypedArray, } from "./type"; +import { getWebcryptoSubtle } from "./index"; export const ecdsa = { generateKeyPair: async (curve: ECDSACurve = "P-256") => { - const keyPair = await crypto.subtle.generateKey( + const subtle = getWebcryptoSubtle(); + const keyPair = await subtle.generateKey( { name: "ECDSA", namedCurve: curve, @@ -15,11 +17,8 @@ export const ecdsa = { true, ["sign", "verify"], ); - const privateKey = await crypto.subtle.exportKey( - "pkcs8", - keyPair.privateKey, - ); - const publicKey = await crypto.subtle.exportKey("spki", keyPair.publicKey); + const privateKey = await subtle.exportKey("pkcs8", keyPair.privateKey); + const publicKey = await subtle.exportKey("spki", keyPair.publicKey); return { privateKey, publicKey }; }, importPrivateKey: async ( @@ -30,7 +29,7 @@ export const ecdsa = { if (typeof privateKey === "string") { privateKey = new TextEncoder().encode(privateKey); } - return await crypto.subtle.importKey( + return await getWebcryptoSubtle().importKey( "pkcs8", privateKey, { @@ -49,7 +48,7 @@ export const ecdsa = { if (typeof publicKey === "string") { publicKey = new TextEncoder().encode(publicKey); } - return await crypto.subtle.importKey( + return await getWebcryptoSubtle().importKey( "spki", publicKey, { @@ -68,7 +67,7 @@ export const ecdsa = { if (typeof data === "string") { data = new TextEncoder().encode(data); } - const signature = await crypto.subtle.sign( + const signature = await getWebcryptoSubtle().sign( { name: "ECDSA", hash: { name: hash }, @@ -97,7 +96,7 @@ export const ecdsa = { if (typeof data === "string") { data = new TextEncoder().encode(data); } - return await crypto.subtle.verify( + return await getWebcryptoSubtle().verify( { name: "ECDSA", hash: { name: hash }, @@ -111,6 +110,6 @@ export const ecdsa = { key: CryptoKey, format: E, ): Promise => { - return (await crypto.subtle.exportKey(format, key)) as any; + return (await getWebcryptoSubtle().exportKey(format, key)) as any; }, }; diff --git a/src/hash.ts b/src/hash.ts index 8d31c80..70ac422 100644 --- a/src/hash.ts +++ b/src/hash.ts @@ -1,6 +1,6 @@ -import { subtle } from "uncrypto"; import { base64, base64Url } from "./base64"; import type { EncodingFormat, SHAFamily, TypedArray } from "./type"; +import { getWebcryptoSubtle } from "./index"; export function createHash( algorithm: SHAFamily, @@ -12,7 +12,7 @@ export function createHash( ): Promise => { const encoder = new TextEncoder(); const data = typeof input === "string" ? encoder.encode(input) : input; - const hashBuffer = await subtle.digest(algorithm, data); + const hashBuffer = await getWebcryptoSubtle().digest(algorithm, data); if (encoding === "hex") { const hashArray = Array.from(new Uint8Array(hashBuffer)); diff --git a/src/hmac.ts b/src/hmac.ts index 66cc64c..8177a27 100644 --- a/src/hmac.ts +++ b/src/hmac.ts @@ -1,7 +1,7 @@ -import { subtle } from "uncrypto"; import type { EncodingFormat, SHAFamily, TypedArray } from "./type"; import { hex } from "./hex"; import { base64, base64Url } from "./base64"; +import { getWebcryptoSubtle } from "./index"; export const createHMAC = ( algorithm: SHAFamily = "SHA-256", @@ -12,7 +12,7 @@ export const createHMAC = ( key: string | ArrayBuffer | TypedArray, keyUsage: "sign" | "verify", ) => { - return subtle.importKey( + return getWebcryptoSubtle().importKey( "raw", typeof key === "string" ? new TextEncoder().encode(key) : key, { name: "HMAC", hash: { name: algorithm } }, @@ -27,7 +27,7 @@ export const createHMAC = ( if (typeof hmacKey === "string") { hmacKey = await hmac.importKey(hmacKey, "sign"); } - const signature = await subtle.sign( + const signature = await getWebcryptoSubtle().sign( "HMAC", hmacKey, typeof data === "string" ? new TextEncoder().encode(data) : data, @@ -64,7 +64,7 @@ export const createHMAC = ( ) { signature = await base64.decode(signature); } - return subtle.verify( + return getWebcryptoSubtle().verify( "HMAC", hmacKey, typeof signature === "string" diff --git a/src/index.ts b/src/index.ts index 419c46a..a7f459a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,6 @@ -export * from "uncrypto"; +export function getWebcryptoSubtle(): SubtleCrypto { + const cr = typeof globalThis !== "undefined" && (globalThis as any).crypto; + if (cr && typeof cr.subtle === "object" && cr.subtle != null) + return cr.subtle; + throw new Error("crypto.subtle must be defined"); +} diff --git a/src/random.test.ts b/src/random.test.ts index 5f3f722..53b2b08 100644 --- a/src/random.test.ts +++ b/src/random.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it, vi } from "vitest"; import { createRandomStringGenerator } from "./random"; -import { getRandomValues } from "uncrypto"; // Utility functions for distribution tests function generateLargeRandomSample( @@ -108,7 +107,7 @@ describe("createRandomStringGenerator", () => { it("combines multiple alphabets when passed during generation", () => { // Mock getRandomValues to return sequentially increasing values - vi.mock("uncrypto", () => ({ + vi.stubGlobal("crypto", { getRandomValues: vi.fn( (array: T): T => { if (array instanceof Uint8Array) { @@ -119,7 +118,7 @@ describe("createRandomStringGenerator", () => { return array; }, ), - })); + }); try { const generator = createRandomStringGenerator("a-z"); diff --git a/src/random.ts b/src/random.ts index 0902a37..7e8dbf6 100644 --- a/src/random.ts +++ b/src/random.ts @@ -1,4 +1,4 @@ -import { getRandomValues } from "uncrypto"; +import { getWebcryptoSubtle } from "./index"; type Alphabet = "a-z" | "A-Z" | "0-9" | "-_"; @@ -52,7 +52,7 @@ export function createRandomStringGenerator( while (result.length < length) { if (bufIndex >= bufLength) { - getRandomValues(buf); + crypto.getRandomValues(buf); bufIndex = 0; } diff --git a/src/rsa.ts b/src/rsa.ts index 7dc2143..52be9e3 100644 --- a/src/rsa.ts +++ b/src/rsa.ts @@ -1,4 +1,4 @@ -import { subtle } from "uncrypto"; +import { getWebcryptoSubtle } from "./index"; type ExportFormat = "jwk" | "spki" | "pkcs8"; @@ -7,7 +7,7 @@ export const rsa = { modulusLength: 2048 | 4096 = 2048, hash: "SHA-256" | "SHA-384" | "SHA-512" = "SHA-256", ) => { - return await subtle.generateKey( + return await getWebcryptoSubtle().generateKey( { name: "RSA-OAEP", modulusLength, @@ -22,14 +22,14 @@ export const rsa = { key: CryptoKey, format: E, ): Promise => { - return (await subtle.exportKey(format, key)) as any; + return (await getWebcryptoSubtle().exportKey(format, key)) as any; }, importKey: async ( key: JsonWebKey, usage: "encrypt" | "decrypt" = "encrypt", hash: "SHA-256" | "SHA-384" | "SHA-512" = "SHA-256", ) => { - return await subtle.importKey( + return await getWebcryptoSubtle().importKey( "jwk", key, { @@ -46,10 +46,14 @@ export const rsa = { ) => { const encodedData = typeof data === "string" ? new TextEncoder().encode(data) : data; - return await subtle.encrypt({ name: "RSA-OAEP" }, key, encodedData); + return await getWebcryptoSubtle().encrypt( + { name: "RSA-OAEP" }, + key, + encodedData, + ); }, decrypt: async (key: CryptoKey, data: ArrayBuffer | ArrayBufferView) => { - return await subtle.decrypt({ name: "RSA-OAEP" }, key, data); + return await getWebcryptoSubtle().decrypt({ name: "RSA-OAEP" }, key, data); }, sign: async ( key: CryptoKey, @@ -58,7 +62,7 @@ export const rsa = { ) => { const encodedData = typeof data === "string" ? new TextEncoder().encode(data) : data; - return await subtle.sign( + return await getWebcryptoSubtle().sign( { name: "RSA-PSS", saltLength, @@ -84,7 +88,7 @@ export const rsa = { } const encodedData = typeof data === "string" ? new TextEncoder().encode(data) : data; - return await subtle.verify( + return await getWebcryptoSubtle().verify( { name: "RSA-PSS", saltLength, From 5bb77fa57e2e26b070397029962ab837f88db9e1 Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Fri, 12 Sep 2025 14:42:32 -0700 Subject: [PATCH 2/4] docs: update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 20842d4..cf541d6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Better Auth Utils -A simple typescript API for common auth utilities like hashing, encryption, encoding, and OTP generation. Built on top of Web Crypto APIs and it wraps over [uncrypto](https://github.com/unjs/uncrypto) to provide a unified API for both Node.js (using the Crypto module) and web environments (using the Web Crypto API) through Conditional Exports. +A simple typescript API for common auth utilities like hashing, encryption, encoding, and OTP generation. Built on top of Web Crypto APIs to provide a unified API for both Node.js (using the Crypto module) and web environments (using the Web Crypto API) through Conditional Exports. ```bash pnpm add @better-auth/utils From e37d42ba56e43c5e972988ea40a0360fb6691fb3 Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Fri, 12 Sep 2025 14:50:14 -0700 Subject: [PATCH 3/4] fix: import --- src/random.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/random.ts b/src/random.ts index 7e8dbf6..aab1e52 100644 --- a/src/random.ts +++ b/src/random.ts @@ -1,5 +1,3 @@ -import { getWebcryptoSubtle } from "./index"; - type Alphabet = "a-z" | "A-Z" | "0-9" | "-_"; function expandAlphabet(alphabet: Alphabet): string { From 734c85be676c7edf69a2d3c709d43a8b62e31d96 Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Fri, 12 Sep 2025 14:53:18 -0700 Subject: [PATCH 4/4] fix: test --- src/random.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/random.test.ts b/src/random.test.ts index 53b2b08..42573bb 100644 --- a/src/random.test.ts +++ b/src/random.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it, vi } from "vitest"; +import { afterEach, describe, expect, it, vi } from "vitest"; import { createRandomStringGenerator } from "./random"; // Utility functions for distribution tests @@ -137,7 +137,7 @@ describe("createRandomStringGenerator", () => { expect(randomString).toHaveLength(256); } finally { // Restore the original implementation - vi.unmock("uncrypto"); + vi.unstubAllGlobals(); } });