3.9 KiB
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 (falseforpacker.format.v2).resolvePackerFlags(opts)— adapter that accepts either a rawPackerFeatureFlagsprovider or a{ evaluator, context }bundle pointing at a@suaveplan/feature-flagsEvaluator.enforceFormatVersionGate(flags, resolver)— production write-path helper that throwsPACKER.FORMAT_VERSION_UNSUPPORTEDwhen 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
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
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 internalPackerFeatureFlagssurface; defaults todefaultPackerFlags()whenoptsis undefined.
- Adapts either
enforceFormatVersionGate(flags, resolver): void- Throws
PackerError(PACKER.FORMAT_VERSION_UNSUPPORTED)whenpacker.format.v2resolves totrue.
- Throws
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.