Files
skills/docs/foundation/data/packer/src/flags/flags.md
2026-05-29 08:20:56 +01:00

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 (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

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 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.