first commit
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
# `sampling.ts`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## 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 `<= 0` always drop, rates `>= 1` always keep; `NaN` clamps to 0.
|
||||
- Injectable random source — the constructor accepts a `random: () => number` seam so tests can drive deterministic decisions without monkey-patching `Math.random`.
|
||||
- Stateless — every call is independent; safe to share a sampler across many trackers.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { HeadSampler } from "@suaveplan/analytics-browser/sampling";
|
||||
|
||||
const sampler = new HeadSampler(0.25); // keep 25 % of events
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
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 Sampler`
|
||||
- `new 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 to `Math.random`.
|
||||
- `shouldSample(event): boolean` — `true` keeps the event, `false` drops 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`).
|
||||
Reference in New Issue
Block a user