first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
## index
**Path:** `bitpacker-strategies/src/index.ts`
**Export:** See source for details.
### What it does
Part of the @suaveplan/bitpacker-strategies package.

View File

@@ -0,0 +1,76 @@
# Bitpacker Strategies OTel Instruments
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
OTel meter, tracer, counter, and histogram declarations for `@suaveplan/bitpacker-strategies`. The package's only public boundary is `registerDefaultStrategies(manager)` — strategies are stateless chain handlers invoked from inside the parent `bitpacker.pack` / `bitpacker.unpack` span owned by `@suaveplan/bitpacker`. Per the proposal "one span per top-level call; per-chunk events nested", strategy invocations do NOT get their own spans; only the bootstrap call `registerDefaultStrategies` is instrumented.
The metric and span names follow the suaveplan convention with snake-case for the multi-word package: tracer/meter named `@suaveplan/bitpacker-strategies`; span named `bitpacker_strategies.register`; counter/histogram named `bitpacker_strategies.register.<metric>`.
## Features
- `getBitPackerStrategiesTracer()` — returns the package's tracer
- `registerStrategiesTotal` — counter labelled by `success`
- `registerStrategiesDurationMs` — histogram of bootstrap duration
- `BITPACKER_STRATEGIES_SPAN_NAMES` — `{ register: "bitpacker_strategies.register" }`
## Usage: Basic Example
```ts
import { PackingStrategyManager } from "@suaveplan/bitpacker";
import { registerDefaultStrategies } from "@suaveplan/bitpacker-strategies";
const manager = new PackingStrategyManager();
registerDefaultStrategies(manager);
// One span emitted: "bitpacker_strategies.register"
// Counter incremented: bitpacker_strategies.register.total{success="true"}
// Histogram recorded: bitpacker_strategies.register.duration_ms
```
## Usage: Advanced Example
```ts
import { trace } from "@opentelemetry/api";
import { PackingStrategyManager } from "@suaveplan/bitpacker";
import {
BITPACKER_STRATEGIES_SPAN_NAMES,
registerDefaultStrategies,
} from "@suaveplan/bitpacker-strategies";
trace
.getTracer("my-app")
.startActiveSpan(
BITPACKER_STRATEGIES_SPAN_NAMES.register + ".wrapper",
(span) => {
const manager = new PackingStrategyManager();
registerDefaultStrategies(manager);
// bitpacker_strategies.register is a child span here.
span.end();
}
);
```
## API Reference
### `getBitPackerStrategiesTracer(): Tracer`
Returns the OTel `Tracer` instance for the package.
### `registerStrategiesTotal: Counter`
Counter incremented once per `registerDefaultStrategies` call. Labels: `success: "true" | "false"`.
### `registerStrategiesDurationMs: Histogram`
Histogram of elapsed wall-clock milliseconds per bootstrap call. No labels.
### `BITPACKER_STRATEGIES_SPAN_NAMES`
Const map: `{ register: "bitpacker_strategies.register" }`.
## Implementation Notes
- **No per-strategy spans.** The 30 shipped strategies are invoked thousands of times per pack/unpack on large schemas; one span each would explode trace volume. They run inside the parent `bitpacker.pack` / `bitpacker.unpack` span without instrumentation.
- **Bootstrap-only counter.** `registerStrategiesTotal` is incremented once per `registerDefaultStrategies` call. A consumer can reason about chain-bootstrap frequency from this counter without correlating across packages.

View File

