Skip to content

Commit

Permalink
feat: add hex
Browse files Browse the repository at this point in the history
  • Loading branch information
Bekacru committed Dec 10, 2024
1 parent f2d3469 commit afd0de1
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 0 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ utilities provided by `@better-auth/utils`:
| [**RSA**](#rsa) | Perform encryption, decryption, signing, and verification with RSA keys. |
| [**ECDSA**](#ecdsa) | Perform signing and verification with ECDSA keys. |
| [**Base64**](#base64) | Encode and decode data in base64 format. |
| [**Hex**](#hex) | Encode and decode data in hexadecimal format. |
| [**OTP**](#otp) | Generate and verify one-time passwords. |
| [**Cookies**](#cookies) | Parse, serialize, and manage HTTP cookies. |

Expand Down Expand Up @@ -254,6 +255,28 @@ const decodedData = await base64.decode(encodedData);
It automatically detects if the input is URL-safe and includes padding characters.


## Hex

Hex utilities provide a simple interface to encode and decode data in hexadecimal format.

### Encoding

Encode data in hexadecimal format. Input can be a string, `ArrayBuffer`, or `TypedArray`.

```ts
import { hex } from "@better-auth/utils/hex";

const encodedData = hex.encode("Data to encode");
```

### Decoding

Decode hexadecimal-encoded data. Input can be a string or `ArrayBuffer`.

```ts
const decodedData = hex.decode(encodedData);
```

## OTP

The OTP utility provides a simple and secure way to generate and verify one-time passwords (OTPs), commonly used in multi-factor authentication (MFA) systems. It includes support for both HOTP (HMAC-based One-Time Password) and TOTP (Time-based One-Time Password) standards.
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
"import": "./dist/ecdsa.mjs",
"require": "./dist/ecdsa.cjs"
},
"./hex": {
"import": "./dist/hex.mjs",
"require": "./dist/hex.cjs"
},
"./hmac": {
"import": "./dist/hmac.mjs",
"require": "./dist/hmac.cjs"
Expand Down
64 changes: 64 additions & 0 deletions src/hex.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { describe, it, expect } from 'vitest';
import { hex } from './hex';

describe('hex', () => {
describe('encode', () => {
it('should encode a string to hexadecimal', () => {
const input = "Hello, World!";
const expected = "48656c6c6f2c20576f726c6421";
expect(hex.encode(input)).toBe(expected);
});

it('should encode an ArrayBuffer to hexadecimal', () => {
const input = new TextEncoder().encode("Hello").buffer;
const expected = "48656c6c6f";
expect(hex.encode(input)).toBe(expected);
});

it('should encode a TypedArray to hexadecimal', () => {
const input = new Uint8Array([72, 101, 108, 108, 111]);
const expected = "48656c6c6f";
expect(hex.encode(input)).toBe(expected);
});
});

describe('decode', () => {
it('should decode a hexadecimal string to its original value', () => {
const input = "48656c6c6f2c20576f726c6421";
const expected = "Hello, World!";
expect(hex.decode(input)).toBe(expected);
});

it('should handle decoding of a hexadecimal string to binary data', () => {
const input = "48656c6c6f";
const expected = "Hello";
expect(hex.decode(input)).toBe(expected);
});

it('should throw an error for an odd-length string', () => {
const input = "123";
expect(() => hex.decode(input)).toThrow(Error);
});

it('should throw an error for a non-hexadecimal string', () => {
const input = "zzzz";
expect(() => hex.decode(input)).toThrow(Error);
});
});

describe('round-trip tests', () => {
it('should return the original string after encoding and decoding', () => {
const input = "Hello, Hex!";
const encoded = hex.encode(input);
const decoded = hex.decode(encoded);
expect(decoded).toBe(input);
});

it('should handle empty strings', () => {
const input = "";
const encoded = hex.encode(input);
const decoded = hex.decode(encoded);
expect(decoded).toBe(input);
});
});
});
37 changes: 37 additions & 0 deletions src/hex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { TypedArray } from "./type";


const hexadecimal = "0123456789abcdef";
export const hex = {
encode: (data:
string | ArrayBuffer | TypedArray) => {
if (typeof data === "string") {
data = new TextEncoder().encode(data);
}
if (data.byteLength === 0) {
return "";
}
const buffer = new Uint8Array(data);
let result = "";
for (const byte of buffer) {
result += byte.toString(16).padStart(2, "0");
}
return result;
},
decode: (data: string) => {
if (!data) {
return "";
}
if (data.length % 2 !== 0) {
throw new Error("Invalid hexadecimal string");
}
if (!new RegExp(`^[${hexadecimal}]+$`).test(data)) {
throw new Error("Invalid hexadecimal string");
}
const result = new Uint8Array(data.length / 2);
for (let i = 0; i < data.length; i += 2) {
result[i / 2] = parseInt(data.slice(i, i + 2), 16);
}
return new TextDecoder().decode(result);
}
}

0 comments on commit afd0de1

Please sign in to comment.