import { type Counter, type Histogram, metrics } from "@opentelemetry/api"; /** Low-cardinality `mode` enum for the histograms below. */ export type GenerateMode = "random" | "pronounceable" | "passphrase" | "pin"; let _generateDurationMs: Histogram | undefined; let _entropyBits: Histogram | undefined; let _errorsTotal: Counter | undefined; function getInstruments(): { generateDurationMs: Histogram; entropyBits: Histogram; errorsTotal: Counter; } { const meter = metrics.getMeter("@suaveplan/password-generator"); _generateDurationMs ??= meter.createHistogram( "pwgen.generate_duration_ms", { description: "Wall-clock duration of a single successful password / passphrase / PIN generation", unit: "ms", } ); _entropyBits ??= meter.createHistogram("pwgen.entropy_bits", { description: "Estimated entropy of the generated secret", unit: "bit", }); _errorsTotal ??= meter.createCounter("pwgen.errors_total", { description: "Count of typed errors thrown from @suaveplan/password-generator", }); return { generateDurationMs: _generateDurationMs, entropyBits: _entropyBits, errorsTotal: _errorsTotal, }; } /** * Record a successful generation. Threading every production path through * a single helper keeps the attribute key set ("mode") consistent and the * cardinality bounded. */ export function recordGeneration( mode: GenerateMode, durationMs: number, bits: number ): void { const { generateDurationMs, entropyBits } = getInstruments(); generateDurationMs.record(durationMs, { mode }); entropyBits.record(bits, { mode }); } /** Record a typed error by stable code. */ export function recordError(code: string): void { const { errorsTotal } = getInstruments(); errorsTotal.add(1, { code }); } /** Reset the cached instruments (test-only). */ export function _resetInstruments(): void { _generateDurationMs = undefined; _entropyBits = undefined; _errorsTotal = undefined; } /** * Public-facing accessors so callers (and tests) can inspect the * underlying instruments. Lazy-evaluated so the active MeterProvider is * always honoured. */ export const generateDurationMs = { record(value: number, attrs: { mode: GenerateMode }): void { getInstruments().generateDurationMs.record(value, attrs); }, }; export const entropyBits = { record(value: number, attrs: { mode: GenerateMode }): void { getInstruments().entropyBits.record(value, attrs); }, }; export const errorsTotal = { add(value: number, attrs: { code: string }): void { getInstruments().errorsTotal.add(value, attrs); }, };