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

12 KiB

Capability Spec Delta: packer (enterprise-packer-extensibility)

Package: @suaveplan/packer Status: PROPOSED

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

ADDED Requirements

Requirement: PACKER-26 — @suaveplan/schema-registry integration

The package SHALL register every Zod schema in src/schemas/packer.schemas.ts (PackerOptionsSchema, UnpackOptionsSchema, ChunkedPackerOptionsSchema, ManifestSchema) plus a packer-chunk-format subject capturing the wire-format version byte, into a process-singleton SchemaRegistry exposed via getPackerSchemaRegistry(). The packer-chunk-format subject MUST register V1 (the current wire format) under a BACKWARD compatibility policy with evolution metadata { formatVersion: "1", encoding: "binary", description: "v1 fixed-width 164-byte header + AEAD payload" } so a future V2 entry can be added without disturbing V1. Every other schema MUST be registered under its own subject (PackerOptions, UnpackOptions, ChunkedPackerOptions, Manifest) carrying the metadata { formatVersion: "1" }. A registerPackerSchemas(registry) function MUST allow callers to register the same schemas into any external SchemaRegistry instance for cross-package validation.

Scenario: getPackerSchemaRegistry returns a populated singleton

  • WHEN getPackerSchemaRegistry() is called and then registry.listSubjects() is read
  • THEN the list contains exactly PackerOptions, UnpackOptions, ChunkedPackerOptions, Manifest, and packer-chunk-format

Scenario: packer-chunk-format records V1 with evolution metadata

  • WHEN registry.getLatest("packer-chunk-format") is read
  • THEN metadata.formatVersion === "1", metadata.encoding === "binary", and registering an identical V1 candidate succeeds under BACKWARD compatibility

Scenario: registerPackerSchemas writes into a caller-supplied registry

  • WHEN registerPackerSchemas(externalRegistry) is called against a fresh createRegistry()
  • THEN externalRegistry.listSubjects() contains the same five subjects with formatVersion: "1" metadata, and no mutation to the global singleton has occurred

Verified by: src/schemas/registration.test.ts::"registers every option schema as a subject with formatVersion 1 metadata" AND src/schemas/registration.test.ts::"records packer-chunk-format V1 with binary encoding and BACKWARD compatibility" AND src/schemas/registration.spec-conformance.test.ts::"getPackerSchemaRegistry returns a process-singleton populated with the canonical subjects" AND src/schemas/registration.spec-conformance.test.ts::"registerPackerSchemas writes into a caller-supplied registry without mutating the global singleton"


Requirement: PACKER-27 — @suaveplan/feature-flags for format-version rollouts

The package SHALL accept a flags?: PackerFlagsOptions bundle on PackerOptions and route the wire-format selection through the resolved PackerFeatureFlags provider. The exported constant PACKER_FLAG_KEYS.FORMAT_V2 === "packer.format.v2" MUST identify the V2 rollout flag. When no provider is supplied, defaultPackerFlags() MUST act as a no-op that returns the V1 default (false) for every flag. When the resolved provider reports packer.format.v2 === true, Packer.pack MUST throw PackerError(PACKER.FORMAT_VERSION_UNSUPPORTED) with the catalog message packer.error.FORMAT_VERSION_UNSUPPORTED.v2-not-implemented; this is a versioning gate — the V2 codec is a future change. A caller-supplied @suaveplan/feature-flags Evaluator MUST be acceptable as the provider via resolvePackerFlags({ evaluator, context? }).

Scenario: default flags resolve V2 to false and pack succeeds in V1

  • WHEN new Packer().pack(data) is called with no flags option
  • THEN the call returns a PackedData whose metadata carries the V1 shape (no V2-specific fields), and defaultPackerFlags().get("packer.format.v2") === false

