npm package: @philotheephilix/piifilter
Description: Detect and mask PII in text before sending to LLMs, then reconstruct original values from responses.
Author: philotheephilix
Latest version: 0.1.1

# @philotheephilix/piifilter

Detect and mask PII (Personally Identifiable Information) in text before sending to LLMs, then reconstruct original values from responses. Zero dependencies.

## Installation

```bash
npm install @philotheephilix/piifilter
```

## How It Works

```
User text → filter() → sanitized text + sessionId
                              ↓
                        Send to LLM (placeholders preserved)
                              ↓
LLM response → reconstruct(sessionId, response) → original PII restored
```

Placeholders use the format `[TYPE_N]` — e.g. `[PAN_1]`, `[PHONE_2]`, `[EMAIL_3]`.

## Quick Start

### JavaScript (CommonJS)

```js
const { PIIFilter } = require("@philotheephilix/piifilter");

const pf = new PIIFilter();

const [sanitized, sessionId] = pf.filter(
  "My PAN is ABCDE1234F, email rahul@example.com"
);
console.log(sanitized);
// "My PAN is [PAN_1], email [EMAIL_2]"

// After getting LLM response with placeholders intact:
const restored = pf.reconstruct(sessionId, sanitized);
console.log(restored);
// "My PAN is ABCDE1234F, email rahul@example.com"

pf.clearSession(sessionId);
```

### TypeScript (ESM)

```ts
import { PIIFilter } from "@philotheephilix/piifilter";

const pf = new PIIFilter();

const [sanitized, sessionId] = pf.filter(
  "My PAN is ABCDE1234F, call +919876543210"
);
console.log(sanitized);
// "My PAN is [PAN_1], call [PHONE_2]"

const restored = pf.reconstruct(sessionId, sanitized);
console.log(restored);
// "My PAN is ABCDE1234F, call +919876543210"
```

## Built-in Detectors

| Detector | Label | Formats |
|---|---|---|
| Credit Card | `CREDIT_CARD` | Visa, Mastercard, Amex, Discover (13-19 digits, Luhn validated). Supports spaces and dashes. |
| Phone | `PHONE` | **Indian:** `+91`, `091`, bare 10-digit starting 6-9. **International:** `+CC` followed by 7-14 digits. |
| Aadhaar | `AADHAAR` | 12-digit Indian Aadhaar (first digit 2-9). Supports `2345 6789 0123` and `2345-6789-0123`. |
| PAN Card | `PAN` | Indian PAN format: `ABCDE1234F` (5 letters + 4 digits + 1 letter, uppercase). |
| Driving License | `DL` | Indian DL: state code + RTO + year (19xx/20xx) + serial. e.g. `MH-12-2010-0123456`. |
| Email | `EMAIL` | Standard `user@domain.tld`. Supports `.`, `+`, `%`, `-` in local part. |

## Usage

### Select Specific Detectors

Only enable the detectors you need:

```js
const { PIIFilter } = require("@philotheephilix/piifilter");

const pf = new PIIFilter({ detectors: ["PAN", "EMAIL"] });
console.log(pf.activeDetectors); // ["PAN", "EMAIL"]

const [sanitized] = pf.filter(
  "PAN ABCDE1234F email user@test.com phone 9876543210"
);
console.log(sanitized);
// "PAN [PAN_1] email [EMAIL_2] phone 9876543210"
// Phone is NOT masked because PHONE detector was not enabled
```

Available labels: `CREDIT_CARD`, `PHONE`, `AADHAAR`, `PAN`, `DL`, `EMAIL`.

### Custom Patterns

Add your own regex-based detectors:

```js
const pf = new PIIFilter({ includeAll: false });
pf.addCustomPattern("PASSPORT", "[A-Z]\\d{7}");

const [sanitized] = pf.filter("Passport: A1234567");
console.log(sanitized);
// "Passport: [PASSPORT_1]"
```

With a validator function:

```js
pf.addCustomPattern(
  "PASSPORT",
  "[A-Z]\\d{7}",
  (match) => match.length === 8
);
```

### Custom Detector Class (TypeScript)

```ts
import { PIIFilter, CustomRegexDetector } from "@philotheephilix/piifilter";

const pf = new PIIFilter({ includeAll: false });

const detector = new CustomRegexDetector(
  "SSN",
  /\d{3}-\d{2}-\d{4}/g,
  (match) => !match.startsWith("000")
);
pf.addDetector(detector);

const [sanitized] = pf.filter("SSN: 123-45-6789");
console.log(sanitized);
// "SSN: [SSN_1]"
```

### Append LLM Instruction

Automatically append an instruction telling the LLM to preserve placeholders:

```js
const [sanitized] = pf.filter("My PAN is ABCDE1234F", true);
console.log(sanitized);
// My PAN is [PAN_1]
//
// ---
// IMPORTANT: The text above contains placeholders in the format [TYPE_N]
// (e.g. [PAN_1], [PHONE_2], [EMAIL_3]). These placeholders represent sensitive
// personal information that has been redacted. You MUST preserve every placeholder
// exactly as-is in your response. ...
// ---
```

Or retrieve the instruction text directly:

```js
const { PIIFilter } = require("@philotheephilix/piifilter");
const instruction = PIIFilter.getPromptInstruction();
```

### Session Cleanup

```js
pf.clearSession(sessionId);   // remove one session
pf.clearAllSessions();        // remove all sessions
```

## Full Example: Express + OpenAI

```js
const { PIIFilter } = require("@philotheephilix/piifilter");
const OpenAI = require("openai");
const express = require("express");

const app = express();
app.use(express.json());

const openai = new OpenAI();
const pf = new PIIFilter();

app.post("/chat", async (req, res) => {
  const { message } = req.body;

  // 1. Filter PII before sending to LLM
  const [sanitized, sessionId] = pf.filter(message, true);

  // 2. Send sanitized text to LLM
  const completion = await openai.chat.completions.create({
    model: "gpt-4o-mini",
    messages: [{ role: "user", content: sanitized }],
  });
  const llmResponse = completion.choices[0].message.content;

  // 3. Reconstruct original PII in LLM response
  const restored = pf.reconstruct(sessionId, llmResponse);

  // 4. Cleanup
  pf.clearSession(sessionId);

  res.json({ response: restored });
});

app.listen(3000);
```

## API Reference

### `new PIIFilter(options?)`

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `detectors` | `string[]` | `undefined` | List of detector labels to enable. If omitted, behaviour depends on `includeAll`. |
| `includeAll` | `boolean` | `true` | When `detectors` is omitted, enables all built-in detectors. Set `false` for an empty filter. |

### `pf.filter(text, appendInstruction?): [string, string]`

Returns `[sanitizedText, sessionId]`. Pass `true` for `appendInstruction` to add LLM placeholder-preservation instructions.

### `pf.reconstruct(sessionId, llmResponse): string`

Replaces all `[TYPE_N]` placeholders in `llmResponse` with the original PII values from the session.

### `pf.addCustomPattern(label, pattern, validator?): void`

Add a custom regex detector. `pattern` is a regex string. Optional `validator` function receives the matched text and returns `true` to accept.

### `pf.addDetector(detector): void`

Add a `PIIDetector` instance (e.g. `CustomRegexDetector`).

### `pf.activeDetectors: string[]`

List of active detector labels.

### `pf.clearSession(sessionId): void`

Remove a single session.

### `pf.clearAllSessions(): void`

Remove all sessions.

### `PIIFilter.getPromptInstruction(): string`

Returns the LLM instruction text for placeholder preservation.

## License

MIT