# OpenSpec Change: enterprise-bitpacker - **Change ID**: enterprise-bitpacker - **Package**: `@suaveplan/bitpacker` - **Category**: universal - **Target path**: `universal/compression/bitpacker` - **Status**: PROPOSED - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/bitpacker.md` --- ## Summary Deliver `@suaveplan/bitpacker` on the genesis branch from scratch at enterprise quality. The package provides bit-level binary packing and unpacking: arbitrary-width unsigned/signed integers (1–53 bits), booleans, fixed-point numbers, bit-field schemas with named fields, and streaming read/write cursors. Designed for network protocols, game state serialisation, and compact binary formats. Pure TypeScript, zero deps beyond `@suaveplan/error`. --- ## Current-state audit (genesis) The package does not exist on genesis yet — this is a fresh build. No prior source, no test results, no coverage data. The main-branch package had 91% coverage. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/error` (`workspace:^`) | Base class for `BitpackerError`; `BITPACKER.*` codes | | `@suaveplan/types` (`workspace:^`) | `LoggerLike` | | `zod` (`catalog:forms`) | Zod schemas for schema definitions | | `@opentelemetry/api` (`catalog:observability`) | Spans + counters | Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check`, `mitata` (all `catalog:testing`). --- ## Public API ### BitWriter ```ts class BitpackerError extends SuaveplanError class BitWriter { constructor(initialCapacityBytes?: number) writeUint(value: number, bits: number): this // 1–53 bits writeInt(value: number, bits: number): this // 1–53 bits, two's complement writeBool(value: boolean): this writeFixed(value: number, intBits: number, fracBits: number): this writeBytes(bytes: Uint8Array): this align(toBytes?: number): this // pad to byte/word boundary toUint8Array(): Uint8Array get bitLength(): number get byteLength(): number } ``` ### BitReader ```ts class BitReader { constructor(data: Uint8Array) readUint(bits: number): number readInt(bits: number): number readBool(): boolean readFixed(intBits: number, fracBits: number): number readBytes(byteCount: number): Uint8Array align(toBytes?: number): void get bitPosition(): number get bitsRemaining(): number peek(bits: number): number // read without advancing seek(bitPosition: number): void } ``` ### Schema-based packing ```ts type FieldType = 'uint' | 'int' | 'bool' | 'fixed' | 'bytes' interface FieldDef { name: string type: FieldType bits?: number // for uint/int intBits?: number // for fixed fracBits?: number // for fixed byteCount?: number // for bytes } interface BitSchema = Record> { fields: FieldDef[] pack(values: T): Uint8Array unpack(data: Uint8Array): T byteSize: number // fixed size if computable; 0 = variable } function defineBitSchema>(fields: FieldDef[]): BitSchema ``` ### Utilities ```ts function packBits(values: number[], bitsPerValue: number): Uint8Array function unpackBits(data: Uint8Array, count: number, bitsPerValue: number): number[] function countBits(n: number): number // position of highest set bit function zigzagEncode(n: number): number // signed → unsigned function zigzagDecode(n: number): number // unsigned → signed ``` ### Error codes ```ts type BitpackerErrorCode = | 'BITPACKER_OVERFLOW' | 'BITPACKER_UNDERFLOW' | 'BITPACKER_INVALID_BIT_WIDTH' | 'BITPACKER_VALUE_OUT_OF_RANGE' | 'BITPACKER_SCHEMA_INVALID' const BITPACKER_ERROR_METADATA: Readonly> function registerBitpackerErrorCodes(): void ``` ### Zod schemas ```ts FieldDefSchema ``` --- ## New additions for genesis 1. **`defineBitSchema`** — type-safe named-field schemas with compile-time inferred value types. 2. **Zigzag encoding** — `zigzagEncode`/`zigzagDecode` for efficient signed-integer variable-length encoding. 3. **`peek` and `seek`** on `BitReader` — non-destructive lookahead and random-access positioning. 4. **Alignment helpers** — `align()` pads writer or advances reader to next byte/word boundary for protocol framing. 5. **OTel instrumentation** — counter `bitpacker.pack_total`, counter `bitpacker.unpack_total`, histogram `bitpacker.pack_bytes`. 6. **Property-based tests** — pack/unpack round-trip for arbitrary bit widths and values within range, `zigzagEncode(zigzagDecode(n)) === n` for all signed 32-bit integers. 7. **Benchmark suite** — pack 1000 × 7-bit uints, schema pack/unpack 100-field struct, `packBits` on 10000 values. --- ## File layout ```text universal/compression/bitpacker/ package.json tsconfig.json vitest.config.ts src/ index.ts error-codes.ts constants/ index.ts types/ common.ts common.md core/ bit-writer/ bit-writer.ts bit-writer.md bit-writer.types.ts bit-writer.test.ts index.ts bit-reader/ bit-reader.ts bit-reader.md bit-reader.types.ts bit-reader.test.ts index.ts bit-schema/ bit-schema.ts bit-schema.md bit-schema.types.ts bit-schema.test.ts index.ts utils/ pack-bits.ts pack-bits.md pack-bits.test.ts zigzag.ts zigzag.md zigzag.test.ts __benchmarks__/ bitpacker.bench.ts tests/ integration.test.ts property.test.ts ``` --- ## Impact **Downstream**: `@suaveplan/packer`, network protocol packages, game state serialisation. **Breaking changes**: none — fresh build. **Prerequisites**: `@suaveplan/error`, `@suaveplan/types` published to Verdaccio.