Files
2026-05-27 23:29:53 +01:00

6.6 KiB

Capability Spec Delta: bitpacker-compressed (bitpacker-stub-completion)

Package: @suaveplan/bitpacker-compressed Status: PROPOSED

Every requirement below carries a **Verified by:** line at authoring time.

ADDED Requirements

Requirement: CompressedBitPacker subclass

The package SHALL export CompressedBitPacker<T extends BitPackerSchema> extends BitPacker<T>. The subclass SHALL run packed bytes through a configured CompressionAdapter before returning them, and SHALL run incoming bytes through decompressData before delegating to BitPacker.unpack. Compression behavior SHALL be configurable via BitPackerConfig.compression (algorithm, options, enabled); accessors compressionAlgorithm, compressionOptions, compressionEnabled SHALL surface and mutate the current settings.

Scenario: pack → decompress round-trip is byte-identical

  • WHEN a CompressedBitPacker packs { a: 1, b: "hello" } with algorithm: "gzip" and the result is decompressed and unpacked
  • THEN the unpacked value deep-equals the input

Scenario: enabled: false skips compression entirely

  • WHEN compressionEnabled is false
  • THEN pack returns bytes byte-identical to BitPacker.pack (no compression header, no codec invocation)

Scenario: algorithm: "none" invokes NoCompressionAdapter

  • WHEN algorithm is "none"
  • THEN pack produces the same bytes as BitPacker.pack AND the OTel counter bitpacker.compress.bytes_in records the input length while bitpacker.compress.bytes_out records the same value

Verified by: packages/bitpacker/bitpacker-compressed/src/compressed-bit-packer/compressed-bit-packer.test.ts::"pack-decompress round-trip is byte-identical for gzip" AND packages/bitpacker/bitpacker-compressed/src/compressed-bit-packer/compressed-bit-packer.test.ts::"compressionEnabled false skips codec invocation" AND packages/bitpacker/bitpacker-compressed/src/compressed-bit-packer/compressed-bit-packer.test.ts::"algorithm 'none' yields BitPacker.pack output"


Requirement: CompressionAdapter contract and built-in adapters

The package SHALL expose a CompressionAdapter interface with compress(data, options): Promise<Uint8Array> and decompress(data, options): Promise<Uint8Array>. The package SHALL ship NoCompressionAdapter, GzipCompressionAdapter, and DeflateCompressionAdapter matching upstream behavior (compression.ts L121-L336).

Scenario: gzip adapter round-trip

  • WHEN GzipCompressionAdapter compresses 1 KiB of zeros, then decompresses
  • THEN the output is the original 1 KiB AND the compressed length is < 50 bytes

Scenario: deflate adapter round-trip

  • WHEN DeflateCompressionAdapter compresses random 1 KiB bytes, then decompresses
  • THEN the decompressed output deep-equals the original

Scenario: NoCompressionAdapter passes bytes through

  • WHEN NoCompressionAdapter.compress(input) is called
  • THEN the result is byte-identical to the input

Verified by: packages/bitpacker/bitpacker-compressed/src/compression-registry/compression-registry.test.ts::"GzipCompressionAdapter round-trip" AND packages/bitpacker/bitpacker-compressed/src/compression-registry/compression-registry.test.ts::"DeflateCompressionAdapter round-trip" AND packages/bitpacker/bitpacker-compressed/src/compression-registry/compression-registry.test.ts::"NoCompressionAdapter passes bytes through"


Requirement: CompressionRegistry replacement and unknown-algorithm error

The package SHALL expose a CompressionRegistry allowing callers to register(algorithm, adapter) and resolve(algorithm). Resolving an unknown algorithm SHALL throw BitpackerError with code BITPACKER.UNKNOWN_COMPRESSION_ALGORITHM and context.algorithm.

Scenario: registering an adapter overrides the default

  • WHEN a custom BrotliCompressionAdapter is registered under key "brotli" then resolve("brotli") is called
  • THEN the registered adapter is returned; subsequent pack calls with algorithm: "brotli" route through it

Scenario: resolving an unknown algorithm throws coded error

  • WHEN resolve("zstd") is called and no adapter is registered for that key
  • THEN the call throws BitpackerError with code BITPACKER.UNKNOWN_COMPRESSION_ALGORITHM and context.algorithm === "zstd"

Verified by: packages/bitpacker/bitpacker-compressed/src/compression-registry/compression-registry.spec-conformance.test.ts::"register overrides default adapter" AND packages/bitpacker/bitpacker-compressed/src/compression-registry/compression-registry.spec-conformance.test.ts::"resolve throws UNKNOWN_COMPRESSION_ALGORITHM for missing key"


Requirement: Compression failure surfaces coded error

When the underlying codec rejects (e.g. truncated gzip stream), the package SHALL wrap the cause and throw BitpackerError with code BITPACKER.COMPRESSION_FAILED (during compress) or BITPACKER.DECOMPRESSION_FAILED (during decompress); context.algorithm SHALL identify the codec.

Scenario: truncated gzip stream emits DECOMPRESSION_FAILED

  • WHEN a half-truncated gzip-compressed buffer is passed to decompressData(buf, "gzip")
  • THEN a BitpackerError with code BITPACKER.DECOMPRESSION_FAILED is thrown with the underlying codec error chained as cause

Verified by: packages/bitpacker/bitpacker-compressed/src/compression-registry/compression-registry.spec-conformance.test.ts::"truncated gzip stream emits DECOMPRESSION_FAILED with chained cause"

MODIFIED Requirements

Requirement: RLE pass-through helpers

The package SHALL keep compressRle / decompressRle as exported free functions for the bytes-only fast path; they SHALL be available both at the package root (one-release deprecation) and at the ./rle subpath. Their behavior SHALL match the existing 61-line implementation byte-for-byte (no semantic change).

Scenario: existing RLE call sites still resolve from package root

  • WHEN a consumer imports { compressRle } from "@suaveplan/bitpacker-compressed"
  • THEN the import resolves and compressRle produces the same bytes as before this change

Scenario: canonical import location is ./rle

  • WHEN a consumer imports { compressRle } from "@suaveplan/bitpacker-compressed/rle"
  • THEN the import resolves and the function is the same reference as the root export

Verified by: packages/bitpacker/bitpacker-compressed/src/rle/rle.test.ts::"compressRle output unchanged from pre-rewrite snapshot" AND packages/bitpacker/bitpacker-compressed/src/rle/rle.test.ts::"./rle subpath and root export resolve to the same function"