Scenario: enabling packer.format.v2 throws FORMAT_VERSION_UNSUPPORTED

  • WHEN Packer.pack(data) is called with a flags provider whose get("packer.format.v2") returns true
  • THEN the call rejects with PackerError(PACKER.FORMAT_VERSION_UNSUPPORTED) and error.code === "PACKER.FORMAT_VERSION_UNSUPPORTED"

Scenario: a caller-supplied feature-flags Evaluator drives the gate

  • WHEN Packer.pack(data) is called with flags: { evaluator, context: { userId: "u-1" } } where evaluator resolves packer.format.v2 to false for userId: "u-1"
  • THEN the call returns a V1 bundle and the evaluator's internal counter records one evaluation for flag_key: "packer.format.v2"

Verified by: src/flags/flags.test.ts::"defaultPackerFlags returns false for packer.format.v2" AND src/flags/flags.test.ts::"resolvePackerFlags adapts a feature-flags Evaluator into a PackerFeatureFlags provider" AND src/flags/flags.spec-conformance.test.ts::"Packer.pack throws PACKER.FORMAT_VERSION_UNSUPPORTED when packer.format.v2 is enabled" AND src/flags/flags.spec-conformance.test.ts::"Packer.pack succeeds in V1 when flags option is omitted" AND src/flags/flags.spec-conformance.test.ts::"a caller-supplied feature-flags Evaluator drives the format-version gate"


Requirement: PACKER-28 — Plugin / extension interface

The package SHALL expose a PackerPlugin contract carrying six lifecycle hooks (onPackStart(ctx), onEntry(ctx), onPackEnd(ctx), onUnpackStart(ctx), onEntryDecoded(ctx), onUnpackEnd(ctx)), each optional and each invoked in plugin-declaration order. Plugins are registered via plugins?: ReadonlyArray<PackerPlugin> on PackerOptions. Packer.pack MUST invoke onPackStart exactly once before the pipeline, onEntry exactly once per entry packed (one for whole-file mode), and onPackEnd exactly once after the final result is composed. Unpacker.unpack MUST invoke onUnpackStart / onEntryDecoded / onUnpackEnd symmetrically. Any plugin error MUST be caught and re-thrown as PackerError(PACKER.PLUGIN_FAILED) with a catalog message packer.error.PLUGIN_FAILED.<hook> and the original error carried on error.cause.

Scenario: plugin hooks fire in declaration order

  • WHEN Packer.pack(data) is called with two plugins whose onPackStart / onEntry / onPackEnd push their name onto a shared array
  • THEN the array equals ["A:start", "B:start", "A:entry", "B:entry", "A:end", "B:end"]

Scenario: an unpack pass fires onUnpackStart / onEntryDecoded / onUnpackEnd

  • WHEN Unpacker.unpack(packed) is called with a plugin that records each unpack hook
  • THEN the recorded order is ["unpackStart", "entryDecoded", "unpackEnd"]

Scenario: a plugin error surfaces as PACKER.PLUGIN_FAILED

  • WHEN Packer.pack(data) is called with a plugin whose onEntry throws new Error("boom")
  • THEN the call rejects with PackerError(PACKER.PLUGIN_FAILED), error.cause.message === "boom", and error.message is the resolved packer.error.PLUGIN_FAILED.onEntry catalog string

Scenario: a double-integrity sidecar plugin contributes SHA-256

  • WHEN Packer.pack(data) is called with a plugin whose onEntry computes SHA-256 of the payload and stashes it on the plugin's internal map, then Unpacker.unpack is called with the same plugin whose onEntryDecoded reads back and asserts equality
  • THEN both calls succeed and the plugin observed identical SHA-256 bytes on the way out and the way in

Verified by: src/plugins/plugins.test.ts::"runs onPackStart / onEntry / onPackEnd in declaration order" AND src/plugins/plugins.test.ts::"runs onUnpackStart / onEntryDecoded / onUnpackEnd in declaration order" AND src/plugins/plugins.test.ts::"wraps thrown plugin errors as PackerError(PACKER.PLUGIN_FAILED) with cause" AND src/plugins/plugins.spec-conformance.test.ts::"a noop-logger plugin observes every lifecycle hook" AND src/plugins/plugins.spec-conformance.test.ts::"a double-integrity sidecar plugin contributes SHA-256 alongside BLAKE3"