@@ -0,0 +1,72 @@
# Register Default Strategies — Schema
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Zod schema for the `manager` argument of `registerDefaultStrategies`. Validates that an external caller hands the function a real `PackingStrategyManager` instance (not a plain object that "quacks like" one) so the chain ordering invariants the function depends on are guaranteed at the boundary. Identity is preserved — the parsed value is the same instance the caller passed in, so `registerDefaultStrategies(manager)` mutates the caller's manager rather than a clone.
This package's strategy classes are themselves parameterless and therefore expose no per-strategy options to validate. The single boundary that needs a schema is `registerDefaultStrategies(manager)`. Strategy *shape* (`canHandle`/`pack`/`unpack` are functions) is already validated upstream by `packingStrategySchema` in `@suaveplan/bitpacker`.
## Features
- `packingStrategyManagerSchema` — `z.custom<PackingStrategyManager>` `instanceof` check
- Identity preservation (no wrapping)
- Rejects null, undefined, and plain objects that share the manager API surface
## Usage: Basic Example
```ts
import {
PackingStrategyManager,
BitPackerError,
BITPACKER_CODES,
} from "@suaveplan/bitpacker";
import {
packingStrategyManagerSchema,
registerDefaultStrategies,
} from "@suaveplan/bitpacker-strategies";
const manager = packingStrategyManagerSchema.parse(new PackingStrategyManager());
registerDefaultStrategies(manager);
```
## Usage: Advanced Example
```ts
import {
PackingStrategyManager,
BitPackerError,
BITPACKER_CODES,
} from "@suaveplan/bitpacker";
import {
packingStrategyManagerSchema,
registerDefaultStrategies,
} from "@suaveplan/bitpacker-strategies";
function bootstrap(rawManager: unknown): PackingStrategyManager {
const result = packingStrategyManagerSchema.safeParse(rawManager);
if (!result.success) {
throw new BitPackerError(
BITPACKER_CODES.INVALID_SCHEMA,
"registerDefaultStrategies received a non-manager value",
{ cause: result.error }
);
}
registerDefaultStrategies(result.data);
return result.data;
}
```
## API Reference
### `packingStrategyManagerSchema: z.ZodType<PackingStrategyManager>`
Strict `instanceof` validator. Returns the input value unchanged on success; throws `ZodError` with the message `"manager must be a PackingStrategyManager instance"` on failure.
## Implementation Notes
- **No per-strategy schemas.** Every shipped strategy in this package is parameterless — `new ArrayPackingStrategy()`, `new ObjectPackingStrategy()`, etc. There is nothing per-strategy to validate at construction time. Strategy shape (`canHandle`/`pack`/`unpack`) is enforced by `packingStrategySchema` in `@suaveplan/bitpacker` at the registration boundary, so this package does not duplicate that validation.
- **`instanceof` over duck-typing.** A plain object with `add`/`register` methods would compile, but the order-sensitive insertion logic in `PackingStrategyManager.register` (insert before catch-all) depends on the class internals. Refusing duck-types prevents subtle ordering bugs that would only surface deep into a pack/unpack chain.

View File

@@ -0,0 +1,14 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## register-default-strategies
**Path:** `bitpacker-strategies/src/register-default-strategies.ts`
**Export:** See source for details.
### What it does
Part of the @suaveplan/bitpacker-strategies package.

View File

@@ -0,0 +1,68 @@
# Register Schemas — `@suaveplan/bitpacker-strategies`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Idempotent integration with `@suaveplan/schema-registry` for the `@suaveplan/bitpacker-strategies` package. Exposes a single `registerBitPackerStrategiesSchemas(registry)` function that converts the package's only public boundary schema (`packingStrategyManagerSchema`) into JSON Schema form and registers it under the canonical subject `bitpacker:@suaveplan/bitpacker-strategies` at version 1.
This package's strategies are parameterless, so the only registered surface is the boundary check on `registerDefaultStrategies(manager)` — the strategy classes themselves are validated by `@suaveplan/bitpacker`'s `packingStrategySchema`. Mirroring the cross-package shape avoids duplicating those schemas here.
## Features
- `BITPACKER_STRATEGIES_SUBJECT` constant — `"bitpacker:@suaveplan/bitpacker-strategies"`
- `BITPACKER_STRATEGIES_SCHEMA_VERSION` constant — `1`
- `registerBitPackerStrategiesSchemas(registry)` — idempotent registration
## Usage: Basic Example
```ts
import { createRegistry } from "@suaveplan/schema-registry";
import {
BITPACKER_STRATEGIES_SUBJECT,
registerBitPackerStrategiesSchemas,
} from "@suaveplan/bitpacker-strategies/register-schemas";
const registry = createRegistry();
registerBitPackerStrategiesSchemas(registry);
console.log(registry.getLatest(BITPACKER_STRATEGIES_SUBJECT).version); // 1
```
## Usage: Advanced Example
```ts
import { createRegistry, SchemaRegistry } from "@suaveplan/schema-registry";
import { registerBitPackerSchemas } from "@suaveplan/bitpacker/register-schemas";
import { registerBitPackerStrategiesSchemas } from "@suaveplan/bitpacker-strategies/register-schemas";
function bootstrapAll(registry: SchemaRegistry) {
registerBitPackerSchemas(registry);
registerBitPackerStrategiesSchemas(registry);
}
const registry = createRegistry();
bootstrapAll(registry);
bootstrapAll(registry); // safe: idempotent
```
## API Reference
### `BITPACKER_STRATEGIES_SUBJECT: "bitpacker:@suaveplan/bitpacker-strategies"`
Const string. The exact subject under which the package's schemas are registered.
### `BITPACKER_STRATEGIES_SCHEMA_VERSION: 1`
Const number. The version under which the package's schemas are registered.
### `registerBitPackerStrategiesSchemas(registry: SchemaRegistry): void`
Idempotent registration. If the subject is already present in `registry.listSubjects()`, returns without writing. Otherwise registers the `packingStrategyManager` payload with `{ namespace, package }` metadata.
## Implementation Notes
- **Single-key payload.** This package's only boundary schema is `packingStrategyManagerSchema`. The payload object keeps a single key (`packingStrategyManager`) for parity with the larger families' payloads, so a downstream consumer can pick the schema by name.
- **Idempotency via `listSubjects()` check.** Same pattern as the core package — a membership test is sufficient to keep `version: 1` stable across re-imports.
- **`unrepresentable: "any"`.** The strategy-manager schema uses `z.custom` for the `instanceof` check; without this option, `z.toJSONSchema` throws. Coercing to `{}` is fine because consumers reading the JSON-Schema payload are typically asking "what does this package register?" not "validate me against this schema at runtime."

