Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 152 additions & 60 deletions README.md

Large diffs are not rendered by default.

18 changes: 15 additions & 3 deletions array.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
/**
* TypedArray utils and conversions.
*
* ```js
* import { typedView } from '@exodus/bytes/array.js'
* ```
*
* @module @exodus/bytes/array.js
*/

/// <reference types="node" />

// >= TypeScript 5.9 made Uint8Array templated with <> and defaulted to ArrayBufferLike
Expand All @@ -12,10 +22,12 @@ export type Uint8ArrayBuffer = ReturnType<typeof Uint8Array.from>;
export type OutputFormat = 'uint8' | 'buffer';

/**
* Creates a view of a TypedArray in the specified format
* Note: This does not copy data - returns a view on the same underlying buffer
* Create a view of a TypedArray in the specified format (`'uint8'` or `'buffer'`)
*
* Important: does not copy data, returns a view on the same underlying buffer
*
* @param arr - The input TypedArray
* @param format - The desired output format ('uint8' or 'buffer')
* @param format - The desired output format (`'uint8'` or `'buffer'`)
* @returns A view on the same underlying buffer
*/
export function typedView(arr: ArrayBufferView, format: 'uint8'): Uint8Array;
Expand Down
55 changes: 38 additions & 17 deletions base64.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
/**
* Implements Base64 from [RFC4648](https://datatracker.ietf.org/doc/html/rfc4648)
* (no differences from [RFC3548](https://datatracker.ietf.org/doc/html/rfc4648)).
*
* ```js
* import { fromBase64, toBase64 } from '@exodus/bytes/base64.js'
* import { fromBase64url, toBase64url } from '@exodus/bytes/base64.js'
* import { fromBase64any } from '@exodus/bytes/base64.js'
* ```
*
* @module @exodus/bytes/base64.js
*/

/// <reference types="node" />

