Files
suaveplan/openspec/archive/2026-04-24-enterprise-bitpacker/proposal.md
2026-05-27 23:29:53 +01:00

200 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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<T extends Record<string, unknown> = Record<string, unknown>> {
fields: FieldDef[]
pack(values: T): Uint8Array
unpack(data: Uint8Array): T
byteSize: number // fixed size if computable; 0 = variable
}
function defineBitSchema<T extends Record<string, unknown>>(fields: FieldDef[]): BitSchema<T>
```
### 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<Record<BitpackerErrorCode, CodeMetadata>>
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.