first commit
This commit is contained in:
107
docs/foundation/data/packer/src/flags/flags.md
Normal file
107
docs/foundation/data/packer/src/flags/flags.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# `flags/flags` — feature-flagged format-version rollouts
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`flags.ts` wires `@suaveplan/packer` into the `@suaveplan/feature-flags`
|
||||
ecosystem so future wire-format versions can be rolled out behind
|
||||
runtime flags without forcing every consumer to upgrade in lockstep.
|
||||
Today the package writes V1 unconditionally; the flag surface is the
|
||||
contract that a future V2 codec slots into. The exported
|
||||
`defaultPackerFlags()` provider is a no-op that returns the V1 default
|
||||
for every flag, so callers who don't care about rollouts never have to
|
||||
configure anything.
|
||||
|
||||
## Features
|
||||
|
||||
- `PACKER_FLAG_KEYS.FORMAT_V2 = "packer.format.v2"` — single source of
|
||||
truth for the V2 rollout flag key.
|
||||
- `defaultPackerFlags()` — no-op provider; every flag resolves to its
|
||||
V1 default (`false` for `packer.format.v2`).
|
||||
- `resolvePackerFlags(opts)` — adapter that accepts either a raw
|
||||
`PackerFeatureFlags` provider or a `{ evaluator, context }` bundle
|
||||
pointing at a `@suaveplan/feature-flags` `Evaluator`.
|
||||
- `enforceFormatVersionGate(flags, resolver)` — production write-path
|
||||
helper that throws `PACKER.FORMAT_VERSION_UNSUPPORTED` when V2 is
|
||||
enabled (V2 codec is a future change).
|
||||
- Boolean flags only — the package's flag surface is intentionally
|
||||
small; semver versions are records under `@suaveplan/schema-registry`,
|
||||
not under feature flags.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { Packer } from "@suaveplan/packer";
|
||||
|
||||
// No `flags` option — V1 default behaviour.
|
||||
const packer = new Packer({ encryption: { algorithm: "aes-256-gcm", key } });
|
||||
const packed = await packer.pack(payload);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
Evaluator,
|
||||
FlagRegistry,
|
||||
MemoryProvider,
|
||||
UserTargetStrategy,
|
||||
} from "@suaveplan/feature-flags";
|
||||
import { Packer } from "@suaveplan/packer";
|
||||
import { PACKER_FLAG_KEYS } from "@suaveplan/packer/flags";
|
||||
|
||||
// Roll out V2 to a 1% canary via a feature-flags Evaluator.
|
||||
const provider = new MemoryProvider();
|
||||
const registry = new FlagRegistry({ provider });
|
||||
registry.register({
|
||||
key: PACKER_FLAG_KEYS.FORMAT_V2,
|
||||
type: "boolean",
|
||||
defaultValue: false,
|
||||
});
|
||||
const evaluator = new Evaluator({
|
||||
strategies: [
|
||||
new UserTargetStrategy({ users: ["canary-1"], value: true }),
|
||||
],
|
||||
});
|
||||
|
||||
const packer = new Packer({
|
||||
encryption: { algorithm: "aes-256-gcm", key },
|
||||
flags: { evaluator, context: { userId: "canary-1" } },
|
||||
});
|
||||
|
||||
// In this canary build the call would throw FORMAT_VERSION_UNSUPPORTED
|
||||
// until V2 ships.
|
||||
await packer.pack(payload).catch((err) => {
|
||||
console.log(err.code); // "PACKER.FORMAT_VERSION_UNSUPPORTED"
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `defaultPackerFlags(): PackerFeatureFlags`
|
||||
- No-op provider returning V1 defaults for every flag.
|
||||
- `resolvePackerFlags(opts?: PackerFlagsOptions): PackerFeatureFlags`
|
||||
- Adapts either `{ evaluator, context? }` or `{ provider }` into the
|
||||
internal `PackerFeatureFlags` surface; defaults to
|
||||
`defaultPackerFlags()` when `opts` is undefined.
|
||||
- `enforceFormatVersionGate(flags, resolver): void`
|
||||
- Throws `PackerError(PACKER.FORMAT_VERSION_UNSUPPORTED)` when
|
||||
`packer.format.v2` resolves to `true`.
|
||||
- `PACKER_FLAG_KEYS`
|
||||
- Frozen object exposing every flag key used by the package.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The `Evaluator` adapter constructs a `FlagDefinition` on the fly from
|
||||
the internal `FLAG_DEFINITIONS` table — this keeps the public surface
|
||||
flat (callers don't have to register flags themselves) while still
|
||||
flowing through the canonical evaluation pipeline (strategies, default
|
||||
reasons, OTel counters wired up by `@suaveplan/feature-flags`). The
|
||||
provider surface is boolean-only by design; multi-valued flags would be
|
||||
a different abstraction. The V2 gate is invoked once per `Packer.pack`
|
||||
call, after option validation and idempotency lookup but before any
|
||||
expensive pipeline work, so an enabled V2 flag fails fast without
|
||||
producing partial side effects.
|
||||
Reference in New Issue
Block a user