first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
# @suaveplan/bitpacker-compressed - Bitpacker compressed plugin
## Summary
Compression pipeline integration for bitpacker.
<!-- begin:auto-docs -->
## Documentation tree
- [deflate](./src/adapters/deflate/deflate.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [deflate](./src/adapters/deflate/deflate.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [gzip](./src/adapters/gzip/gzip.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [gzip](./src/adapters/gzip/gzip.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [none](./src/adapters/none/none.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [none](./src/adapters/none/none.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [api](./src/api/api.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [api](./src/api/api.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [compressed-bit-packer](./src/compressed-bit-packer/compressed-bit-packer.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - compressed-bit-packer.schema — Zod schema for `Comp…
- [compressed-bit-packer](./src/compressed-bit-packer/compressed-bit-packer.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - compressed-bit-packer.schema — Zod schema for `Comp…
- [compressed-bit-packer.schema](./src/compressed-bit-packer/compressed-bit-packer.schema.md) — Zod schema for `CompressedBitPackerOptions`, the constructor options bundle accepted by `CompressedBitPacker`. Validate…
- [otel](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [instruments](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [register-schemas](./src/register-schemas/register-schemas.md) — Idempotent integration with `@suaveplan/schema-registry` for the `@suaveplan/bitpacker-compressed` package. Exposes `re…
- [register-schemas](./src/register-schemas/register-schemas.md) — Idempotent integration with `@suaveplan/schema-registry` for the `@suaveplan/bitpacker-compressed` package. Exposes `re…
- [registry](./src/registry/registry.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [registry](./src/registry/registry.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [rle](./src/rle/rle.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - rle.schema — Zod schema for the per-call `Compressi…
- [rle](./src/rle/rle.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - rle.schema — Zod schema for the per-call `Compressi…
- [rle.schema](./src/rle/rle.schema.md) — Zod schema for the per-call `CompressionOptions` accepted by `compressRle` / `decompressRle`. The RLE codec is byte-onl…
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - types.schema — Zod schemas for the public type comp…
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - types.schema — Zod schemas for the public type comp…
- [types.schema](./src/types/types.schema.md) — Zod schemas for the public type companions of `@suaveplan/bitpacker-compressed`'s compression pipeline: the open-record…
- [index](./src/index.md) — **Path:** `bitpacker-compressed/src/index.ts` **Export:** See source for details.
<!-- end:auto-docs --> - [deflate](./src/adapters/deflate/deflate.md) — <!-- begin:auto-docs --> _(no submodules)_- [gzip](./src/adapters/gzip/gzip.md) — <!-- begin:auto-docs --> _(no submodules)_- [gzip](./src/adapters/gzip/gzip.md) — <!-- begin:auto-docs --> _(no submodules)_- [none](./src/adapters/none/none.md) — <!-- begin:auto-docs --> _(no submodules)_- [none](./src/adapters/none/none.md) — <!-- begin:auto-docs --> _(no submodules)_- [api](./src/api/api.md) — <!-- begin:auto-docs --> _(no submodules)_- [api](./src/api/api.md) — <!-- begin:auto-docs --> _(no submodules)_- [compressed-bit-packer](./src/compressed-bit-packer/compressed-bit-packer.md) — <!-- begin:auto-docs --> _(no submodules)_- compressed-bit-packer.schema — Zod schema for `Comp…
- [compressed-bit-packer](./src/compressed-bit-packer/compressed-bit-packer.md) — <!-- begin:auto-docs --> _(no submodules)_- compressed-bit-packer.schema — Zod schema for `Comp…
- [compressed-bit-packer.schema](./src/compressed-bit-packer/compressed-bit-packer.schema.md) — Zod schema for `CompressedBitPackerOptions`, the constructor options bundle accepted by `CompressedBitPacker`. Validate…
- [otel](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [registry](./src/registry/registry.md) — <!-- begin:auto-docs --> _(no submodules)_- [registry](./src/registry/registry.md) — <!-- begin:auto-docs --> _(no submodules)_- [rle](./src/rle/rle.md) — <!-- begin:auto-docs --> _(no submodules)_- rle.schema — Zod schema for the per-call `Compressi…
- [rle](./src/rle/rle.md) — <!-- begin:auto-docs --> _(no submodules)_- rle.schema — Zod schema for the per-call `Compressi…
- [rle.schema](./src/rle/rle.schema.md) — Zod schema for the per-call `CompressionOptions` accepted by `compressRle` / `decompressRle`. The RLE codec is byte-onl…
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- types.schema — Zod schemas for the public type comp…
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- types.schema — Zod schemas for the public type comp…
- [types.schema](./src/types/types.schema.md) — Zod schemas for the public type companions of `@suaveplan/bitpacker-compressed`'s compression pipeline: the open-record…
- [index](./src/index.md) — **Path:** `bitpacker-compressed/src/index.ts` **Export:** See source for details.
- [register-schemas](./src/register-schemas.md) — Idempotent integration with `@suaveplan/schema-registry` for the `@suaveplan/bitpacker-compressed` package. Exposes `re…
- [deflate](./src/adapters/deflate/deflate.md) — <!-- begin:auto-docs --> _(no submodules)_- [gzip](./src/adapters/gzip/gzip.md) — <!-- begin:auto-docs --> _(no submodules)_- [gzip](./src/adapters/gzip/gzip.md) — <!-- begin:auto-docs --> _(no submodules)_- [none](./src/adapters/none/none.md) — <!-- begin:auto-docs --> _(no submodules)_- [none](./src/adapters/none/none.md) — <!-- begin:auto-docs --> _(no submodules)_- [api](./src/api/api.md) — <!-- begin:auto-docs --> _(no submodules)_- [api](./src/api/api.md) — <!-- begin:auto-docs --> _(no submodules)_- [compressed-bit-packer](./src/compressed-bit-packer/compressed-bit-packer.md) — <!-- begin:auto-docs --> _(no submodules)_- compressed-bit-packer.schema — Zod schema for `Comp…
- [compressed-bit-packer](./src/compressed-bit-packer/compressed-bit-packer.md) — <!-- begin:auto-docs --> _(no submodules)_- compressed-bit-packer.schema — Zod schema for `Comp…
- [compressed-bit-packer.schema](./src/compressed-bit-packer/compressed-bit-packer.schema.md) — Zod schema for `CompressedBitPackerOptions`, the constructor options bundle accepted by `CompressedBitPacker`. Validate…
- [otel](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [registry](./src/registry/registry.md) — <!-- begin:auto-docs --> _(no submodules)_- [registry](./src/registry/registry.md) — <!-- begin:auto-docs --> _(no submodules)_- [rle](./src/rle/rle.md) — <!-- begin:auto-docs --> _(no submodules)_- rle.schema — Zod schema for the per-call `Compressi…
- [rle](./src/rle/rle.md) — <!-- begin:auto-docs --> _(no submodules)_- rle.schema — Zod schema for the per-call `Compressi…
- [rle.schema](./src/rle/rle.schema.md) — Zod schema for the per-call `CompressionOptions` accepted by `compressRle` / `decompressRle`. The RLE codec is byte-onl…
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- types.schema — Zod schemas for the public type comp…
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- types.schema — Zod schemas for the public type comp…
- [types.schema](./src/types/types.schema.md) — Zod schemas for the public type companions of `@suaveplan/bitpacker-compressed`'s compression pipeline: the open-record…
- [index](./src/index.md) — **Path:** `bitpacker-compressed/src/index.ts` **Export:** See source for details.
- [register-schemas](./src/register-schemas.md) — Idempotent integration with `@suaveplan/schema-registry` for the `@suaveplan/bitpacker-compressed` package. Exposes `re…
- [deflate](./src/adapters/deflate/deflate.md) — <!-- begin:auto-docs --> _(no submodules)_- [gzip](./src/adapters/gzip/gzip.md) — <!-- begin:auto-docs --> _(no submodules)_- [gzip](./src/adapters/gzip/gzip.md) — <!-- begin:auto-docs --> _(no submodules)_- [none](./src/adapters/none/none.md) — <!-- begin:auto-docs --> _(no submodules)_- [none](./src/adapters/none/none.md) — <!-- begin:auto-docs --> _(no submodules)_- [api](./src/api/api.md) — <!-- begin:auto-docs --> _(no submodules)_- [api](./src/api/api.md) — <!-- begin:auto-docs --> _(no submodules)_- [compressed-bit-packer](./src/compressed-bit-packer/compressed-bit-packer.md) — <!-- begin:auto-docs --> _(no submodules)_- compressed-bit-packer.schema — Zod schema for `Comp…
- [compressed-bit-packer](./src/compressed-bit-packer/compressed-bit-packer.md) — <!-- begin:auto-docs --> _(no submodules)_- compressed-bit-packer.schema — Zod schema for `Comp…
- [compressed-bit-packer.schema](./src/compressed-bit-packer/compressed-bit-packer.schema.md) — Zod schema for `CompressedBitPackerOptions`, the constructor options bundle accepted by `CompressedBitPacker`. Validate…
- [otel](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [instruments](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [registry](./src/registry/registry.md) — <!-- begin:auto-docs --> _(no submodules)_- [registry](./src/registry/registry.md) — <!-- begin:auto-docs --> _(no submodules)_- [rle](./src/rle/rle.md) — <!-- begin:auto-docs --> _(no submodules)_- rle.schema — Zod schema for the per-call `Compressi…
- [rle](./src/rle/rle.md) — <!-- begin:auto-docs --> _(no submodules)_- rle.schema — Zod schema for the per-call `Compressi…
- [rle.schema](./src/rle/rle.schema.md) — Zod schema for the per-call `CompressionOptions` accepted by `compressRle` / `decompressRle`. The RLE codec is byte-onl…
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- types.schema — Zod schemas for the public type comp…
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_- types.schema — Zod schemas for the public type comp…
- [types.schema](./src/types/types.schema.md) — Zod schemas for the public type companions of `@suaveplan/bitpacker-compressed`'s compression pipeline: the open-record…
- [index](./src/index.md) — **Path:** `bitpacker-compressed/src/index.ts` **Export:** See source for details.
- [register-schemas](./src/register-schemas.md) — Idempotent integration with `@suaveplan/schema-registry` for the `@suaveplan/bitpacker-compressed` package. Exposes `re…
- [deflate](./src/adapters/deflate/deflate.md) — <!-- begin:auto-docs --> _(no submodules)_- [gzip](./src/adapters/gzip/gzip.md) — <!-- begin:auto-docs --> _(no submodules)_ - [gzip](./src/adapters/gzip/gzip.md) — <!-- begin:auto-docs --> _(no submodules)_- [none](./src/adapters/none/none.md) — <!-- begin:auto-docs --> _(no submodules)_ - [none](./src/adapters/none/none.md) — <!-- begin:auto-docs --> _(no submodules)_- [api](./src/api/api.md) — <!-- begin:auto-docs --> _(no submodules)_ - [api](./src/api/api.md) — <!-- begin:auto-docs --> _(no submodules)_- [compressed-bit-packer](./src/compressed-bit-packer/compressed-bit-packer.md) — <!-- begin:auto-docs --> _(no submodules)_ - [compressed-bit-packer](./src/compressed-bit-packer/compressed-bit-packer.md) — <!-- begin:auto-docs --> _(no submodules)_ - [compressed-bit-packer.schema](./src/compressed-bit-packer/compressed-bit-packer.schema.md) — Zod schema for `CompressedBitPackerOptions`, the constructor options bundle accepted by `CompressedBitPacker`. Validate…
- [otel](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_ - [instruments](./src/otel/instruments.md) — <!-- begin:auto-docs --> _(no submodules)_- [registry](./src/registry/registry.md) — <!-- begin:auto-docs --> _(no submodules)_ - [registry](./src/registry/registry.md) — <!-- begin:auto-docs --> _(no submodules)_- [rle](./src/rle/rle.md) — <!-- begin:auto-docs --> _(no submodules)_ - [rle](./src/rle/rle.md) — <!-- begin:auto-docs --> _(no submodules)_ - [rle.schema](./src/rle/rle.schema.md) — Zod schema for the per-call `CompressionOptions` accepted by `compressRle` / `decompressRle`. The RLE codec is byte-onl…
- [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ - [types](./src/types/types.md) — <!-- begin:auto-docs --> _(no submodules)_ - [types.schema](./src/types/types.schema.md) — Zod schemas for the public type companions of `@suaveplan/bitpacker-compressed`'s compression pipeline: the open-record…
- [index](./src/index.md) — **Path:** `bitpacker-compressed/src/index.ts` **Export:** See source for details.
- [register-schemas](./src/register-schemas.md) — Idempotent integration with `@suaveplan/schema-registry` for the `@suaveplan/bitpacker-compressed` package. Exposes `re…
## Install
```bash
bun add @suaveplan/bitpacker-compressed
```
## Usage
```typescript
import { compress, decompress } from "@suaveplan/bitpacker-compressed";
const data = new Uint8Array([1, 2, 3, 4, 5]);
const compressed = compress(data);
const restored = decompress(compressed);
```
## Modules
- `src/index.ts` — `compress`, `decompress`

View File

@@ -0,0 +1,54 @@
# DeflateCompressionAdapter
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`DeflateCompressionAdapter` is the WHATWG Streams-backed deflate codec for `@suaveplan/bitpacker-compressed`. It mirrors `GzipCompressionAdapter` exactly but switches the underlying stream to `CompressionStream("deflate")` / `DecompressionStream("deflate")`. The chunk-concatenation pump is shared with the gzip adapter via `_runStreamForTests` so the two adapters do not duplicate the same loop. Use this when you need raw deflate (no gzip header) — typically for inter-op with consumers that expect that wire format (HTTP `Content-Encoding: deflate`, certain binary protocols, etc.).
## Features
- Drop-in `CompressionAdapter` implementation registered as `"deflate"` in the default registry.
- Re-uses the gzip adapter's streaming pump for chunk concatenation, eliminating duplicated logic.
- Same coded-error treatment as gzip: `COMPRESSION_FAILED` / `DECOMPRESSION_FAILED` with `cause`.
- Identical async signature to all other adapters so swapping codecs is a `registry.resolve(otherName)` change.
## Usage: Basic Example
```ts
import { DeflateCompressionAdapter } from "@suaveplan/bitpacker-compressed";
const deflate = new DeflateCompressionAdapter();
const compressed = await deflate.compress(payload);
const restored = await deflate.decompress(compressed);
```
## Usage: Advanced Example
```ts
import {
compressData,
decompressData,
} from "@suaveplan/bitpacker-compressed";
// HTTP body that arrived with Content-Encoding: deflate.
const body = await req.arrayBuffer();
const decoded = await decompressData(new Uint8Array(body), "deflate");
```
## API Reference
### `class DeflateCompressionAdapter implements CompressionAdapter`
- `readonly name: "deflate"`
- `async compress(data: Uint8Array, options?: { level?: number }): Promise<Uint8Array>`
- `async decompress(data: Uint8Array, options?: { level?: number }): Promise<Uint8Array>`
**Throws:** `BitpackerError(COMPRESSION_FAILED, { cause })` / `BitpackerError(DECOMPRESSION_FAILED, { cause })` on stream failure or environments without `globalThis.CompressionStream`.
## Implementation Notes
- "Deflate" here means raw RFC 1951 (no zlib header). If you need the zlib-wrapped variant, the platform `CompressionStream("deflate")` already produces it on most browsers — this is a known compatibility nuance and callers MUST be aware of which side they need.
- The shared `_runStreamForTests` helper is intentionally underscore-prefixed to mark it as adapter-internal; consumers should not import it directly.
- WHATWG `CompressionStream` ignores the `level` option for deflate the same way it does for gzip.

View File

@@ -0,0 +1,56 @@
# GzipCompressionAdapter
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`GzipCompressionAdapter` is the WHATWG Streams-backed gzip codec. It uses `globalThis.CompressionStream("gzip")` and `DecompressionStream("gzip")` — available in modern browsers, Bun, Deno, and Node 18+. The adapter exists so callers don't have to reach into Web Streams APIs directly: the pipeline framing, error coding, and chunk-concatenation loop are all handled here so `compressData(data, "gzip")` is a one-liner. If the host environment lacks `CompressionStream`, the registry's `defaultCompressionRegistry()` skips registering this adapter; constructing one directly and calling `compress` in such a host throws `BitpackerError(COMPRESSION_FAILED)` with a descriptive cause chain.
## Features
- Drop-in `CompressionAdapter` implementation registered as `"gzip"` in the default registry.
- Streaming pump under the hood — large payloads are chunked through `CompressionStream` rather than buffered whole.
- Coded errors (`COMPRESSION_FAILED` / `DECOMPRESSION_FAILED`) wrap any underlying stream exception with `cause`.
- Re-exported `_runStreamForTests` helper used by `DeflateCompressionAdapter` to share the chunk-concatenation loop.
## Usage: Basic Example
```ts
import { GzipCompressionAdapter } from "@suaveplan/bitpacker-compressed";
const gzip = new GzipCompressionAdapter();
const compressed = await gzip.compress(myBytes);
const restored = await gzip.decompress(compressed);
```
## Usage: Advanced Example
```ts
import { defaultCompressionRegistry } from "@suaveplan/bitpacker-compressed";
// Production code typically goes through the registry.
const registry = defaultCompressionRegistry();
const adapter = registry.resolve("gzip");
const out = await adapter.compress(payload, { level: 9 });
```
## API Reference
### `class GzipCompressionAdapter implements CompressionAdapter`
- `readonly name: "gzip"`
- `async compress(data: Uint8Array, options?: { level?: number }): Promise<Uint8Array>`
- `async decompress(data: Uint8Array, options?: { level?: number }): Promise<Uint8Array>`
**Throws:** `BitpackerError(COMPRESSION_FAILED, { cause })` / `BitpackerError(DECOMPRESSION_FAILED, { cause })` if the underlying `CompressionStream` rejects, including environments without `globalThis.CompressionStream`.
### `_runStreamForTests(stream, data): Promise<Uint8Array>` (internal)
Exposed for the deflate adapter to re-use the pump loop. Not part of the public API.
## Implementation Notes
- WHATWG `CompressionStream` does not honour the `level` option — gzip level is fixed at the platform default. The option is accepted for API symmetry with adapters that DO honour it (e.g. a future `BrotliAdapter`).
- The pump loop uses `ReadableStream.prototype.getReader()` and concatenates chunks into a single `Uint8Array` at the end. For very large payloads, callers should consider invoking `CompressionStream` directly to get backpressure-aware streaming output.
- Every async method awaits inside a try/catch and re-throws as a coded `BitpackerError`. There is no silent fallback — if gzip fails, the caller sees a fully-typed coded error.

View File

@@ -0,0 +1,55 @@
# NoCompressionAdapter
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`NoCompressionAdapter` is the pass-through codec used as the default when `CompressionConfig.algorithm` is `"none"` or omitted. It exists so that `compressData`, `decompressData`, and `CompressedBitPacker` can ALWAYS route through the compression pipeline without special-casing the "compression disabled" branch — the bytes flow through `compress` and `decompress` calls that resolve immediately with the input. This keeps the framing logic in `CompressedBitPacker` uniform: every payload has the same header shape regardless of whether real compression occurred.
## Features
- Pass-through `compress` / `decompress` — returns the input `Uint8Array` reference unchanged.
- Drop-in `CompressionAdapter` implementation registered as `"none"` in the default registry.
- Always available — does not depend on `CompressionStream` and never throws.
- Default fallback when no algorithm is specified.
## Usage: Basic Example
```ts
import { NoCompressionAdapter } from "@suaveplan/bitpacker-compressed";
const none = new NoCompressionAdapter();
const same = await none.compress(myBytes); // myBytes itself
```
## Usage: Advanced Example
```ts
import {
compressData,
defaultCompressionRegistry,
} from "@suaveplan/bitpacker-compressed";
// "none" routes through the pipeline so framing stays uniform with
// gzip / deflate. Useful in tests that want to assert framing is
// orthogonal to the codec.
const out = await compressData(payload, "none");
const adapter = defaultCompressionRegistry().resolve("none");
```
## API Reference
### `class NoCompressionAdapter implements CompressionAdapter`
- `readonly name: "none"`
- `async compress(data: Uint8Array): Promise<Uint8Array>` — returns `data` unchanged.
- `async decompress(data: Uint8Array): Promise<Uint8Array>` — returns `data` unchanged.
The `options` parameter is accepted for interface symmetry but ignored.
## Implementation Notes
- The returned reference is the SAME `Uint8Array` instance as the input — the adapter does not copy. Callers that need an independent buffer should `slice()` separately.
- This adapter is unconditionally registered in `defaultCompressionRegistry()`. The other two defaults (`"gzip"`, `"deflate"`) are gated on `CompressionStream` availability; "none" always works.
- Tests that need to prove "compression is opt-in" use this adapter to confirm the pipeline behaves identically when the caller chose to skip real compression.

View File

@@ -0,0 +1,62 @@
# compression API (`compressData` / `decompressData`)
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`compressData` and `decompressData` are the primary public surface of `@suaveplan/bitpacker-compressed`. They are algorithm-by-name helpers that route through a `CompressionRegistry` (defaulting to the process-wide singleton) so callers can switch between `"gzip"`, `"deflate"`, `"none"`, and any custom registered adapter without touching adapter classes directly. They are the layer that hot-path consumers like `CompressedBitPacker` use; it is also the layer that wraps codec failures in `BitpackerError` codes so application code never has to handle a raw `DOMException` or platform-specific stream error.
## Features
- Algorithm-by-name routing through a pluggable `CompressionRegistry`.
- Coded errors: unknown algorithm → `UNKNOWN_COMPRESSION_ALGORITHM`, codec failure → `COMPRESSION_FAILED` / `DECOMPRESSION_FAILED` with the underlying error chained.
- Optional registry parameter — pass a custom `CompressionRegistry` for isolation in tests.
- Symmetric: `compressData(data, "gzip")` round-trips through `decompressData(bytes, "gzip")`.
## Usage: Basic Example
```ts
import { compressData, decompressData } from "@suaveplan/bitpacker-compressed";
const data = new TextEncoder().encode("hello world".repeat(100));
const compressed = await compressData(data, "gzip");
const restored = await decompressData(compressed, "gzip");
```
## Usage: Advanced Example
```ts
import {
compressData,
createCompressionRegistry,
} from "@suaveplan/bitpacker-compressed";
// Test isolation: create a fresh registry that ONLY has the "none" adapter
// registered, then prove the unknown-algorithm path throws coded errors.
const registry = createCompressionRegistry({ withDefaults: false });
try {
await compressData(data, "gzip", { registry });
} catch (err) {
// BitpackerError with code BITPACKER.UNKNOWN_COMPRESSION_ALGORITHM
}
```
## API Reference
### `compressData(data, algorithm, options?): Promise<Uint8Array>`
- `data: Uint8Array` — bytes to compress.
- `algorithm: string` — registry key (e.g. `"gzip"`, `"deflate"`, `"none"`).
- `options?: { registry?, level? }` — `registry` overrides the default singleton; `level` is forwarded to the adapter.
- **Throws:** `BitpackerError(UNKNOWN_COMPRESSION_ALGORITHM)` or `BitpackerError(COMPRESSION_FAILED, { cause })`.
### `decompressData(data, algorithm, options?): Promise<Uint8Array>`
- Inverse of `compressData`. Throws `BitpackerError(DECOMPRESSION_FAILED, { cause })` on codec failure.
## Implementation Notes
- The default singleton is initialized lazily; it registers `"none"`, `"gzip"`, `"deflate"` only if the host supports `CompressionStream`.
- Adapters are async because `CompressionStream` is async; `NoCompressionAdapter` returns immediately.
- Hot-path callers should resolve the adapter once and call it directly to avoid the per-call registry lookup.

View File

@@ -0,0 +1,122 @@
# CompressedBitPacker
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs --> - compressed-bit-packer.schema — Zod schema for `Comp…
- [compressed-bit-packer.schema](./compressed-bit-packer.schema.md) — Zod schema for `CompressedBitPackerOptions`, the constructor options bundle accepted by `CompressedBitPacker`. Validate…
- compressed-bit-packer.schema — Zod schema for `Comp…
- [compressed-bit-packer.schema](./compressed-bit-packer.schema.md) — Zod schema for `CompressedBitPackerOptions`, the constructor options bundle accepted by `CompressedBitPacker`. Validate…
- compressed-bit-packer.schema — Zod schema for `Comp…
- [compressed-bit-packer.schema](./compressed-bit-packer.schema.md) — Zod schema for `CompressedBitPackerOptions`, the constructor options bundle accepted by `CompressedBitPacker`. Validate…
- compressed-bit-packer.schema — Zod schema for `Comp…
- [compressed-bit-packer.schema](./compressed-bit-packer.schema.md) — Zod schema for `CompressedBitPackerOptions`, the constructor options bundle accepted by `CompressedBitPacker`. Validate…
- [compressed-bit-packer.schema](./compressed-bit-packer.schema.md) — Zod schema for `CompressedBitPackerOptions`, the constructor options bundle accepted by `CompressedBitPacker`. Validate…
## Purpose
`CompressedBitPacker<T>` extends `@suaveplan/bitpacker::BitPacker<T>` to add a configurable compression stage between bit-packing and the wire bytes. The compressed bytes are framed with a self-describing algorithm header so a receiver can decompress without knowing the sender's algorithm choice in advance — the byte stream itself names the codec.
This is the port of upstream `bitpacker/src/compressed/bitpacker.ts`. The port keeps the upstream wire shape (`[headerLen: u8][header: ASCII algorithm][compressed payload]`) so any consumer carrying upstream-encoded bytes round-trips unchanged. Hardening over upstream:
- Plain `Error` throws → `BitpackerError` with stable codes (`UNKNOWN_COMPRESSION_ALGORITHM`, `COMPRESSION_FAILED`, `DECOMPRESSION_FAILED`, `COMPRESSED_BAD_HEADER`).
- The synchronous `pack`/`unpack` overrides from upstream — which threw when compression was enabled — are dropped. Consumers call the explicit async `packWithCompression` / `unpackWithDecompression` methods. This separates the sync no-compression hot path from the async compressed path cleanly.
- Adapter resolution goes through the injected `CompressionRegistry`, not a private static singleton.
- No `EventEmitter`. suaveplan's `BitPacker` does not extend an emitter; observability is OTel-driven via `bitpacker-compressed.*` spans.
## Features
- `packWithCompression(data, schema?)` — bit-pack, compress, frame.
- `unpackWithDecompression(data, schema?)` — read frame, decompress, bit-unpack.
- Configurable algorithm + options + enabled flag via constructor `compression` config or runtime accessors.
- Self-describing wire format: receiver dispatches to the named algorithm without prior knowledge.
- Synchronous `pack`/`unpack` from `BitPacker` remain for the no-compression case (no header added — bytes are exactly what `BitPacker.pack` produces).
- Pluggable `CompressionRegistry` via constructor option (defaults to the process-wide singleton).
## Usage: Basic Example
```typescript
import { CompressedBitPacker } from "@suaveplan/bitpacker-compressed";
const schema = { x: 255, y: 255 } as const;
const packer = new CompressedBitPacker(schema);
// Compression disabled (default) — frames with "none" header
const bytes = await packer.packWithCompression({ x: 42, y: 99 });
const decoded = await packer.unpackWithDecompression(bytes);
// → { x: 42, y: 99 }
```
## Usage: Advanced Example
```typescript
import {
CompressedBitPacker,
createCompressionRegistry,
} from "@suaveplan/bitpacker-compressed";
// Custom registry isolated from process-wide singleton, useful for tests
// or multi-tenant servers where adapter sets must not leak.
const registry = createCompressionRegistry();
// Register a custom adapter (e.g. brotli) into this isolated registry.
registry.register(new MyBrotliAdapter());
const packer = new CompressedBitPacker(
{
position: { x: "f32", y: "f32", z: "f32" },
velocity: { x: "f32", y: "f32", z: "f32" },
},
{
compression: {
algorithm: "brotli",
enabled: true,
options: { quality: 11 },
},
compressionRegistry: registry,
}
);
const wireBytes = await packer.packWithCompression({
position: { x: 1, y: 2, z: 3 },
velocity: { x: 0.1, y: 0, z: 0 },
});
// wireBytes is [headerLen=6][b][r][o][t][l][i][brotli-compressed bit-packed payload]
```
## API Reference
### `new CompressedBitPacker<T>(schema, options?)`
`schema` — a `BitPackerSchema` describing the data shape.
`options.compression?` — `{ algorithm?, options?, enabled? }`. Default is `{ algorithm: "none", options: {}, enabled: false }`.
`options.compressionRegistry?` — optional `CompressionRegistry` override. Defaults to `defaultCompressionRegistry()`.
### `packWithCompression(data, schema?) → Promise<Uint8Array>`
Bit-packs `data`, runs the bytes through the configured compression adapter (or `NoCompressionAdapter` if `enabled` is false or `algorithm` is `"none"`), and frames the result with `[headerLen: u8][header: ASCII algorithm name][payload]`. Returns the framed wire bytes.
### `unpackWithDecompression(data?, schema?) → Promise<unknown>`
Reverse of `packWithCompression`. Reads the framing header, dispatches to the named adapter for decompression (or routes the body through unchanged if the header is `"none"`), then bit-unpacks the result. If `data` is `undefined` or empty, falls back to `super.unpack(empty)` for parity with upstream.
### Accessors
`compressionAlgorithm` / `compressionOptions` / `compressionEnabled` — getters and setters for the runtime configuration. Allow callers to switch algorithms or toggle compression mid-flight.
### Inherited from `BitPacker`
Synchronous `pack(data, schema?) → Uint8Array` and `unpack(bytes, schema?) → T` remain available. They produce/consume bytes that are NOT framed (no `[headerLen][header]` prefix) — use them when compression is genuinely off and you don't want the framing overhead.
## Implementation Notes
The wire format's `headerLen` byte limits algorithm names to 1–255 ASCII bytes. The four shipping defaults (`none`, `gzip`, `deflate`, plus future `brotli`/`zstd`) are all well under that. If a future custom algorithm name exceeds the limit, register the adapter under a shorter alias.
The synchronous `pack`/`unpack` from `BitPacker` are NOT overridden here. The upstream version overrode them but threw an error when compression was enabled — that mixed sync and async expectations confusingly. suaveplan splits cleanly: callers wanting compression call the async methods; callers OK with no compression use the inherited sync methods.
`CompressionRegistry` is injectable so tests can use isolated registries (`createCompressionRegistry()`) without polluting the process-wide singleton. The lazy registration of `gzip`/`deflate` on the singleton (when `CompressionStream` is available) means the singleton is environment-aware without forcing every consumer through the streams API path.
The `compressed-bit-packer.test.ts` test file uses `vitest.skipIf(!hasStreams)` to skip stream-backed tests in environments without `CompressionStream`. The frame/unframe helpers are tested directly via `_frameForTests` / `_unframeForTests` so the wire format is locked even in non-streaming environments.

View File

@@ -0,0 +1,65 @@
# Compressed BitPacker Options Schema
## Purpose
Zod schema for `CompressedBitPackerOptions`, the constructor options bundle accepted by `CompressedBitPacker`. Validates the shape at the package boundary so a typo in the algorithm name (`alogrithm`), a non-`CompressionRegistry` value passed as the registry (a plain object that "quacks like" one), or a stray unknown key all fail at parse time rather than producing a packer that silently runs without the consumer's intended compression configuration.
The schema composes `compressionConfigSchema` for the `compression` field and uses an `instanceof` check for `compressionRegistry`. Identity of the registry is preserved so the constructor receives the same instance the caller built.
## Features
- `compressedBitPackerOptionsSchema` — strict, two optional fields (`compression`, `compressionRegistry`)
- Composes `compressionConfigSchema` for nested validation
- `instanceof CompressionRegistry` check with identity preservation
- `as unknown as z.ZodType<T>` cast for the EOPT mismatch
## Usage: Basic Example
```ts
import {
CompressedBitPacker,
compressedBitPackerOptionsSchema,
} from "@suaveplan/bitpacker-compressed";
const opts = compressedBitPackerOptionsSchema.parse({
compression: { algorithm: "gzip", enabled: true },
});
const packer = new CompressedBitPacker({ x: 255 }, opts);
```
## Usage: Advanced Example
```ts
import {
CompressedBitPacker,
compressedBitPackerOptionsSchema,
createCompressionRegistry,
GzipCompressionAdapter,
} from "@suaveplan/bitpacker-compressed";
const registry = createCompressionRegistry();
registry.register(new GzipCompressionAdapter());
const opts = compressedBitPackerOptionsSchema.parse({
compression: {
algorithm: "gzip",
options: { level: 9 },
enabled: true,
},
compressionRegistry: registry,
});
const packer = new CompressedBitPacker({ x: 255 }, opts);
```
## API Reference
### `compressedBitPackerOptionsSchema: z.ZodType<CompressedBitPackerOptions>`
Strict schema. `compression` is an optional `CompressionConfig` (validated by `compressionConfigSchema`). `compressionRegistry` is an optional `CompressionRegistry` instance (validated by `instanceof` with identity preservation).
## Implementation Notes
- **Composition over duplication.** The nested `compression` field is validated by `compressionConfigSchema` from the same package. Composing rather than inlining the shape keeps the per-field error messages consistent across all entry points that accept a `CompressionConfig`.
- **`instanceof` over duck-typing.** A duck-typed registry would compile but would not pass the prototype-aware checks the registry's lookup methods perform internally. Refusing duck-types prevents an entire class of "registry doesn't find my adapter" bugs.
- **`.strict()` is non-negotiable.** Same rationale as elsewhere in this change: typo'd flags would otherwise silently default and hide configuration bugs.

View File

@@ -0,0 +1,8 @@
## index
**Path:** `bitpacker-compressed/src/index.ts`
**Export:** See source for details.
### What it does
Part of the @suaveplan/ package.

View File

@@ -0,0 +1,83 @@
# Bitpacker Compressed OTel Instruments
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
OTel meter, tracer, counter, and histogram declarations for `@suaveplan/bitpacker-compressed`. Ships canonical instrumentation primitives for the compression/decompression pipeline (`gzip`, `deflate`, `none`, `rle`, future custom adapters) plus the `CompressedBitPacker.pack` / `unpack` boundaries that wrap the compression step. Every operation can be observed with the same vocabulary regardless of which adapter is selected.
The metric and span names follow the suaveplan convention with snake-case for the multi-word package: tracer/meter named `@suaveplan/bitpacker-compressed`; spans named `bitpacker_compressed.<operation>`; counters/histograms named `bitpacker_compressed.<scope>.<metric>`.
## Features
- `getBitPackerCompressedTracer()` — returns the package's tracer
- `compressionOpsTotal` — counter labelled by `op`, `algorithm`, `success`
- `compressionOpDurationMs` — histogram of compress/decompress duration
- `compressionRatio` — histogram of `output_bytes / input_bytes` per successful compress
- `BITPACKER_COMPRESSED_SPAN_NAMES` — four canonical span names
## Usage: Basic Example
```ts
import {
BITPACKER_COMPRESSED_SPAN_NAMES,
compressionOpsTotal,
getBitPackerCompressedTracer,
} from "@suaveplan/bitpacker-compressed";
const tracer = getBitPackerCompressedTracer();
tracer.startActiveSpan(
BITPACKER_COMPRESSED_SPAN_NAMES.compress,
(span) => {
try {
// compress work
compressionOpsTotal.add(1, {
op: "compress",
algorithm: "gzip",
success: "true",
});
} finally {
span.end();
}
}
);
```
## Usage: Advanced Example
```ts
import { compressionRatio } from "@suaveplan/bitpacker-compressed";
function recordCompressionRatio(input: Uint8Array, output: Uint8Array, algorithm: string) {
compressionRatio.record(output.byteLength / input.byteLength, { algorithm });
}
```
## API Reference
### `getBitPackerCompressedTracer(): Tracer`
Returns the OTel `Tracer` instance for the package.
### `compressionOpsTotal: Counter`
Counter labelled by `op: "compress" | "decompress"`, `algorithm: string`, `success: "true" | "false"`.
### `compressionOpDurationMs: Histogram`
Histogram of elapsed milliseconds per compress/decompress call.
### `compressionRatio: Histogram`
Histogram of compression ratio (`output_bytes / input_bytes`), labelled by `algorithm`. Recorded only on successful `compress` calls — decompress doesn't have a meaningful ratio.
### `BITPACKER_COMPRESSED_SPAN_NAMES`
Const map: `{ compress, decompress, pack, unpack }` → canonical span names.
## Implementation Notes
- **Algorithm is a label, not a counter prefix.** `compressionOpsTotal{algorithm="gzip"}` is more flexible than separate `gzip_compress_total` / `deflate_compress_total` counters; new adapters fold in without schema changes.
- **`compressionRatio` is not a counter.** The ratio is per-call and stored as a histogram so dashboards can plot p50/p95/p99 ratios over time. A counter would only show throughput, not effectiveness.
- **No per-chunk spans.** Compression adapters often process bytes in internal chunks; one span each would explode trace volume on large inputs.

View File

@@ -0,0 +1,61 @@
# Register Schemas — `@suaveplan/bitpacker-compressed`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Idempotent integration with `@suaveplan/schema-registry` for the `@suaveplan/bitpacker-compressed` package. Exposes `registerBitPackerCompressedSchemas(registry)` which converts the package's five public boundary schemas into JSON Schema form and registers them under `bitpacker:@suaveplan/bitpacker-compressed` at version 1. Re-registration on the same registry is a no-op.
The five schemas covered are: `bitPackerCompressionOptions` (the open-record adapter options), `compressionAdapter` (the structural contract every adapter implements), `compressionConfig` (the `{ algorithm, options, enabled }` config), `compressedBitPackerOptions` (the `CompressedBitPacker` constructor bundle), and `rleCompressionOptions` (the RLE-only `level` knob).
## Features
- `BITPACKER_COMPRESSED_SUBJECT` constant — `"bitpacker:@suaveplan/bitpacker-compressed"`
- `BITPACKER_COMPRESSED_SCHEMA_VERSION` constant — `1`
- `registerBitPackerCompressedSchemas(registry)` — idempotent registration
## Usage: Basic Example
```ts
import { createRegistry } from "@suaveplan/schema-registry";
import {
BITPACKER_COMPRESSED_SUBJECT,
registerBitPackerCompressedSchemas,
} from "@suaveplan/bitpacker-compressed/register-schemas";
const registry = createRegistry();
registerBitPackerCompressedSchemas(registry);
```
## Usage: Advanced Example
```ts
import { createRegistry, SchemaRegistry } from "@suaveplan/schema-registry";
import { registerBitPackerSchemas } from "@suaveplan/bitpacker/register-schemas";
import { registerBitPackerCompressedSchemas } from "@suaveplan/bitpacker-compressed/register-schemas";
const registry = createRegistry();
registerBitPackerSchemas(registry);
registerBitPackerCompressedSchemas(registry);
```
## API Reference
### `BITPACKER_COMPRESSED_SUBJECT: "bitpacker:@suaveplan/bitpacker-compressed"`
Const string.
### `BITPACKER_COMPRESSED_SCHEMA_VERSION: 1`
Const number.
### `registerBitPackerCompressedSchemas(registry: SchemaRegistry): void`
Idempotent registration. Returns without writing if the subject is already present.
## Implementation Notes
- **Five-key payload.** One key per public boundary schema.
- **`unrepresentable: "any"`.** Required because `compressionAdapter` and `compressedBitPackerOptions` use `z.custom` for the structural contract and registry instance check.

View File

@@ -0,0 +1,68 @@
# CompressionRegistry
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`CompressionRegistry` is the pluggable map from algorithm name to `CompressionAdapter` that backs `@suaveplan/bitpacker-compressed`'s public API. It is the single source of truth that `compressData`, `decompressData`, and `CompressedBitPacker` consult to look up an adapter by string name. Two flavours are exposed: a process-wide singleton (`defaultCompressionRegistry()`) for production code, and a fresh-instance factory (`createCompressionRegistry()`) for tests that need isolation. This mirrors the registry conventions used by `@suaveplan/random` and `@suaveplan/bitpacker-crypto` so the codebase stays uniform.
## Features
- Singleton + fresh-instance factory pattern (no class-static singletons).
- Coded errors: unknown algorithm → `BitpackerError(UNKNOWN_COMPRESSION_ALGORITHM)`; soft-fail variant `tryResolve` returns `undefined`.
- Lazy default registration — `defaultCompressionRegistry()` registers `"none"`, `"gzip"`, `"deflate"` on first access (only registers gzip/deflate when `CompressionStream` is available).
- `withDefaults: false` option on `createCompressionRegistry` produces a completely empty registry for negative-path tests.
## Usage: Basic Example
```ts
import { defaultCompressionRegistry } from "@suaveplan/bitpacker-compressed";
const registry = defaultCompressionRegistry();
const adapter = registry.resolve("gzip");
const compressed = await adapter.compress(myBytes);
```
## Usage: Advanced Example
```ts
import {
createCompressionRegistry,
type CompressionAdapter,
} from "@suaveplan/bitpacker-compressed";
// Plug in a custom Brotli adapter (caller-owned WASM Brotli wrapper).
class BrotliAdapter implements CompressionAdapter {
readonly name = "brotli";
async compress(data: Uint8Array) { /* WASM call */ return new Uint8Array(); }
async decompress(data: Uint8Array) { /* WASM call */ return new Uint8Array(); }
}
const registry = createCompressionRegistry();
registry.register(new BrotliAdapter());
const adapter = registry.resolve("brotli");
```
## API Reference
### `defaultCompressionRegistry(): CompressionRegistry`
Returns the lazily-initialized process-wide singleton. Same reference every call.
### `createCompressionRegistry(options?: { withDefaults?: boolean }): CompressionRegistry`
Returns a fresh registry. `withDefaults` defaults to `true`; pass `false` to start empty.
### `CompressionRegistry`
- `register(adapter: CompressionAdapter): void` — Last-write-wins by `adapter.name`.
- `resolve(name: string): CompressionAdapter` — Throws on unknown.
- `tryResolve(name: string): CompressionAdapter | undefined` — Soft-fail variant.
- `list(): readonly string[]` — All registered adapter names.
## Implementation Notes
- The singleton is module-scope inside `registry.ts`; tests that need to mutate it create a fresh `createCompressionRegistry()` rather than poking the singleton.
- Default registration of gzip/deflate is gated on `typeof CompressionStream === "function"` so the registry stays usable in environments without WHATWG Streams (the gzip/deflate adapters themselves throw when called there, but lookup never fails).
- `register` mutates the registry; this is the documented behaviour for plug-in adapters. There is no immutable variant.

View File

@@ -0,0 +1,78 @@
# Run-length encoding (`compressRle` / `decompressRle`)
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs --> - rle.schema — Zod schema for the per-call `Compressi…
- [rle.schema](./rle.schema.md) — Zod schema for the per-call `CompressionOptions` accepted by `compressRle` / `decompressRle`. The RLE codec is byte-onl…
- rle.schema — Zod schema for the per-call `Compressi…
- [rle.schema](./rle.schema.md) — Zod schema for the per-call `CompressionOptions` accepted by `compressRle` / `decompressRle`. The RLE codec is byte-onl…
- rle.schema — Zod schema for the per-call `Compressi…
- [rle.schema](./rle.schema.md) — Zod schema for the per-call `CompressionOptions` accepted by `compressRle` / `decompressRle`. The RLE codec is byte-onl…
- rle.schema — Zod schema for the per-call `Compressi…
- [rle.schema](./rle.schema.md) — Zod schema for the per-call `CompressionOptions` accepted by `compressRle` / `decompressRle`. The RLE codec is byte-onl…
- [rle.schema](./rle.schema.md) — Zod schema for the per-call `CompressionOptions` accepted by `compressRle` / `decompressRle`. The RLE codec is byte-onl…
## Purpose
`compressRle` / `decompressRle` are byte-level run-length encoding helpers shipped as a separate module within `@suaveplan/bitpacker-compressed` so embedded / no-streams environments can import them without pulling in `CompressionStream`-using gzip/deflate adapters or the `CompressedBitPacker` class. They are not part of the `CompressionRegistry` framing — bytes produced by `compressRle` are NOT the same shape as bytes produced by the registry's `"none"`/`"gzip"`/`"deflate"` adapters, which include the `CompressedBitPacker` header. If you need framed RLE, write a thin `RleCompressionAdapter` that internally calls these helpers.
## Features
- Bytes-only fast path; no streams required.
- Single-byte runs longer than 3 are emitted as `[byte][0][count]`; everything else passes through verbatim.
- Symmetric round-trip with `decompressRle`.
- Zero allocation overhead beyond the output `Uint8Array`.
## Usage: Basic Example
```ts
import { compressRle, decompressRle } from "@suaveplan/bitpacker-compressed";
const data = new Uint8Array([1, 1, 1, 1, 1, 2, 3]);
const compressed = compressRle(data); // [1, 0, 5, 2, 3]
const restored = decompressRle(compressed); // back to original
```
## Usage: Advanced Example
```ts
import {
compressRle,
decompressRle,
type CompressionAdapter,
} from "@suaveplan/bitpacker-compressed";
// Wrap RLE in a CompressionAdapter shape so it plugs into the registry.
class RleAdapter implements CompressionAdapter {
readonly name = "rle";
async compress(data: Uint8Array) { return compressRle(data); }
async decompress(data: Uint8Array) { return decompressRle(data); }
}
```
## API Reference
### `compressRle(data: Uint8Array): Uint8Array`
- Encodes runs of >3 identical bytes as `[byte][0][count]`.
- Returns a fresh `Uint8Array`; input is not mutated.
### `decompressRle(data: Uint8Array): Uint8Array`
- Reverses `compressRle`. Looks ahead for `[0][count]` markers after each byte.
- Returns a fresh `Uint8Array`.
### `CompressionOptions`
Type alias for `{ readonly level?: number }` — accepted but ignored by the bytes-only RLE path; included so adapter authors can re-export the canonical compression-options shape.
## Implementation Notes
- The marker byte is `0`, which would collide with a literal `0` in input data; the encoder handles this by always emitting the run-length form when `byte === 0` runs.
- This is not the most aggressive RLE possible — it does not pack runs of distinct bytes into literal blocks. The intent is "smaller than verbatim with a single linear pass", not "competitive with gzip on text".
- Use this when you have known byte-pattern data (e.g. graphics with large flat regions, sparse network frames). For general-purpose data, use the gzip / deflate adapters.

View File

@@ -0,0 +1,48 @@
# RLE Compression Options Schema
## Purpose
Zod schema for the per-call `CompressionOptions` accepted by `compressRle` / `decompressRle`. The RLE codec is byte-only and exposes a single tuning knob (`level`) — this schema validates that knob at the boundary so untrusted input can be normalized before reaching the codec.
## Features
- `rleCompressionOptionsSchema` — strict, single optional field
- `level` is a non-negative integer; the codec ignores values outside its supported range
- `as unknown as z.ZodType<T>` cast for the EOPT mismatch
## Usage: Basic Example
```ts
import { compressRle, rleCompressionOptionsSchema } from "@suaveplan/bitpacker-compressed";
const opts = rleCompressionOptionsSchema.parse({ level: 1 });
const compressed = compressRle(new Uint8Array([0, 0, 0, 0, 1, 2]), opts);
```
## Usage: Advanced Example
```ts
import {
compressRle,
decompressRle,
rleCompressionOptionsSchema,
} from "@suaveplan/bitpacker-compressed";
function safeRoundTrip(data: Uint8Array, rawOpts: unknown) {
const opts = rleCompressionOptionsSchema.parse(rawOpts);
const compressed = compressRle(data, opts);
const decompressed = decompressRle(compressed);
return decompressed;
}
```
## API Reference
### `rleCompressionOptionsSchema: z.ZodType<CompressionOptions>`
Strict schema. `level` is an optional non-negative integer. Throws `ZodError` on negative or non-integer values, or on unknown keys.
## Implementation Notes
- **Why a one-field schema.** Even a single-field bundle benefits from boundary validation: a `{ level: "9" }` (string instead of number) would otherwise reach the codec and produce undefined behaviour. Refusing it at parse time keeps the codec's contract simple.
- **Codec ignores out-of-range levels.** The schema enforces non-negative integer; the codec itself ignores values outside `[0, 9]`. The schema is the boundary; the codec is the implementation.

View File

@@ -0,0 +1,92 @@
# Compression types
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs --> - types.schema — Zod schemas for the public type comp…
- [types.schema](./types.schema.md) — Zod schemas for the public type companions of `@suaveplan/bitpacker-compressed`'s compression pipeline: the open-record…
- types.schema — Zod schemas for the public type comp…
- [types.schema](./types.schema.md) — Zod schemas for the public type companions of `@suaveplan/bitpacker-compressed`'s compression pipeline: the open-record…
- types.schema — Zod schemas for the public type comp…
- [types.schema](./types.schema.md) — Zod schemas for the public type companions of `@suaveplan/bitpacker-compressed`'s compression pipeline: the open-record…
- types.schema — Zod schemas for the public type comp…
- [types.schema](./types.schema.md) — Zod schemas for the public type companions of `@suaveplan/bitpacker-compressed`'s compression pipeline: the open-record…
- [types.schema](./types.schema.md) — Zod schemas for the public type companions of `@suaveplan/bitpacker-compressed`'s compression pipeline: the open-record…
## Purpose
This module collects the public TypeScript type companions used across `@suaveplan/bitpacker-compressed`: the algorithm-name string brand, the per-call options shape, and the `CompressionAdapter` interface that any plug-in codec must implement. Keeping them in one file means downstream consumers only need a single import path to write a custom adapter or to type a config object that flows through the pipeline. The shapes mirror the upstream `bitpacker/src/types.ts` compression types verbatim so adapters and consumers see the same contracts whether they imported from upstream or the port.
## Features
- `BitPackerCompressionAlgorithm` — string-typed registry key (e.g. `"none"`, `"gzip"`, `"deflate"`, `"brotli"`).
- `BitPackerCompressionOptions` — caller-supplied options forwarded to the adapter (currently `level?: number`).
- `CompressionAdapter` — the contract every codec implements: `name`, `compress`, `decompress`.
- All async to match the WHATWG Streams reality; sync codecs simply resolve immediately.
## Usage: Basic Example
```ts
import type {
BitPackerCompressionAlgorithm,
BitPackerCompressionOptions,
CompressionAdapter,
} from "@suaveplan/bitpacker-compressed";
const algorithm: BitPackerCompressionAlgorithm = "gzip";
const options: BitPackerCompressionOptions = { level: 9 };
```
## Usage: Advanced Example
```ts
import type { CompressionAdapter } from "@suaveplan/bitpacker-compressed";
// Custom adapter for a hypothetical FastLZ codec.
class FastLzAdapter implements CompressionAdapter {
readonly name = "fastlz";
async compress(data: Uint8Array, options?: { level?: number }) {
// Call into FastLZ-WASM module here.
return data; // placeholder
}
async decompress(data: Uint8Array, _options?: { level?: number }) {
return data; // placeholder
}
}
```
## API Reference
### `BitPackerCompressionAlgorithm`
Type alias for `string`. Used as the registry lookup key. Default registered names: `"none"`, `"gzip"`, `"deflate"`. Consumers MAY register additional algorithms.
### `BitPackerCompressionOptions`
```ts
interface BitPackerCompressionOptions {
readonly level?: number;
}
```
Forwarded to the adapter. Adapters that don't support `level` (e.g. `none`) ignore it.
### `CompressionAdapter`
```ts
interface CompressionAdapter {
readonly name: BitPackerCompressionAlgorithm;
compress(data: Uint8Array, options?: BitPackerCompressionOptions): Promise<Uint8Array>;
decompress(data: Uint8Array, options?: BitPackerCompressionOptions): Promise<Uint8Array>;
}
```
## Implementation Notes
- `BitPackerCompressionAlgorithm` is intentionally `string`, not a closed union — module augmentation is overkill for plug-in codecs and the registry already validates names at lookup.
- All methods are async because the standard browser/Node codecs (`CompressionStream`) are async. The pass-through `NoCompressionAdapter` resolves immediately so async overhead is negligible.
- Adapters MUST be deterministic: same input + same options ⇒ same output bytes (modulo internal nondeterminism in the underlying codec, which is allowed but unusual).

View File

@@ -0,0 +1,77 @@
# Compression Type Schemas
## Purpose
Zod schemas for the public type companions of `@suaveplan/bitpacker-compressed`'s compression pipeline: the open-record `BitPackerCompressionOptions`, the `CompressionAdapter` interface (the contract every adapter implements), and the `CompressionConfig` configuration shape consumed by `CompressedBitPacker`. Each schema is the runtime gatekeeper at the corresponding boundary so an adapter loaded from a plugin manifest, or a config bundle parsed from environment variables, can be validated before reaching the compression hot path.
The schemas use `z.custom<T>((v): v is T => …)` for the adapter validation so adapter prototype chains are preserved (registries depend on prototype-aware `instanceof` checks), and `z.record(z.string(), z.unknown())` for the open-record `BitPackerCompressionOptions` because each adapter accepts its own option keys — the schema validates "is this an object?" and lets the adapter validate its own option semantics.
## Features
- `bitPackerCompressionOptionsSchema` — open record (any string-keyed object)
- `compressionAdapterSchema` — structural contract: `name: string`, `compress: function`, `decompress: function`
- `compressionConfigSchema` — strict, three optional fields (`algorithm`, `options`, `enabled`)
- Identity-preserving `z.custom` for adapter validation
- `as unknown as z.ZodType<T>` casts where Zod's `T | undefined` collides with `exactOptionalPropertyTypes`
## Usage: Basic Example
```ts
import {
CompressedBitPacker,
compressionAdapterSchema,
compressionConfigSchema,
NoCompressionAdapter,
} from "@suaveplan/bitpacker-compressed";
const adapter = compressionAdapterSchema.parse(new NoCompressionAdapter());
const cfg = compressionConfigSchema.parse({ algorithm: "none", enabled: true });
```
## Usage: Advanced Example
```ts
import {
CompressedBitPacker,
bitPackerCompressionOptionsSchema,
compressionAdapterSchema,
compressionConfigSchema,
} from "@suaveplan/bitpacker-compressed";
async function loadVendorAdapter(url: URL) {
const mod = await import(url.href);
const adapter = compressionAdapterSchema.parse(mod.default);
return adapter;
}
const opts = bitPackerCompressionOptionsSchema.parse({
level: 9,
windowBits: 15,
});
const cfg = compressionConfigSchema.parse({
algorithm: "deflate",
options: opts,
enabled: true,
});
```
## API Reference
### `bitPackerCompressionOptionsSchema: z.ZodType<BitPackerCompressionOptions>`
Open-record schema. Accepts any object with string keys. Adapter-specific validation is the adapter's responsibility.
### `compressionAdapterSchema: z.ZodType<CompressionAdapter>`
Structural contract validator. Asserts `name: string`, `compress: function`, `decompress: function`. Identity is preserved.
### `compressionConfigSchema: z.ZodType<CompressionConfig>`
Strict schema. Three optional fields: `algorithm: string`, `options: BitPackerCompressionOptions`, `enabled: boolean`.
## Implementation Notes
- **Open-record options.** Adapters accept arbitrary keys (`level`, `windowBits`, `dictionary`, …). Validating them at this layer would force this package to know every adapter's surface, which it can't. The contract is "is this an object?"; semantic validation belongs to the adapter.
- **Structural adapter check.** A duck-typed object that exposes `name/compress/decompress` works at runtime. Refusing duck-types would break the plugin model where adapters are loaded from external modules without inheriting from a base class.
- **Identity preservation.** `z.custom` for the adapter; the registry's `register(adapter)` method uses `===` and `instanceof` checks downstream.