Files
skills/docs/observability/monitor/analytics-browser/src/pii/pii.md
2026-05-29 08:20:56 +01:00

2.9 KiB

pii.ts

(no submodules)

Purpose

Implements field-level PII scrubbing for @suaveplan/analytics-browser. scrubPII(event, rules) takes an AnalyticsEvent plus an ordered array of ScrubRules and returns a new event with the affected properties either redacted, replaced with a deterministic fingerprint, or dropped entirely. Scrubbing runs on the tracker hot path between consent / sampling decisions and the queue write, so it must be synchronous, side-effect-free, and never throw on valid input. The function clones properties via structuredClone so the original event remains immutable.

Features

  • Three strategies: "redact" (replace value with "[REDACTED]"), "hash" (replace with FNV-1a fingerprint), "drop" (delete the key).
  • Dotted-path field resolution — "user.email" walks into nested objects.
  • Order-preserving — rules apply in the order given so later rules see the effect of earlier ones.
  • Pure / synchronous — no async, no I/O, no subtle.digest.
  • Idempotent — the same input + rules always yields the same output (including the fingerprint).

Usage: Basic

import { scrubPII } from "@suaveplan/analytics-browser/pii";

const safe = scrubPII(event, [
    { field: "email", strategy: "redact" },
    { field: "ssn", strategy: "drop" },
]);

Usage: Advanced

import type { ScrubRule } from "@suaveplan/analytics-browser/types";
import { scrubPII } from "@suaveplan/analytics-browser/pii";

const piiRules: readonly ScrubRule[] = [
    { field: "user.email", strategy: "redact" },
    { field: "user.phone", strategy: "hash" },
    { field: "payment.cardNumber", strategy: "drop" },
];

function safeTrack(name: string, properties: Record<string, unknown>) {
    const wrapped = {
        id: crypto.randomUUID(),
        name,
        properties,
        timestamp: new Date().toISOString(),
        sessionId: "s",
        anonymousId: "a",
    };
    return scrubPII(wrapped, piiRules);
}

API Reference

  • scrubPII(event: AnalyticsEvent, rules: readonly ScrubRule[]): AnalyticsEvent — returns a fresh event with scrubbed properties. Returns the original event reference when rules.length === 0.
  • Rule shape: { field: string; strategy: "redact" | "hash" | "drop" }.

Implementation Notes

The fingerprint is FNV-1a 32-bit, encoded as 8 lowercase hex chars. FNV-1a was chosen over Web Crypto's subtle.digest because the latter is asynchronous, which would force the entire scrub pipeline onto a Promise chain and complicate the tracker's hot path. FNV-1a is NOT a cryptographic hash — it is a redaction that preserves cross-event correlation (the same value always fingerprints to the same hex string) without revealing the value itself. If a stronger guarantee is required, downstream code SHOULD scrub via a custom rule sequence and a pre-hashed input value, or implement its own Sampler-style hook.