diff --git a/README.md b/README.md index fcda007..3b66638 100644 --- a/README.md +++ b/README.md @@ -302,7 +302,7 @@ Base64 utilities provide a simple interface to encode and decode data in base64 ### Encoding -Encode data in base64 format. Input can be a string, `ArrayBuffer`, or `TypedArray`. +Encode data in base64 format. Input can be a string, `ArrayBuffer`, or number-based `TypedArray`. BigInt typed arrays are not supported. ```ts import { base64 } from "@better-auth/utils/base64"; @@ -343,7 +343,7 @@ Hex utilities provide a simple interface to encode and decode data in hexadecima ### Encoding -Encode data in hexadecimal format. Input can be a string, `ArrayBuffer`, or `TypedArray`. +Encode data in hexadecimal format. Input can be a string, `ArrayBuffer`, or number-based `TypedArray`. BigInt typed arrays are not supported. ```ts import { hex } from "@better-auth/utils/hex"; diff --git a/src/base32.ts b/src/base32.ts index d119354..9354f57 100644 --- a/src/base32.ts +++ b/src/base32.ts @@ -1,6 +1,6 @@ //inspired by oslo implementation by pilcrowonpaper: https://github.com/pilcrowonpaper/oslo/blob/main/src/encoding/base32.ts -import type { TypedArray, Uint8Array_ } from "./type"; +import type { NumberTypedArray, TypedArray, Uint8Array_ } from "./type"; import { toUint8Array } from "./bytes"; /** @@ -100,12 +100,12 @@ function base32Decode(data: string, alphabet: string): Uint8Array_ { export const base32 = { /** * Encodes data into a Base32 string. - * @param data - The data to encode (ArrayBuffer, TypedArray, or string). + * @param data - The data to encode (ArrayBuffer, number-based TypedArray, or string). * @param options - Encoding options. * @returns The Base32 encoded string. */ encode( - data: ArrayBuffer | TypedArray | string, + data: ArrayBuffer | NumberTypedArray | string, options: { padding?: boolean } = {}, ): string { const alphabet = getAlphabet(false); @@ -133,12 +133,12 @@ export const base32 = { export const base32hex = { /** * Encodes data into a Base32hex string. - * @param data - The data to encode (ArrayBuffer, TypedArray, or string). + * @param data - The data to encode (ArrayBuffer, number-based TypedArray, or string). * @param options - Encoding options. * @returns The Base32hex encoded string. */ encode( - data: ArrayBuffer | TypedArray | string, + data: ArrayBuffer | NumberTypedArray | string, options: { padding?: boolean } = {}, ): string { const alphabet = getAlphabet(true); diff --git a/src/base64.ts b/src/base64.ts index 8caa11a..7a1cde5 100644 --- a/src/base64.ts +++ b/src/base64.ts @@ -1,6 +1,6 @@ //inspired by oslo implementation by pilcrowonpaper: https://github.com/pilcrowonpaper/oslo/blob/main/src/encoding/base64.ts -import type { TypedArray, Uint8Array_ } from "./type"; +import type { NumberTypedArray, TypedArray, Uint8Array_ } from "./type"; import { toUint8Array } from "./bytes"; function getAlphabet(urlSafe: boolean): string { @@ -68,7 +68,7 @@ function base64Decode(data: string, alphabet: string): Uint8Array_ { export const base64 = { encode( - data: ArrayBuffer | TypedArray | string, + data: ArrayBuffer | NumberTypedArray | string, options: { padding?: boolean } = {}, ) { const alphabet = getAlphabet(false); @@ -87,7 +87,7 @@ export const base64 = { export const base64Url = { encode( - data: ArrayBuffer | TypedArray | string, + data: ArrayBuffer | NumberTypedArray | string, options: { padding?: boolean } = {}, ) { const alphabet = getAlphabet(true); diff --git a/src/bytes.test.ts b/src/bytes.test.ts index 7b39510..01dc294 100644 --- a/src/bytes.test.ts +++ b/src/bytes.test.ts @@ -9,6 +9,12 @@ describe("bytes", () => { expect(toBufferSource(input)).toBe(input); }); + it("reuses ArrayBuffer-backed BigInt views", () => { + const input = new BigUint64Array([1n]); + + expect(toBufferSource(input)).toBe(input); + }); + it("copies SharedArrayBuffer-backed views", () => { const buffer = new SharedArrayBuffer(4); const input = new Uint8Array(buffer, 1, 2); @@ -42,13 +48,20 @@ describe("bytes", () => { }); describe("toUint8Array", () => { + it("accepts clamped typed arrays", () => { + const input = new Uint8ClampedArray([0, 255]); + + expect(Array.from(toUint8Array(input))).toEqual([0, 255]); + }); + it("preserves element conversion for multi-byte typed arrays", () => { const input = new Uint16Array([256, 1]); expect(Array.from(toUint8Array(input))).toEqual([0, 1]); }); - it("preserves errors for BigInt typed arrays", () => { + it("rejects BigInt typed arrays", () => { + // @ts-expect-error BigInt typed arrays do not contain number elements. expect(() => toUint8Array(new BigUint64Array([1n]))).toThrow(TypeError); }); }); diff --git a/src/bytes.ts b/src/bytes.ts index 35321f4..3ba8746 100644 --- a/src/bytes.ts +++ b/src/bytes.ts @@ -1,4 +1,4 @@ -import type { TypedArray, Uint8Array_ } from "./type"; +import type { NumberTypedArray, Uint8Array_ } from "./type"; export function toBufferSource( data: string | ArrayBuffer | ArrayBufferView, @@ -18,14 +18,14 @@ export function toBufferSource( /** * Converts strings and binary data into a `Uint8Array`. * - * `ArrayBuffer` inputs share memory with the returned view, while `TypedArray` - * inputs are copied according to native constructor semantics. + * `ArrayBuffer` inputs share memory with the returned view, while number-based + * `TypedArray` inputs are copied according to native constructor semantics. * * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray#buffer | ArrayBuffer constructor behavior} * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray#typedarray | TypedArray constructor behavior} */ export function toUint8Array( - data: string | ArrayBuffer | TypedArray, + data: string | ArrayBuffer | NumberTypedArray, ): Uint8Array_ { if (typeof data === "string") { return new TextEncoder().encode(data); diff --git a/src/hex.ts b/src/hex.ts index f621d2b..282ce50 100644 --- a/src/hex.ts +++ b/src/hex.ts @@ -1,11 +1,11 @@ import { hexToBytes } from "@noble/hashes/utils.js"; -import type { TypedArray, Uint8Array_ } from "./type"; import { toUint8Array } from "./bytes"; +import type { NumberTypedArray, TypedArray, Uint8Array_ } from "./type"; const toBytes = (data: string): Uint8Array_ => hexToBytes(data) as Uint8Array_; export const hex = { - encode: (data: string | ArrayBuffer | TypedArray) => { + encode: (data: string | ArrayBuffer | NumberTypedArray) => { const buffer = toUint8Array(data); if (buffer.byteLength === 0) { return ""; diff --git a/src/type.ts b/src/type.ts index e71f4d2..85119d1 100644 --- a/src/type.ts +++ b/src/type.ts @@ -1,14 +1,15 @@ -export type TypedArray = +export type NumberTypedArray = | Uint8Array + | Uint8ClampedArray | Int8Array | Uint16Array | Int16Array | Uint32Array | Int32Array | Float32Array - | Float64Array - | BigInt64Array - | BigUint64Array; + | Float64Array; + +export type TypedArray = NumberTypedArray | BigInt64Array | BigUint64Array; /** * Equivalent to `Uint8Array` before TypeScript 5.7, and `Uint8Array` in TypeScript 5.7