Files
suaveplan/openspec/archive/2026-05-02-bitpacker-stub-completion/specs/bitpacker-streaming/spec.md
2026-05-27 23:29:53 +01:00

101 lines
6.4 KiB
Markdown

<!-- markdownlint-disable MD024 -->
# Capability Spec Delta: bitpacker-streaming (bitpacker-stub-completion)
**Package**: `@suaveplan/bitpacker-streaming`
**Status**: PROPOSED
> Every requirement below carries a `**Verified by:**` line at authoring time.
## ADDED Requirements
### Requirement: StreamingBitPacker class
The package SHALL export `StreamingBitPacker<T extends BitPackerSchema> extends EventEmitter` matching upstream `streaming.ts` (L14-L406). The class SHALL expose `packStream(data, options)` and `unpackStream(packed, options)`, both returning `Promise<Uint8Array>` (pack) / `Promise<SchemaTypeToDataType<T>>` (unpack). Both methods SHALL emit `progress` events as chunks complete and `chunk` events with the bytes of each completed chunk; chunk size SHALL come from `options.chunkSize` (default 1024).
#### Scenario: packStream round-trips through unpackStream
- **WHEN** a 100 KiB object is `packStream`-ed and the result is `unpackStream`-ed under the same schema
- **THEN** the unpacked value deep-equals the input
#### Scenario: progress events fire in chunk order
- **WHEN** `packStream` is called on data that produces 8 chunks
- **THEN** 8 `progress` events fire with `{ index, total: 8, percent }` in monotonically increasing order; the final event has `percent === 1`
#### Scenario: chunk events carry chunk bytes
- **WHEN** `packStream` runs with `emitProgress: true`
- **THEN** each `chunk` event's payload is a `Uint8Array` whose concatenation equals the final return value byte-for-byte
**Verified by:** `packages/bitpacker/bitpacker-streaming/src/streaming-bit-packer/streaming-bit-packer.test.ts::"packStream / unpackStream round-trip preserves the value"` AND `packages/bitpacker/bitpacker-streaming/src/streaming-bit-packer/streaming-bit-packer.test.ts::"emits progress events in monotonic chunk order"` AND `packages/bitpacker/bitpacker-streaming/src/streaming-bit-packer/streaming-bit-packer.test.ts::"chunk events concatenate to the final byte output"`
---
### Requirement: chunkProcessor invoked per chunk
If `options.chunkProcessor` is supplied, the streaming bit-packer SHALL invoke it on each chunk before emitting / returning, awaiting the result. If the processor throws, `packStream` / `unpackStream` SHALL throw `BitpackerError` with code `BITPACKER.STREAM_CHUNK_PROCESSOR_FAILED`, chaining the original error.
#### Scenario: chunkProcessor mutates chunks before delivery
- **WHEN** `chunkProcessor` is `(chunk) => xor(chunk, 0xff)` and the produced bytes are XOR'd back before unpack
- **THEN** the round-trip succeeds
#### Scenario: chunkProcessor failure surfaces as coded error
- **WHEN** `chunkProcessor` throws on chunk 3
- **THEN** `packStream` rejects with `BitpackerError` code `BITPACKER.STREAM_CHUNK_PROCESSOR_FAILED` whose `cause` is the original error and `context.chunkIndex === 3`
**Verified by:** `packages/bitpacker/bitpacker-streaming/src/streaming-bit-packer/streaming-bit-packer.spec-conformance.test.ts::"chunkProcessor mutates chunks before delivery"` AND `packages/bitpacker/bitpacker-streaming/src/streaming-bit-packer/streaming-bit-packer.spec-conformance.test.ts::"chunkProcessor failure surfaces as STREAM_CHUNK_PROCESSOR_FAILED"`
---
### Requirement: validateChunks gates pack/unpack on validation
When `options.validateChunks` is `true`, the streaming bit-packer SHALL run the underlying `BitPacker.validate` over the input (pack) or unpacked output (unpack) and reject before emitting if validation fails, throwing `BitpackerError` with code `BITPACKER.STREAM_VALIDATION_FAILED`.
#### Scenario: validation gate rejects malformed input
- **WHEN** `validateChunks: true` and the input violates the schema (e.g. value out of `u8` range)
- **THEN** `packStream` rejects with `BitpackerError` code `BITPACKER.STREAM_VALIDATION_FAILED` AND no `chunk` events fire
#### Scenario: validation gate disabled by default
- **WHEN** `validateChunks` is omitted
- **THEN** packing proceeds without validation; encoder errors surface natively
**Verified by:** `packages/bitpacker/bitpacker-streaming/src/streaming-bit-packer/streaming-bit-packer.spec-conformance.test.ts::"validateChunks: true rejects malformed input with STREAM_VALIDATION_FAILED"` AND `packages/bitpacker/bitpacker-streaming/src/streaming-bit-packer/streaming-bit-packer.spec-conformance.test.ts::"validateChunks default is false"`
---
### Requirement: AbortSignal propagation
`packStream` and `unpackStream` SHALL accept `options.signal: AbortSignal`. When the signal is already aborted at call time, the call SHALL reject immediately with `BitpackerError` code `BITPACKER.STREAM_ABORTED`. When abort fires mid-stream, the in-flight chunk SHALL complete and then the call SHALL reject with the same code; no subsequent chunks SHALL be processed or emitted.
#### Scenario: pre-aborted signal rejects immediately
- **WHEN** `signal.aborted === true` at call time
- **THEN** `packStream` rejects synchronously with `BitpackerError` code `BITPACKER.STREAM_ABORTED`
#### Scenario: mid-stream abort halts further chunks
- **WHEN** `abort()` fires after the 2nd of 8 chunks completes
- **THEN** chunks 3..8 do NOT emit `chunk` events and the promise rejects with `BITPACKER.STREAM_ABORTED`
**Verified by:** `packages/bitpacker/bitpacker-streaming/src/streaming-bit-packer/streaming-bit-packer.test.ts::"pre-aborted signal rejects immediately with STREAM_ABORTED"` AND `packages/bitpacker/bitpacker-streaming/src/streaming-bit-packer/streaming-bit-packer.test.ts::"mid-stream abort halts further chunk emission"`
## MODIFIED Requirements
### Requirement: Synchronous chunked helpers delegate to StreamingBitPacker
The existing `packToChunks`, `unpackFromChunks`, and `unpackFromChunksAsync` functions SHALL be preserved with their current signatures, but their implementation SHALL delegate to `StreamingBitPacker` so behavior stays synchronized. The async helper SHALL honour `AbortSignal` (already true today; pin behavior in test).
#### Scenario: packToChunks output equals StreamingBitPacker.packStream concatenation
- **WHEN** the same data + schema is run through `packToChunks` and through `packStream` and the chunks of each are concatenated
- **THEN** the byte-arrays are equal
**Verified by:** `packages/bitpacker/bitpacker-streaming/src/streaming-bit-packer/streaming-bit-packer.test.ts::"packToChunks delegates to packStream"` AND `packages/bitpacker/bitpacker-streaming/src/streaming-bit-packer/streaming-bit-packer.test.ts::"unpackFromChunksAsync honours AbortSignal"`
<!-- markdownlint-enable MD024 -->