first commit
This commit is contained in:
91
docs/foundation/bitpacker/bitpacker-compressed/README.md
Normal file
91
docs/foundation/bitpacker/bitpacker-compressed/README.md
Normal 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`
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,8 @@
|
||||
## index
|
||||
|
||||
**Path:** `bitpacker-compressed/src/index.ts`
|
||||
**Export:** See source for details.
|
||||
|
||||
### What it does
|
||||
|
||||
Part of the @suaveplan/ package.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user