import type { OutputFormat, Uint8ArrayBuffer } from './array.js';
Expand All @@ -12,9 +25,9 @@ export interface ToBase64Options {

/**
* Padding mode for base64 decoding
* - true: padding is required
* - false: padding is not allowed
* - 'both': padding is optional (default for base64)
* - `true`: padding is required
* - `false`: padding is not allowed
* - `'both'`: padding is optional (default for base64)
*/
export type PaddingMode = boolean | 'both';

Expand All @@ -29,47 +42,55 @@ export interface FromBase64Options {
}

/**
* Encodes a Uint8Array to a base64 string (RFC 4648)
* Encode a `Uint8Array` to a base64 string (RFC 4648)
*
* @param arr - The input bytes
* @param options - Encoding options
* @returns The base64 encoded string
*/
export function toBase64(arr: Uint8ArrayBuffer, options?: ToBase64Options): string;

/**
* Encodes a Uint8Array to a base64url string (RFC 4648)
* Encode a `Uint8Array` to a base64url string (RFC 4648)
*
* @param arr - The input bytes
* @param options - Encoding options (padding defaults to false)
* @returns The base64url encoded string
*/
export function toBase64url(arr: Uint8ArrayBuffer, options?: ToBase64Options): string;

/**
* Decodes a base64 string to bytes
* Decode a base64 string to bytes
*
* Operates in strict mode for last chunk, does not allow whitespace
* @param str - The base64 encoded string
*
* @param string - The base64 encoded string
* @param options - Decoding options
* @returns The decoded bytes
*/
export function fromBase64(str: string, options?: FromBase64Options): Uint8ArrayBuffer;
export function fromBase64(str: string, options: FromBase64Options & { format: 'buffer' }): Buffer;
export function fromBase64(string: string, options?: FromBase64Options): Uint8ArrayBuffer;
export function fromBase64(string: string, options: FromBase64Options & { format: 'buffer' }): Buffer;

/**
* Decodes a base64url string to bytes
* Decode a base64url string to bytes
*
* Operates in strict mode for last chunk, does not allow whitespace
* @param str - The base64url encoded string
*
* @param string - The base64url encoded string
* @param options - Decoding options (padding defaults to false)
* @returns The decoded bytes
*/
export function fromBase64url(str: string, options?: FromBase64Options): Uint8ArrayBuffer;
export function fromBase64url(str: string, options: FromBase64Options & { format: 'buffer' }): Buffer;
export function fromBase64url(string: string, options?: FromBase64Options): Uint8ArrayBuffer;
export function fromBase64url(string: string, options: FromBase64Options & { format: 'buffer' }): Buffer;

/**
* Decodes either base64 or base64url string to bytes
* Decode either base64 or base64url string to bytes
*
* Automatically detects the variant based on characters present
* @param str - The base64 or base64url encoded string
*
* @param string - The base64 or base64url encoded string
* @param options - Decoding options
* @returns The decoded bytes
*/
export function fromBase64any(str: string, options?: FromBase64Options): Uint8ArrayBuffer;
export function fromBase64any(str: string, options: FromBase64Options & { format: 'buffer' }): Buffer;
export function fromBase64any(string: string, options?: FromBase64Options): Uint8ArrayBuffer;
export function fromBase64any(string: string, options: FromBase64Options & { format: 'buffer' }): Buffer;
4 changes: 4 additions & 0 deletions encoding-browser.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
/**
* @module @exodus/bytes/encoding-browser.js
*/

export * from './encoding.js'
4 changes: 4 additions & 0 deletions encoding-lite.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
/**
* @module @exodus/bytes/encoding-lite.js
*/

export * from './encoding.js'
103 changes: 92 additions & 11 deletions encoding.d.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,57 @@
/**
* ```js
* import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding.js'
* import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding.js' // Requires Streams
*
* // Hooks for standards
* import { getBOMEncoding, legacyHookDecode, labelToName, normalizeEncoding } from '@exodus/bytes/encoding.js'
* ```
*
* Implements the [Encoding standard](https://encoding.spec.whatwg.org/):
* [TextDecoder](https://encoding.spec.whatwg.org/#interface-textdecoder),
* [TextEncoder](https://encoding.spec.whatwg.org/#interface-textencoder),
* [TextDecoderStream](https://encoding.spec.whatwg.org/#interface-textdecoderstream),
* [TextEncoderStream](https://encoding.spec.whatwg.org/#interface-textencoderstream),
* some [hooks](https://encoding.spec.whatwg.org/#specification-hooks).
*
* @module @exodus/bytes/encoding.js
*/

/// <reference types="node" />

/**
* Converts an encoding label to its name, as an ASCII-lowercased string
* Convert an encoding [label](https://encoding.spec.whatwg.org/#names-and-labels) to its name,
* as an ASCII-lowercased string.
*
* If an encoding with that label does not exist, returns `null`.
*
* This is the same as [`decoder.encoding` getter](https://encoding.spec.whatwg.org/#dom-textdecoder-encoding),
* except that it:
* 1. Supports [`replacement` encoding](https://encoding.spec.whatwg.org/#replacement) and its
* [labels](https://encoding.spec.whatwg.org/#ref-for-replacement%E2%91%A1)
* 2. Does not throw for invalid labels and instead returns `null`
*
* It is identical to:
* ```js
* labelToName(label)?.toLowerCase() ?? null
* ```
*
* All encoding names are also valid labels for corresponding encodings.
*
* @param label - The encoding label to normalize
* @returns The normalized encoding name, or null if invalid
*/
export function normalizeEncoding(label: string): string | null;

/**
* Implements BOM sniff (https://encoding.spec.whatwg.org/#bom-sniff) legacy hook.
* Implements [BOM sniff](https://encoding.spec.whatwg.org/#bom-sniff) legacy hook.
*
* Given a `TypedArray` or an `ArrayBuffer` instance `input`, returns either of:
* - `'utf-8'`, if `input` starts with UTF-8 byte order mark.
* - `'utf-16le'`, if `input` starts with UTF-16LE byte order mark.
* - `'utf-16be'`, if `input` starts with UTF-16BE byte order mark.
* - `null` otherwise.
*
* @param input - The bytes to check for BOM
* @returns The encoding ('utf-8', 'utf-16le', 'utf-16be'), or null if no BOM found
*/
Expand All @@ -17,7 +60,26 @@ export function getBOMEncoding(
): 'utf-8' | 'utf-16le' | 'utf-16be' | null;

/**
* Implements decode (https://encoding.spec.whatwg.org/#decode) legacy hook.
* Implements [decode](https://encoding.spec.whatwg.org/#decode) legacy hook.
*
* Given a `TypedArray` or an `ArrayBuffer` instance `input` and an optional `fallbackEncoding`
* encoding [label](https://encoding.spec.whatwg.org/#names-and-labels),
* sniffs encoding from BOM with `fallbackEncoding` fallback and then
* decodes the `input` using that encoding, skipping BOM if it was present.
*
* Notes:
* - BOM-sniffed encoding takes precedence over `fallbackEncoding` option per spec.
* Use with care.
* - Always operates in non-fatal [mode](https://encoding.spec.whatwg.org/#textdecoder-error-mode),
* aka replacement. It can convert different byte sequences to equal strings.
*
* This method is similar to the following code, except that it doesn't support encoding labels and
* only expects lowercased encoding name:
*
* ```js
* new TextDecoder(getBOMEncoding(input) ?? fallbackEncoding).decode(input)
* ```
*
* @param input - The bytes to decode
* @param fallbackEncoding - The encoding to use if no BOM detected (default: 'utf-8')
* @returns The decoded string
Expand All @@ -28,31 +90,50 @@ export function legacyHookDecode(
): string;

/**
* Converts an encoding label to its name, as a case-sensitive string.
* Implements [get an encoding from a string `label`](https://encoding.spec.whatwg.org/#concept-encoding-get).
*
* Convert an encoding [label](https://encoding.spec.whatwg.org/#names-and-labels) to its name,
* as a case-sensitive string.
*
* If an encoding with that label does not exist, returns `null`.
*
* All encoding names are also valid labels for corresponding encodings.
*
* @param label - The encoding label
* @returns The proper case encoding name, or null if invalid
*/
export function labelToName(label: string): string | null;

/**
* Text decoder for decoding bytes to strings in various encodings
* Supports strict and lossy modes
* [TextDecoder](https://encoding.spec.whatwg.org/#interface-textdecoder) implementation/polyfill.
*
* Decode bytes to strings according to [WHATWG Encoding](https://encoding.spec.whatwg.org) specification.
*/
export const TextDecoder: typeof globalThis.TextDecoder;

/**
* Text encoder for encoding strings to UTF-8 bytes
* [TextEncoder](https://encoding.spec.whatwg.org/#interface-textencoder) implementation/polyfill.
*
* Encode strings to UTF-8 bytes according to [WHATWG Encoding](https://encoding.spec.whatwg.org) specification.
*/
export const TextEncoder: typeof globalThis.TextEncoder;

/**
* Transform stream wrapper for TextDecoder
* Decodes chunks of bytes to strings
* [TextDecoderStream](https://encoding.spec.whatwg.org/#interface-textdecoderstream) implementation/polyfill.
*
* A [Streams](https://streams.spec.whatwg.org/) wrapper for `TextDecoder`.
*
* Requires [Streams](https://streams.spec.whatwg.org/) to be either supported by the platform or
* [polyfilled](https://npmjs.com/package/web-streams-polyfill).
*/
export const TextDecoderStream: typeof globalThis.TextDecoderStream;

/**
* Transform stream wrapper for TextEncoder
* Encodes chunks of strings to UTF-8 bytes
* [TextEncoderStream](https://encoding.spec.whatwg.org/#interface-textencoderstream) implementation/polyfill.
*
* A [Streams](https://streams.spec.whatwg.org/) wrapper for `TextEncoder`.
*
* Requires [Streams](https://streams.spec.whatwg.org/) to be either supported by the platform or
* [polyfilled](https://npmjs.com/package/web-streams-polyfill).
*/
export const TextEncoderStream: typeof globalThis.TextEncoderStream;
28 changes: 21 additions & 7 deletions hex.d.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
/**
* Implements Base16 from [RFC4648](https://datatracker.ietf.org/doc/html/rfc4648)
* (no differences from [RFC3548](https://datatracker.ietf.org/doc/html/rfc4648)).
*
* ```js
* import { fromHex, toHex } from '@exodus/bytes/hex.js'
* ```
*
* @module @exodus/bytes/hex.js
*/

/// <reference types="node" />

import type { OutputFormat, Uint8ArrayBuffer } from './array.js';

/**
* Encodes a Uint8Array to a lowercase hex string
* Encode a `Uint8Array` to a lowercase hex string
*
* @param arr - The input bytes
* @returns The hex encoded string
*/
export function toHex(arr: Uint8ArrayBuffer): string;

/**
* Decodes a hex string to bytes
* Unlike Buffer.from(), throws on invalid input
* @param str - The hex encoded string (case-insensitive)
* Decode a hex string to bytes
*
* Unlike `Buffer.from()`, throws on invalid input
*
* @param string - The hex encoded string (case-insensitive)
* @param format - Output format (default: 'uint8')
* @returns The decoded bytes
*/
export function fromHex(str: string, format?: 'uint8'): Uint8ArrayBuffer;
export function fromHex(str: string, format: 'buffer'): Buffer;
export function fromHex(str: string, format?: OutputFormat): Uint8ArrayBuffer | Buffer;
export function fromHex(string: string, format?: 'uint8'): Uint8ArrayBuffer;
export function fromHex(string: string, format: 'buffer'): Buffer;
export function fromHex(string: string, format?: OutputFormat): Uint8ArrayBuffer | Buffer;
2 changes: 1 addition & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@
* import { getBOMEncoding, legacyHookDecode, labelToName, normalizeEncoding } from '@exodus/bytes/encoding-browser.js'
* ```
*/
declare module "@exodus/bytes" {}
declare module '@exodus/bytes' {}
Loading
Loading