Requirement: PACKER-29 — fast-check property-based round-trip + determinism

The package SHALL include fast-check property tests pinning the pack/unpack round-trip identity, streaming↔batch equivalence, and seeded-Rng determinism invariants. Property assertions MUST run over arbitrary Uint8Array inputs of length 0..1024 (at least 50 runs per property). Round-trip identity: for every data, unpack(pack(data)) returns bytes byte-equal to data. Streaming equivalence: the concatenation of every PackedChunk.bytes emitted by packChunks(asyncIter(data), { ...same fileKey/signKey/baseNonce }) MUST byte-equal the wire bytes of packChunks(data, { ...same opts }) consumed from a whole-buffer source. Determinism: two Packer.pack runs with identical inputs, identical options, the same seeded Rng and the same fixed Clock MUST produce byte-identical PackedData (both data and every byte of metadata.integrity).

Scenario: round-trip identity over arbitrary Uint8Array inputs

  • WHEN fc.assert(fc.asyncProperty(fc.uint8Array({ maxLength: 1024 }), async (data) => { … pack/unpack … })) runs
  • THEN every drawn data is recovered byte-identical from unpack(pack(data))

Scenario: streaming chunked pack matches whole-buffer chunked pack

  • WHEN the same input is fed to packChunks once as a whole-buffer source and once as a 1-byte-at-a-time async-iterable source, with identical options
  • THEN concatenated wire bytes are byte-identical between the two runs

Scenario: seeded Rng + fixed Clock produces byte-identical bundles

  • WHEN two Packer.pack(data) calls run with the same data, the same seeded Rng, the same fixed Clock, and identical encryption options
  • THEN both result.data and result.metadata.integrity.entryDigest bytes are equal

Verified by: src/__property__/round-trip.property.test.ts::"pack then unpack recovers the original bytes for arbitrary inputs" AND src/__property__/round-trip.property.test.ts::"chunked streaming pack matches whole-buffer chunked pack byte-for-byte" AND src/__property__/determinism.property.test.ts::"two packs with identical inputs and seeded Rng produce byte-identical bundles"


Requirement: PACKER-30 — mitata benchmark suite

The package SHALL ship a runnable mitata benchmark suite at src/__benchmarks__/packer.bench.ts covering: (a) whole-file Packer.pack over a fixed 1 KiB payload, (b) whole-file Unpacker.unpack over the result of (a), (c) chunked packChunks over a single-chunk source, (d) chunked packChunks over a 1000-chunk source, (e) computeWholeFileIntegrity over a fixed 1 KiB payload, and (f) a plugin-overhead comparison (Packer.pack with zero plugins vs. with a no-op plugin). The benchmark MUST be runnable via bun run benchmark declared in package.json and MUST execute without error. Baseline numbers MUST be published in README.md under a ## Benchmarks section with a hardware/runtime context line.

Scenario: bun run benchmark executes the mitata suite

  • WHEN bun run benchmark is invoked at the package root
  • THEN the suite completes without throwing and emits mitata's standard ops/sec summary for every named case

Scenario: every named case is registered with mitata

  • WHEN the benchmark module is statically inspected for bench( invocations
  • THEN there are at least six distinct bench("...") calls matching the named cases (whole-file pack, whole-file unpack, chunked single, chunked 1000, integrity verify, plugin overhead)

Verified by: src/benchmarks-conformance/packer.bench.spec-conformance.test.ts::"declares the six named benchmark cases" AND src/benchmarks-conformance/packer.bench.spec-conformance.test.ts::"package.json declares a bun run benchmark script" AND src/benchmarks-conformance/packer.bench.spec-conformance.test.ts::"README.md publishes a Benchmarks section with a hardware context line"