2.5 KiB
sampling.ts
(no submodules)
Purpose
Implements head-based sampling for @suaveplan/analytics-browser. The tracker calls sampler.shouldSample(event) after the consent gate but before the scrubber so events that will be dropped never pay the cost of property scrubbing or queue persistence. HeadSampler accepts a base sample rate in [0, 1] plus an optional Record<eventName, rate> override map so common events (e.g. page) can be captured at a different rate than rare events (e.g. error_reported).
Features
- Per-event-name overrides —
new HeadSampler(0.1, { error_reported: 1 })keeps every error while sampling everything else at 10 %. - Strict clamping — rates
<= 0always drop, rates>= 1always keep;NaNclamps to 0. - Injectable random source — the constructor accepts a
random: () => numberseam so tests can drive deterministic decisions without monkey-patchingMath.random. - Stateless — every call is independent; safe to share a sampler across many trackers.
Usage: Basic
import { HeadSampler } from "@suaveplan/analytics-browser/sampling";
const sampler = new HeadSampler(0.25); // keep 25 % of events
Usage: Advanced
import { HeadSampler } from "@suaveplan/analytics-browser/sampling";
// 1 % sampling for verbose page-view events, 100 % for clicks and errors.
const sampler = new HeadSampler(0.01, {
clicked: 1,
error_reported: 1,
});
// In tests, inject a deterministic counter so behaviour is reproducible.
let n = 0;
const deterministic = new HeadSampler(0.5, {}, () => {
const sequence = [0.4, 0.6, 0.2, 0.8];
return sequence[n++ % sequence.length] as number;
});
API Reference
class HeadSampler implements Samplernew HeadSampler(rate, overrides?, random?)rate: number— base rate in[0, 1]. Values outside this range clamp.overrides?: Readonly<Record<string, number>>— per-event-name rate. Each entry clamps independently.random?: () => number— random source; defaults toMath.random.
shouldSample(event): boolean—truekeeps the event,falsedrops it.
Implementation Notes
The clamp logic guards against floating-point drift and pathological caller input (e.g. accidentally passing rate=2). NaN clamps to 0 rather than 1 so a misconfigured environment variable does not silently flood the backend. The injected random source enables deterministic testing without resorting to vi.stubGlobal (banned by §2 of AGENTS.md).