# `flags/flags` — feature-flagged format-version rollouts _(no submodules)_ ## 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.