3.3 KiB
3.3 KiB
FeatureFlagPlugin
(no submodules)
Purpose
FeatureFlagPlugin wraps a @suaveplan/feature-flags FlagManager and
exposes its evaluation surface as a lifecycle-managed game-server plugin.
It folds a per-player userId into the evaluation context automatically
and records every evaluation to a rotating log so callers can replay or
audit flag decisions without instrumenting every callsite by hand.
The plugin is intentionally a thin adapter: it does not own the
provider chain, override semantics, or persistence story — those remain
in @suaveplan/feature-flags. Errors raised by the underlying manager
(notably FLAG_NOT_FOUND) propagate unchanged so callers can catch the
canonical FeatureFlagsError discriminator.
Features
- Lifecycle-managed via
BasePlugin— clears the evaluation log on uninstall. - Per-player
EvaluationContextbuilding fromdefaultContext + playerId. isEnabled/evaluate/getString/getNumberhelpers wrapping the underlying manager API.- Rotating evaluation log (FIFO; default cap 1 000) for downstream analytics.
- Zod-validated
maxLogEntriesoption.
Usage: Basic
import {
FlagManager,
FlagRegistry,
Evaluator,
MemoryProvider,
} from "@suaveplan/feature-flags";
import { FeatureFlagPlugin } from "@suaveplan/game-server/plugins/feature-flag-plugin";
const provider = new MemoryProvider({
flags: [
{ key: "boss-rush", type: "boolean", defaultValue: false },
],
});
const registry = new FlagRegistry({ provider });
await registry.load();
const evaluator = new Evaluator();
const flagManager = new FlagManager({ registry, evaluator });
const plugin = new FeatureFlagPlugin({ flagManager });
plugin.isEnabled("boss-rush", "player-1");
Usage: Advanced
import { FeatureFlagPlugin } from "@suaveplan/game-server/plugins/feature-flag-plugin";
const plugin = new FeatureFlagPlugin({
flagManager,
defaultContext: { attributes: { region: "us-west" } },
maxLogEntries: 50,
clock: () => 1_700_000_000_000,
});
await plugin.onInstall(ctx);
plugin.evaluate("boss-rush", "p1");
plugin.getString("hero-skin", "p1");
plugin.getNumber("drop-rate-multiplier", "p1");
const log = plugin.getEvaluationLog();
API Reference
class FeatureFlagPlugin extends BasePluginconstructor(options: FeatureFlagPluginOptions)isEnabled(key, playerId?): booleanevaluate<T>(key, playerId?): FlagEvaluationResult<T>getString(key, playerId?): stringgetNumber(key, playerId?): numbergetEvaluationLog(): readonly EvaluationLogEntry[]clearLog(): void
Implementation Notes
- The evaluation log is bounded; once
evaluationLog.lengthexceedsmaxLogEntries, the oldest entries are pruned viaslice(-max). buildContextmergesdefaultContextwith the per-call player id.userIdfrom the default context is preserved when the caller does not supply aplayerId.- Errors thrown by
FlagManager(e.g.FLAG_NOT_FOUND) bubble up asFeatureFlagsError. The plugin does not translate them so callers retain access to the underlying error code without an extra cause unwrap. - The plugin holds the
flagManagerby reference and never disposes it. Disposal is the caller's responsibility because the manager may be shared across plugins.