Files
helper-tools/public/docs/API.md
T
zemion 6c763fcb5a
Verify / verify (push) Canceled after 0s
Release Helper Tools 0.2.0
2026-09-02 01:01:56 +02:00

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.