pii · API Reference
JavaScript API
The @cognipeer/pii package (CommonJS, Node 18+, require('@cognipeer/pii')). See Python API for the mirrored surface — the two are independent ports of the same algorithm, checked against shared parity fixtures.
Entry points
const {
detect, redact, mask, tokenizeText, detokenizeText,
detectAsync, redactAsync, maskAsync, tokenizeAsync,
categories, categoriesById,
categoryLabel, categoryDescription, filterCategoriesByLanguages,
buildDefaultCategories,
} = require('@cognipeer/pii');Synchronous functions (pattern tier only)
These never touch the dictionary or NER tiers, regardless of options.detection.mode — they always run the pattern (L1) tier.
function detect(text: string, options?: Options): ScanResult
function redact(text: string, options?: Options): ScanResult
function mask(text: string, options?: Options): ScanResult
function tokenizeText(text: string, options?: Options): ScanResult & { vault: Vault }
function detokenizeText(text: string, vault: Vault): { outputText: string }const pii = require('@cognipeer/pii');
pii.detect('e-postam ahmet@example.com, IBAN: TR330006100519786457841326');
pii.redact('...'); // findings removed, replaced with [REDACTED_<CATEGORY>]
pii.mask('...'); // findings replaced with a partial mask ([CATEGORY] or j***@gmail.com-style)
const { outputText, vault } = pii.tokenizeText('Kart no: 4111111111111111');
pii.detokenizeText(outputText, vault); // restores the original textAsync functions (opt into dictionary and NER tiers)
Identical signatures, Promise-returning, and the only way to reach pattern+dictionary, pattern+dictionary+ner, and pattern+ner-verified:
function detectAsync(text: string, options?: Options): Promise<ScanResult>
function redactAsync(text: string, options?: Options): Promise<ScanResult>
function maskAsync(text: string, options?: Options): Promise<ScanResult>
function tokenizeAsync(text: string, options?: Options): Promise<ScanResult & { vault: Vault }>const withNer = await pii.detectAsync(text, {
detection: {
mode: 'pattern+dictionary+ner',
ner: { minScore: 0.5, maxConcurrent: 2, timeoutMs: 1500 },
},
});
const verified = await pii.detectAsync(text, {
detection: { mode: 'pattern+ner-verified' },
});The Options object
Shared by every function above:
| Option | Default | What it does |
|---|---|---|
categories | Every defaultEnabled category on | { email: true, tc_kimlik: false, ... } — explicitly enable/disable per category. Passing this object replaces the default set entirely rather than merging with it. |
customPatterns | none | Your own regex rules, run through a bounded vm.Script timeout so a catastrophic pattern cannot hang the process. See the shape below. |
languages | undefined (all) | Restrict to categories tagged for these languages, e.g. ['tr']. 'global'-tagged categories always run regardless. |
locale | 'en' | Passed through to category label/message lookups ('en' or 'tr'). |
detection.mode | 'pattern' | 'pattern' | 'pattern+dictionary' | 'pattern+dictionary+ner' | 'pattern+ner-verified'. |
detection.contextBoost | true | Whether a nearby context keyword boosts a candidate's confidence. Set false to disable. |
detection.minConfidence | 0 (0.4 in pattern+ner-verified when unset) | Drops findings below this fused confidence. |
detection.ner | — | { minScore?, maxChars?, timeoutMs?, maxConcurrent?, failMode?, decode? } — see Detection modes. |
detection.verify | see Detection modes | pattern+ner-verified-only: { agreeWeight: 0.5, floor: 0.25, checksumFloor: 0.7, contextFloor: 0.8, dictionaryFloor: 0.5, nerOnlyWeight: 0.6, nerOnlyNumberWeight: 0.35 }. |
customPatterns[] entries
interface CustomPattern {
categoryId: string;
pattern: string; // regex source, max 512 characters
flags?: string;
enabled: boolean;
severity?: 'low' | 'medium' | 'high';
label?: string;
labels?: Record<string, string>;
languages?: string[]; // applies to all requested languages if omitted
}Return shape: ScanResult
interface ScanResult {
inputLength: number;
findings: Finding[];
outputText: string; // unchanged for 'detect'; replacements applied otherwise
hasBlocking: boolean; // true if any finding has block: true
action: 'detect' | 'redact' | 'mask' | 'tokenize';
languages: string[]; // ['global'] if none were requested
degraded?: DegradedNote[]; // present only when non-empty — NER fallback/timeout/clip reasons
}
interface Finding {
category: string;
value: string;
start: number; // UTF-16 code unit offset — see the offset note below
end: number;
label: string;
message: string;
severity: 'low' | 'medium' | 'high';
confidence: number;
action: string;
block: boolean;
replacement: string;
detector: 'pattern' | 'dictionary';
evidence: string[];
}tokenizeText/tokenizeAsync additionally return vault: Record<string, { value: string; category: string }>, keyed by the exact token strings ([EMAIL_1], [TC_KIMLIK_1], …) that appear in outputText.
Categories catalog
const { categories, categoriesById, categoryLabel, categoryDescription, filterCategoriesByLanguages, buildDefaultCategories } = require('@cognipeer/pii');
categories.length; // 31
categoriesById.tc_kimlik.defaultEnabled; // true
buildDefaultCategories(); // { email: true, phone: true, ..., tr_vkn: false, ... }See Categories for the full table with languages, defaults, and validators.
The UTF-16 vs. code-point offset note
JavaScript strings are UTF-16; the Python port's strings are Unicode code points. For text containing a non-BMP character (most emoji), a single such character counts as 2 units in a JS string but 1 in a Python string, so start/end offsets after that point differ by one per character between the two ports. Neither is wrong — each is correct for slicing the string in its own language — and redact()/mask()/tokenizeText() all produce byte-identical output text in both languages regardless. A caller moving raw offsets between the JS and Python ports (rather than just using each port's own outputText) needs to convert them.
Where to go next
- Python API — the mirrored surface, same option names in Python's casing.
- Detection modes — the full meaning of every
detectionoption. - Redaction & tokens — mask strategies and the finding shape in depth.

