253 lines
9.9 KiB
Markdown
253 lines
9.9 KiB
Markdown
# Package API
|
|
|
|
`@add-ideas/toolbox-helpers` is a side-effect-free ESM package with generated
|
|
TypeScript declarations and no runtime dependency. Import named helpers from
|
|
the package root; internal deep paths are not public API.
|
|
|
|
## Bounds
|
|
|
|
```ts
|
|
type BytesLike = ArrayBuffer | ArrayBufferView;
|
|
|
|
const DEFAULT_HELPER_LIMITS: Readonly<{
|
|
maxInputBytes: number; // 16 MiB
|
|
maxTextChars: 2_000_000;
|
|
maxItems: 100_000;
|
|
maxDepth: 128;
|
|
}>;
|
|
|
|
assertBoundedText(value: string, maximum?: number, label?: string): string;
|
|
assertBoundedBytes(value: BytesLike, maximum?: number, label?: string): Uint8Array;
|
|
assertBoundedItems(count: number, maximum?: number, label?: string): number;
|
|
assertPositiveSafeInteger(value: number, label: string): number;
|
|
asUint8Array(value: BytesLike): Uint8Array;
|
|
|
|
interface ByteRange { offset: number; length: number; end: number }
|
|
assertSafeOffset(value: number, label?: string): number;
|
|
checkedOffsetAdd(offset: number, length: number, maximum?: number, label?: string): number;
|
|
checkedByteRange(availableBytes: number, offset: number, length: number, label?: string): ByteRange;
|
|
class ByteCursor { /* bounded typed reads, seek/skip and subcursors */ }
|
|
ownedBytes(input: BytesLike, maximumBytes?: number): Uint8Array;
|
|
```
|
|
|
|
`HelperLimitError` extends `RangeError` and exposes numeric `actual` and `limit`
|
|
properties. Per-operation ceilings can be made narrower. They are compatibility
|
|
and denial-of-service boundaries, not a substitute for an application-wide
|
|
memory budget.
|
|
|
|
`ByteCursor` defaults to the shared 16 MiB input ceiling and never advances
|
|
after a failed read. It is non-owning by default; `readBytes(length, true)`,
|
|
`bytes(true)` and bounded `ownedBytes` make explicit retained copies.
|
|
|
|
## Encoding and URLs
|
|
|
|
```ts
|
|
type TextEncoding = "utf-8" | "utf-16le" | "utf-16be" | "latin1";
|
|
interface DecodeBinaryOptions {
|
|
maxOutputBytes?: number;
|
|
allowWhitespace?: boolean;
|
|
}
|
|
|
|
bytesToBase64(input: BytesLike): string;
|
|
bytesToBase64Url(input: BytesLike, padded?: boolean): string;
|
|
base64ToBytes(input: string, options?: DecodeBinaryOptions): Uint8Array;
|
|
base64UrlToBytes(input: string, options?: DecodeBinaryOptions): Uint8Array;
|
|
bytesToHex(input: BytesLike, uppercase?: boolean): string;
|
|
hexToBytes(
|
|
input: string,
|
|
options?: DecodeBinaryOptions & { allowPrefix?: boolean },
|
|
): Uint8Array;
|
|
encodeText(input: string, encoding?: TextEncoding): Uint8Array;
|
|
decodeText(input: BytesLike, encoding?: TextEncoding, fatal?: boolean): string;
|
|
|
|
encodeUrlComponent(value: string): string;
|
|
decodeUrlComponent(value: string): string;
|
|
inspectUrl(value: string, base?: string): UrlParts;
|
|
parseFormEncoded(input: string, maximumFields?: number): FormEntries;
|
|
stringifyFormEncoded(entries: Iterable<readonly [string, string]>, maximumFields?: number): string;
|
|
```
|
|
|
|
Base64 decoders reject non-canonical alphabets, malformed padding and non-zero
|
|
unused bits. Base64URL accepts either no padding or the exact RFC 4648 padding
|
|
required for that value. URL inspection is inert: it parses text but never
|
|
opens or fetches the result.
|
|
|
|
## Checksums and digests
|
|
|
|
```ts
|
|
type DigestAlgorithm = "SHA-1" | "SHA-256" | "SHA-384" | "SHA-512";
|
|
type ChecksumAlgorithm = "CRC-32" | "Adler-32" | "FNV-1a-32";
|
|
|
|
interface IncrementalChecksum {
|
|
readonly algorithm: ChecksumAlgorithm;
|
|
readonly bytesProcessed: number;
|
|
update(input: BytesLike): this;
|
|
digest(): number;
|
|
digestHex(): string;
|
|
reset(): this;
|
|
}
|
|
|
|
createIncrementalChecksum(algorithm: ChecksumAlgorithm, maximumBytes?: number): IncrementalChecksum;
|
|
|
|
crc32(input: BytesLike, maximumBytes?: number): number;
|
|
adler32(input: BytesLike, maximumBytes?: number): number;
|
|
fnv1a32(input: BytesLike, maximumBytes?: number): number;
|
|
formatChecksum(value: number): string;
|
|
digestBytes(
|
|
input: BytesLike,
|
|
algorithm?: DigestAlgorithm,
|
|
maximumBytes?: number,
|
|
cryptoProvider?: Pick<Crypto, "subtle">,
|
|
): Promise<Uint8Array>;
|
|
digestHex(
|
|
input: BytesLike,
|
|
algorithm?: DigestAlgorithm,
|
|
maximumBytes?: number,
|
|
cryptoProvider?: Pick<Crypto, "subtle">,
|
|
): Promise<string>;
|
|
```
|
|
|
|
Digests use Web Crypto. SHA-1 is available only for interoperability. Checksums
|
|
and hashes do not establish authorship, and none of these functions is a
|
|
password-hashing API.
|
|
|
|
## Byte sources
|
|
|
|
```ts
|
|
type ByteSource = Blob | ReadableStream<BytesLike> | AsyncIterable<BytesLike>;
|
|
interface ByteSourceProgress { processedBytes: number; totalBytes?: number }
|
|
|
|
iterateByteChunks(source: ByteSource, options?: ConsumeByteSourceOptions): AsyncGenerator<Uint8Array>;
|
|
consumeByteSource(source: ByteSource, consume: (chunk: Uint8Array) => void | Promise<void>, options?: ConsumeByteSourceOptions): Promise<number>;
|
|
checksumByteSource(source: ByteSource, checksum?: ChecksumAlgorithm | IncrementalChecksum, options?: ConsumeByteSourceOptions): Promise<number>;
|
|
digestByteSource(source: ByteSource, algorithm?: DigestAlgorithm, options?: DigestByteSourceOptions): Promise<Uint8Array>;
|
|
digestByteSourceHex(source: ByteSource, algorithm?: DigestAlgorithm, options?: DigestByteSourceOptions): Promise<string>;
|
|
```
|
|
|
|
Every source is bounded and supports cancellation and progress. A supplied
|
|
`knownTotalBytes` is an exact contract rather than an unchecked progress hint.
|
|
CRC-32,
|
|
Adler-32 and FNV-1a are genuinely incremental and constant-memory. Web Crypto
|
|
does not expose incremental SHA state, so `digestByteSource` retains chunks up
|
|
to the caller's explicit ceiling before invoking its one-shot digest API.
|
|
|
|
## Network
|
|
|
|
```ts
|
|
type IpVersion = 4 | 6;
|
|
interface ParsedIpAddress {
|
|
version: IpVersion;
|
|
bytes: Uint8Array;
|
|
value: bigint;
|
|
canonical: string;
|
|
}
|
|
interface ParsedCidr {
|
|
version: IpVersion;
|
|
prefixLength: number;
|
|
address: ParsedIpAddress;
|
|
network: ParsedIpAddress;
|
|
first: ParsedIpAddress;
|
|
last: ParsedIpAddress;
|
|
broadcast?: ParsedIpAddress;
|
|
size: bigint;
|
|
canonical: string;
|
|
}
|
|
|
|
parseIpAddress(input: string): ParsedIpAddress;
|
|
parseIpv4(input: string): ParsedIpAddress;
|
|
parseIpv6(input: string): ParsedIpAddress;
|
|
formatIpv4(input: bigint | BytesLike): string;
|
|
formatIpv6(input: bigint | BytesLike): string;
|
|
parseCidr(input: string): ParsedCidr;
|
|
cidrContains(
|
|
cidr: string | ParsedCidr,
|
|
address: string | ParsedIpAddress,
|
|
): boolean;
|
|
```
|
|
|
|
IPv4 leading zeroes and IPv6 zone identifiers are rejected rather than guessed.
|
|
The helpers perform no DNS, HTTP, probing or scanning. `bigint` fields preserve
|
|
128-bit values exactly and must be converted explicitly before JSON encoding.
|
|
|
|
## Downloads
|
|
|
|
```ts
|
|
interface ObjectUrlLease {
|
|
readonly url: string;
|
|
readonly revoked: boolean;
|
|
revoke(): void;
|
|
}
|
|
|
|
sanitizeDownloadFilename(input: string, fallback?: string, maximumLength?: number): string;
|
|
createObjectUrlLease(
|
|
blob: Blob,
|
|
urlApi?: Pick<typeof URL, "createObjectURL" | "revokeObjectURL">,
|
|
): ObjectUrlLease;
|
|
createObjectUrlLeasePool<TKey = string>(urlApi?: UrlApi): ObjectUrlLeasePool<TKey>;
|
|
planBlobDownloads(input: Iterable<BlobDownload>, options?: PlanBlobDownloadsOptions): readonly PlannedBlobDownload[];
|
|
triggerBlobDownloads(input: Iterable<BlobDownload>, options?: TriggerBlobDownloadsOptions): TriggeredBlobDownloads;
|
|
triggerBlobDownload(
|
|
blob: Blob,
|
|
filename: string,
|
|
ownerDocument?: Document,
|
|
): ObjectUrlLease;
|
|
```
|
|
|
|
`triggerBlobDownload` removes its temporary anchor synchronously and schedules
|
|
object URL revocation for the next task. Long-lived previews should instead
|
|
keep a lease and call `revoke()` when replaced or unmounted.
|
|
|
|
Batch planning consumes at most the configured item ceiling, preserves input
|
|
order by default, sanitizes every name and adds
|
|
stable ` (2)`, ` (3)` suffixes for case-insensitive collisions. All batch URLs
|
|
are revocable as one group. Browsers may still ask users to permit multiple
|
|
downloads.
|
|
|
|
## Worker jobs
|
|
|
|
```ts
|
|
const WORKER_JOB_PROTOCOL = "add-ideas.worker-job/v1";
|
|
|
|
startWorkerJob<TPayload, TResult, TProgress, TError>(
|
|
worker: WorkerJobEndpoint,
|
|
payload: TPayload,
|
|
options?: StartWorkerJobOptions<TProgress, TError>,
|
|
): WorkerJobHandle<TResult>;
|
|
|
|
createWorkerJobMessageHandler<TPayload, TResult, TProgress, TError>(
|
|
run: (payload: TPayload, context: WorkerJobContext<TProgress>) => TResult | Promise<TResult>,
|
|
postMessage: (response: WorkerJobResponse<TResult, TProgress, TError>) => void,
|
|
options: WorkerJobHandlerOptions<TError>,
|
|
): (event: MessageEvent<WorkerJobCommand<TPayload>>) => void;
|
|
```
|
|
|
|
The client filters responses by protocol and job ID, forwards progress,
|
|
propagates abort reasons, enforces a hard timeout by terminating disposable
|
|
workers and removes all listeners when settled. Worker handlers expose the
|
|
same deadline and cancellation signal for cooperative asynchronous work.
|
|
|
|
## Display
|
|
|
|
```ts
|
|
formatBytes(bytes: number, options?: FormatBytesOptions): string;
|
|
```
|
|
|
|
Formatting is deterministic and defaults to IEC units. It supports SI units,
|
|
fixed precision, signed quantities and an explicit invalid-value label.
|
|
|
|
## Remaining groups
|
|
|
|
| Group | Exports |
|
|
| ----------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
| Unicode and text | `inspectCodePoints`, `fromCodePoints`, `segmentGraphemes`, `normalizeUnicode`, `transformCase`, `convertLineEndings` |
|
|
| Numbers and units | `parseBigIntRadix`, `formatBigIntRadix`, `convertNumberBase`, `convertDataUnit`, `convertUnit`, `unitDimension` |
|
|
| Time | `parseTimestamp`, `formatTimestamp`, `parseDuration`, `formatDuration` |
|
|
| Structured data | `safeJsonParse`, `stableStringify`, `parseCsv`, `stringifyCsv` |
|
|
| Random | `secureRandomBytes`, `secureRandomInt`, `createSeededRandom`, `shuffleSeeded` |
|
|
|
|
JSON dangerous object keys are rejected by default, cycles and unsupported
|
|
values are rejected by deterministic JSON, and CSV dimensions and fields are
|
|
bounded. Secure random functions use Web Crypto. The seeded generator is
|
|
reproducible and deliberately unsuitable for any secret, nonce, key, salt or
|
|
token.
|