View File

@@ -0,0 +1,27 @@
# ArrayConfigPackingStrategy
Packs/unpacks arrays described by an explicit configuration object.
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Schema shape
```ts
{
type: "array",
maxLength: number,
elementType: "integer" | "float" | "string" | "boolean" | "schema",
elementMax?: number, // for integer / float / string
elementSchema?: BitPackerSchema // for elementType "schema"
}
```
## Behaviour
- Writes the array length using the minimum bits required for `maxLength`.
- Each element is packed according to its `elementType`.
- `float` elements are encoded as unsigned integers up to `elementMax`.
- `string` elements are encoded as fixed-length byte sequences (padded with null bytes).
- `schema` elements are recursively packed via the strategy chain.

View File

@@ -0,0 +1,22 @@
# ArrayNotationPackingStrategy
Packs/unpacks arrays described by a string notation.
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Schema shape
String tokens of the form `-length[elementSchema]`, e.g.:
- `-5[10]` → array of up to 5 integers (max 10)
- `-5[true]` → array of up to 5 booleans
- `-3[{"x":255}]` → array of up to 3 objects
## Behaviour
Parses the notation into an `ArrayConfig` and packs:
- Length prefix using the minimum bits for `length`.
- Elements encoded according to the inner schema.

View File

@@ -0,0 +1,22 @@
# ArraySchemaPackingStrategy
Packs/unpacks fixed-length tuple schemas.
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Schema shape
An array where each element is a distinct schema:
```ts
[255, "-16", true] // tuple of [uint8, variable-string, boolean]
```
## Behaviour
- No length prefix is written (the schema itself defines the arity).
- Each data element is packed/unpacked against the schema at the same index.
- Arrays matching suaveplan homogeneous formats (`[schema]` or `[number, schema]`)
are delegated to `ArrayPackingStrategy`.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## array
**Path:** `bitpacker-strategies/src/strategies/array/array.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,18 @@
# BigIntArrayPackingStrategy
Packs/unpacks arrays of 64-bit BigInt values.
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Schema tokens
- `"bigint-i64[]"` — signed 64-bit BigInt array
- `"bigint-u64[]"` — unsigned 64-bit BigInt array
## Behaviour
- Accepts `bigint[]`, `BigInt64Array`, or `BigUint64Array`.
- Writes a 16-bit unsigned length prefix.
- Each element is written as 8 bytes (big-endian).

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## bigint
**Path:** `bitpacker-strategies/src/strategies/bigint/bigint.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## bitarray
**Path:** `bitpacker-strategies/src/strategies/bitarray/bitarray.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## bitset
**Path:** `bitpacker-strategies/src/strategies/bitset/bitset.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## blob
**Path:** `bitpacker-strategies/src/strategies/blob/blob.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## boolean
**Path:** `bitpacker-strategies/src/strategies/boolean/boolean.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## conditional
**Path:** `bitpacker-strategies/src/strategies/conditional/conditional.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## customType
**Path:** `bitpacker-strategies/src/strategies/customType/customType.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## datetime
**Path:** `bitpacker-strategies/src/strategies/datetime/datetime.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## defs
**Path:** `bitpacker-strategies/src/strategies/defs/defs.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## enum
**Path:** `bitpacker-strategies/src/strategies/enum/enum.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## map
**Path:** `bitpacker-strategies/src/strategies/map/map.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## null
**Path:** `bitpacker-strategies/src/strategies/null/null.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## number
**Path:** `bitpacker-strategies/src/strategies/number/number.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## object
**Path:** `bitpacker-strategies/src/strategies/object/object.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## oneOf
**Path:** `bitpacker-strategies/src/strategies/oneOf/oneOf.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,23 @@
# OptionalMarkerPackingStrategy
Packs/unpacks optional values using an explicit marker object.
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Schema shape
```ts
{
_optional: true,
_schema: BitPackerSchema,
_default?: unknown // returned when value is absent
}
```
## Behaviour
- Writes a single boolean presence bit.
- When present, the inner `_schema` is packed/unpacked normally.
- When absent on unpack, returns `_default` if defined, otherwise `undefined`.

