Node.js SDK /
Zero-dependency PII redaction for Node.js. 86KB package, ~1.6ms per page.
Installation
$ npm install euredactredact()
Main entry point. Redact PII from a text string. Returns a RedactResult with the cleaned text and a list of detections.
function redact(
text: string,
options?: RedactOptions
): RedactResult| Parameter | Type | Default | Description |
|---|---|---|---|
text | string | — | The input text to redact. |
countries | string[] | undefined | Country codes (e.g. ["NL", "BE"]) the document is declared to belong to. Since 0.3.2 this scores detection rather than gating it: every pattern runs whatever you declare, and a match attributed outside the set comes back with outOfScope true rather than being dropped. Declaring it is worth more in this SDK than in the Python one: 99.72% recall against 99.43% blind, where Python gives up less without it. |
countryHint | string[] | undefined | A prior that resolves ambiguity without narrowing scope or flagging anything out of scope — use it when you know the likely origin but do not want outOfScope semantics. |
context | DocumentContext | undefined | Shares country evidence across the chunks of one document, so a chunk with no country signal of its own is still scored with what the rest of the document established. Pair with chunkOffset. Caching is disabled while a context is in use. |
referentialIntegrity | boolean | false | Replace PII with consistent labels (ENTITY_1, ENTITY_2, ...) instead of generic type labels. |
detectDates | boolean | false | Include DOB and date-of-death detections. Off by default. |
cache | boolean | true | Enable result caching for faster subsequent calls. |
Return value
A RedactResult object with:
| Parameter | Type | Default | Description |
|---|---|---|---|
redactedText | string | — | The text with PII replaced by labels like [NATIONAL_ID], [BANK_ACCOUNT], etc. |
detections | Detection[] | — | Array of detected PII entities with type, value, position, and country. |
source | string | — | Source identifier for the redaction engine. |
degraded | boolean | — | Whether the result was produced in a degraded mode. |
inferredCountries | [string, number][] | — | New in 0.3.2. The countries the engine concluded the document belongs to, each with a score in 0-1, most likely first. Populated whether or not you passed countries. |
evidence | CountryEvidence[] | — | New in 0.3.2. The individual signals behind inferredCountries, so an attribution can be audited rather than taken on faith. |
detectionMode | "declared" | "inferred" | — | New in 0.3.2. "declared" when you passed countries, "inferred" otherwise (including when you passed only countryHint). |
Detection
Each detection in the array contains:
| Parameter | Type | Default | Description |
|---|---|---|---|
entityType | EntityType | — | The type of PII detected (enum value). |
start | number | — | Start character index in the original text. |
end | number | — | End character index in the original text. |
text | string | — | The original PII text that was detected. |
source | DetectionSource | — | The detection engine that found this entity. |
country | string | null | — | The country code associated with this detection, or null. |
confidence | string | — | Confidence level of the detection. |
countryConfidence | number | — | New in 0.3.2. How strongly the document supports the country on this detection, 0-1. Separate from confidence, which is about the match itself: a pattern can fire unambiguously while its country attribution stays a guess. |
outOfScope | boolean | — | New in 0.3.2. True when the detection was attributed to a country outside the countries you declared. It is still redacted and still returned — filter on this field if you only want in-scope entities. |
CountryEvidence
One signal supporting a country attribution. New in 0.3.2.
| Parameter | Type | Default | Description |
|---|---|---|---|
country | string | — | The ISO country code this signal points at. |
source | string | — | What produced the signal, e.g. "ibanPrefix", "phonePrefix", "tld". |
logOdds | number | — | How much this signal moves the score. Signals accumulate, so several weak ones can outweigh a single strong one. |
span | [number, number] | — | Where in the text the signal was found. Offsets are absolute when chunkOffset is passed. |
DocumentContext
New in 0.3.2. Country inference reads signals out of the text, so a document processed in chunks loses accuracy at exactly the chunks that carry no signal of their own — a page of bare reference numbers between two pages full of Dutch addresses. A DocumentContext carries the evidence across chunks so the whole document is scored as one.
import { DocumentContext, redact } from "euredact";
const ctx = new DocumentContext();
let offset = 0;
for (const chunk of chunks) {
const result = redact(chunk, { context: ctx, chunkOffset: offset });
offset += chunk.length;
console.log(result.redactedText);
}
// Every signal the document produced, spans pointing into the full text.
console.log(ctx.evidence);Pass chunkOffset or the evidence spans will all point into the start of the text. Caching is disabled while a context is in use, since the result depends on the document rather than on the chunk alone.
In 0.3.3 evidence became a getter, not a method — it was a method alongside size, a getter, on the same class. Read it as ctx.evidence; ctx.evidence() now throws. The Python SDK keeps ctx.evidence(), following its own idiom.
Reuse a context only across chunks of the same document. A context shared between unrelated documents mixes their countries, which cannot cause a miss — a context influences scoring only, never which spans are found — but can attribute a value to the wrong national scheme.
Example
import { redact } from "euredact";
const result = redact(
"Mijn BSN is 111222333 en IBAN NL91ABNA0417164300.",
{ countries: ["NL"] }
);
console.log(result.redactedText);
// "Mijn BSN is [NATIONAL_ID] en IBAN [BANK_ACCOUNT]."
console.log(result.detections);countries takes an array. Since 0.3.3 a bare string throws a TypeError, on every entry point, in both SDKs. Before that, countries: "NL" was walked character by character into the codes "N" and "L"; neither resolves, so the call declared nothing and every detection came back with outOfScope true while the redacted text still looked correct. A pipeline filtering on that field — which these docs tell you to do — kept none of them. A wrong country code still only warns; a wrong type has no correct reading to fall back on.
redactBatch()
Process multiple texts efficiently. Loads country configs once. Same options as redact(), returns an array of RedactResult.
function redactBatch(
texts: string[],
options?: RedactOptions
): RedactResult[]Example
import { redactBatch } from "euredact";
const results = redactBatch([
"BSN 111222333",
"IBAN DE89370400440532013000",
]);
results.forEach((r) => {
console.log(r.redactedText);
});addCustomPattern()
Register a custom regex pattern at runtime. Detected matches will be labeled with the given entity type.
function addCustomPattern(
entityType: string,
pattern: string
): void| Parameter | Type | Default | Description |
|---|---|---|---|
entityType | string | — | The label to use for matches (e.g. "EMPLOYEE_ID"). |
pattern | string | — | A regex pattern string to match against input text. |
Example
import { addCustomPattern, redact } from "euredact";
addCustomPattern("EMPLOYEE_ID", "EMP-\\d{6}");
const result = redact("Contact EMP-123456 for details");
console.log(result.redactedText);
// "Contact [EMPLOYEE_ID] for details"availableCountries()
Returns an array of supported ISO country codes.
import { availableCountries } from "euredact";
console.log(availableCountries()); // ["AT", "BE", "BG", ...]Secret Detection
euRedact automatically detects secrets and API keys using two strategies: known-prefix patterns for popular services (AWS, GitHub, Stripe, OpenAI, Slack, JWT, SendGrid) and an entropy-based fallback that catches generic secrets near context keywords like api_key, token, and secret. Detected secrets are labeled as [SECRET].
import { redact } from "euredact";
const result = redact(
"My API key is sk-proj-abc123def456ghi789"
);
console.log(result.redactedText);
// "My API key is [SECRET]"Performance
Supported Countries
31 European and EEA countries with country-specific PII patterns.
Entity Types
These are the 27 types this package emits, across all supported countries.
27 is the Rules Engine layer. The forthcoming AI layer adds 13 more — names, addresses, medical information and the other categories that have no fixed format for a pattern to match, plus the GDPR Article 9 special-category types. What euRedact detects lists all 40 with the layer responsible for each, so a count of 40 elsewhere on this site is not a different number from this one.
View source on GitHub
Browse the code, report issues, or contribute.