View File

@@ -0,0 +1,22 @@
# OptionalSuffixPackingStrategy
Packs/unpacks optional primitive schemas using a `?` suffix.
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Schema tokens
Any valid schema string followed by `?`, e.g.:
- `"uuid?"` → optional UUID
- `"20?"` → optional fixed 20-byte string
- `"-20?"` → optional variable-length string
- `"varint?"` → optional varint
## Behaviour
- Writes a single boolean presence bit.
- When present, strips the `?` and delegates packing/unpacking to the base schema.
- When absent on unpack, returns `undefined`.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## optional
**Path:** `bitpacker-strategies/src/strategies/optional/optional.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## set
**Path:** `bitpacker-strategies/src/strategies/set/set.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## string
**Path:** `bitpacker-strategies/src/strategies/string/string.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,24 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## UnionPackingStrategy
**Export:** `UnionPackingStrategy` (class)
Packs and unpacks discriminated unions.
### Schema shape
```ts
{ _union: { on: "kind", cases: { move: {...}, chat: {...} } } }
```
### Behaviour
- **Pack:** reads the discriminator value from `data[on]`, writes its case index as a compact uint, then packs the data using the matching branch schema.
- **Unpack:** reads the case index, then unpacks using the corresponding branch schema.
### Registration order
Must be registered **before** `ObjectPackingStrategy` because the schema is an object shape.

View File

@@ -0,0 +1,12 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## varint
**Path:** `bitpacker-strategies/src/strategies/varint/varint.ts`
**Export:** See source for details.
### What it does
Packing strategy for @suaveplan/bitpacker.

View File

@@ -0,0 +1,103 @@
# VersionPackingStrategy
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Packing strategy for the version-pinned schema shape `{ _version: number; _schema: BitPackerSchema; _tolerance?: "strict" | "forward"; _allowedVersions?: number[] }`. Writes a 16-bit unsigned big-endian wire-version followed by the schema-encoded payload. On unpack, the strategy reads the wire-version and either matches it strictly against `_version` (the default) or accepts a configurable set of alternate versions when the schema opts into forward tolerance.
Forward tolerance is the right choice for clients that read newer-version payloads from a server they don't control: setting `_tolerance: "forward"` lets the client decode a future-versioned wire format using its current schema, as long as the consumer guarantees structural compatibility (e.g. fields added in the newer version are wrapped in `_optional` so they decode as `undefined` against the older schema). Strict mode remains the default because most consumers benefit from the early failure of a version mismatch.
## Features
- 16-bit big-endian wire-version prefix (range `[0, 65535]`)
- Strict mode (default) — throws `BitpackerError(INVALID_SCHEMA)` on any wire-version mismatch
- Forward-tolerant mode (`_tolerance: "forward"`) — accepts alternate versions
- Optional `_allowedVersions: number[]` — restricts forward-tolerant acceptance to a curated allowlist
- Wire format unchanged across all modes — only the unpack-time validation changes
## Usage: Basic Example
```ts
import { BitPacker } from "@suaveplan/bitpacker";
import { registerDefaultStrategies } from "@suaveplan/bitpacker-strategies";
const bp = new BitPacker();
registerDefaultStrategies(bp.strategyManager);
const schema = { _version: 1, _schema: 255 };
const bytes = bp.pack(42, schema);
console.log(bp.unpack(bytes, schema)); // 42
```
## Usage: Advanced Example
```ts
import { BitPacker, BitpackerError, BITPACKER_CODES } from "@suaveplan/bitpacker";
import { registerDefaultStrategies } from "@suaveplan/bitpacker-strategies";
const bp = new BitPacker();
registerDefaultStrategies(bp.strategyManager);
// Write at version 1:
const writeSchema = { _version: 1, _schema: 255 };
const wireBytes = bp.pack(42, writeSchema);
// Read with forward tolerance, accepting versions 1-3:
const readSchema = {
_version: 1,
_schema: 255,
_tolerance: "forward" as const,
_allowedVersions: [1, 2, 3],
};
console.log(bp.unpack(wireBytes, readSchema)); // 42
// Reading a payload tagged version 9 with the same readSchema fails:
const futureBytes = bp.pack(42, { _version: 9, _schema: 255 });
try {
bp.unpack(futureBytes, readSchema);
} catch (err) {
if (err instanceof BitpackerError && err.code === BITPACKER_CODES.INVALID_SCHEMA) {
// Error context: { expected: 1, received: 9, allowedVersions: [1, 2, 3] }
}
}
```
## API Reference
### `class VersionPackingStrategy implements PackingStrategy`
#### `canHandle(schema)`
Returns `true` if `isVersionSchema(schema)` is `true`.
#### `pack(data, schema, ctx)`
Writes 16 bits of `_version` masked to 16 bits, then delegates to `ctx.packValue(data, schema._schema)`.
#### `unpack(schema, ctx)`
Reads 16 bits of wire-version. Behaviour depends on `_tolerance`:
- `"strict"` (default, or omitted): throw `BitpackerError(INVALID_SCHEMA)` on mismatch.
- `"forward"` with `_allowedVersions`: throw if the wire-version is NOT in the allowlist.
- `"forward"` without `_allowedVersions`: accept any wire-version in `[0, 65535]`.
On acceptance, delegates to `ctx.unpackValue(schema._schema)`.
### `function isVersionSchema(value): value is VersionSchemaShape`
Type guard. Returns `true` for any object with `_version: number` (in `[0, 65535]`), `_schema: BitPackerSchema`, optional `_tolerance: "strict" | "forward"`, and optional `_allowedVersions: number[]` (each in `[0, 65535]`).
### `type VersionTolerance = "strict" | "forward"`
The two supported tolerance modes.
## Implementation Notes
- **Wire format unchanged.** Forward-tolerance is purely an unpack-time decision. Bytes produced by a strict-mode pack are byte-identical to those produced by a forward-mode pack at the same `_version`. Bytes produced by either mode are unpacked equivalently when wire-versions match.
- **Schema identity on success.** When forward-tolerance accepts an alternate wire-version, the strategy still uses `_schema` for decoding. This places the burden on the consumer to ensure the schema can decode the alternate wire format. Typical pattern: wrap fields added in newer versions in `_optional` so they decode as `undefined` against the older schema.
- **`_allowedVersions: undefined` is permissive, `[]` is restrictive.** An empty allowlist accepts no alternate versions (forward-tolerant mode collapses to strict). An undefined allowlist with `_tolerance: "forward"` accepts any wire-version.
- **No silent migration.** The strategy does NOT auto-migrate fields between versions. Migrations are the consumer's responsibility — typically encoded as `_optional` wrappers with default values, or as the richer `MigrationStrategy` framework in `@suaveplan/bitpacker`'s `schema-versioning-types`.
- **Stable wire-version cap at 16 bits.** A schema with `_version > 65535` is rejected by `isVersionSchema` and would not be dispatched to this strategy in the first place.