npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

puddinhead

v0.1.3

Published

PII detection and anonymization with built-in analyze and sanitize HTTP endpoints.

Downloads

23

Readme

puddinhead

PII detection and anonymization for Node.js apps, HTTP services, and local tooling.

puddinhead ships a stable public API for:

  • analyze(input, options?)
  • anonymize(input, options?)
  • sanitize(input, options?)
  • POST /analyze
  • POST /sanitize
  • puddinhead CLI

Install

npm install puddinhead

Quick Start

CommonJS

const { analyze, anonymize, sanitize } = require("puddinhead");

const input = "Email [email protected] or call +1 (415) 555-2671.";

console.log(analyze(input));
console.log(sanitize(input));
console.log(
  anonymize(input, {
    operators: {
      EMAIL_ADDRESS: { type: "replace", newValue: "[EMAIL]" },
      PHONE_NUMBER: { type: "mask", charsToMask: 8, fromEnd: true }
    }
  })
);

ESM

import { analyze, anonymize, sanitize } from "puddinhead";

const input = "Visit https://example.com from 192.168.0.10";

console.log(analyze(input));
console.log(sanitize(input));
console.log(
  anonymize(input, {
    operators: {
      URL: { type: "replace", newValue: "[LINK]" },
      IP_ADDRESS: "hash"
    }
  })
);

Supported Entities

Built-in recognizers:

  • EMAIL_ADDRESS
  • PHONE_NUMBER
  • CREDIT_CARD
  • IP_ADDRESS (IPv4 and IPv6)
  • URL
  • SSN
  • IBAN
  • DATE_OF_BIRTH
  • PASSPORT_NUMBER
  • DRIVER_LICENSE_NUMBER
  • TAX_ID
  • BANK_ACCOUNT_NUMBER
  • NATIONAL_ID

These built-ins cover many common identifiers, but they are not a claim to detect every possible PII format in every country or document system. For anything domain-specific, add your own patterns or recognizers.

Custom detection is also supported through:

  • patterns: simple string or RegExp inputs
  • recognizers: structured recognizer objects with type, pattern, optional flags, optional score, and optional validate

API Reference

analyze(input, options?)

Detects entities in a string and returns an ordered array of matches:

type AnalysisResult = {
  type: string;
  start: number;
  end: number;
  score: number;
  text: string;
};

Options:

  • entities: limit analysis to a subset of entity types
  • patterns: add backward-compatible literal or RegExp patterns
  • recognizers: add custom recognizer configs

Example:

import { analyze } from "puddinhead";

const entities = analyze("Order ORD-12345 for [email protected]", {
  recognizers: [{ type: "ORDER_ID", pattern: "ORD-[0-9]+", score: 0.91 }]
});

console.log(entities);

anonymize(input, options?)

Runs analysis, applies operators, and returns both transformed text and the entities that were changed:

type AnonymizeResult = {
  text: string;
  entities: Array<
    AnalysisResult & {
      operator: string;
      replacement: string;
    }
  >;
};

Options:

  • All analyze() options
  • operators: per-entity operator config, for example EMAIL_ADDRESS or DEFAULT
  • defaultOperator: fallback operator when an entity-specific operator is not configured
  • replacement: shortcut for the default replace operator value
  • results: reuse a previous analyze() result array

Supported operators:

  • replace
  • redact
  • mask
  • hash
  • keep
  • custom

Example:

const result = anonymize("Reach me at [email protected] or +1 (415) 555-2671.", {
  operators: {
    EMAIL_ADDRESS: { type: "replace", newValue: "[EMAIL]" },
    PHONE_NUMBER: { type: "mask", charsToMask: 8, fromEnd: true }
  }
});

console.log(result.text);
console.log(result.entities);

sanitize(input, options?)

Convenience wrapper around anonymize() that returns only the transformed string.

Example:

sanitize("token abc123", {
  patterns: ["abc123"],
  replacement: "[MASKED]"
});
// "token [MASKED]"

createAnalyzeHandler(options?)

Creates a Node HTTP handler for POST /analyze.

createSanitizeHandler(options?)

Creates a Node HTTP handler for POST /sanitize.

createSanitizeServer(options?)

Creates a Node HTTP server that serves both endpoints. Optional path overrides:

  • analyzePath
  • sanitizePath

HTTP API

POST /analyze

Request body:

{
  "text": "Email [email protected] or call +1 (415) 555-2671",
  "entities": ["EMAIL_ADDRESS", "PHONE_NUMBER"]
}

You may also send:

  • a raw JSON string body
  • an object with input instead of text
  • patterns as an array of string or RegExp
  • recognizers to add custom detectors

Success response:

{
  "entities": [
    {
      "type": "EMAIL_ADDRESS",
      "start": 6,
      "end": 20,
      "score": 0.95,
      "text": "[email protected]"
    },
    {
      "type": "PHONE_NUMBER",
      "start": 29,
      "end": 46,
      "score": 0.78,
      "text": "+1 (415) 555-2671"
    }
  ]
}

Errors:

  • 400 for invalid JSON or missing text/input
  • 405 for non-POST requests
  • 413 when the request body exceeds maxBodySize

POST /sanitize

Request body:

{
  "text": "Email [email protected] or call +1 (415) 555-2671",
  "operators": {
    "EMAIL_ADDRESS": { "type": "replace", "newValue": "[EMAIL]" },
    "PHONE_NUMBER": { "type": "mask", "charsToMask": 8, "fromEnd": true }
  }
}

Success response:

{
  "sanitized": "Email [EMAIL] or call +1 (415) ********",
  "entities": [
    {
      "type": "EMAIL_ADDRESS",
      "start": 6,
      "end": 20,
      "score": 0.95,
      "text": "[email protected]",
      "operator": "replace",
      "replacement": "[EMAIL]"
    },
    {
      "type": "PHONE_NUMBER",
      "start": 29,
      "end": 46,
      "score": 0.78,
      "text": "+1 (415) 555-2671",
      "operator": "mask",
      "replacement": "+1 (415) ********"
    }
  ]
}

Errors:

  • 400 for invalid JSON or missing text/input
  • 405 for non-POST requests
  • 413 when the request body exceeds maxBodySize

fetch Example

const response = await fetch("http://127.0.0.1:3000/sanitize", {
  method: "POST",
  headers: {
    "content-type": "application/json"
  },
  body: JSON.stringify({
    text: "Call me at +1 (415) 555-2671",
    operators: {
      PHONE_NUMBER: { type: "mask", charsToMask: 8, fromEnd: true }
    }
  })
});

const body = await response.json();
console.log(body.sanitized);

CLI

Start the built-in HTTP server:

npx puddinhead

Or set a specific port:

npx puddinhead 4000
PORT=4000 HOST=0.0.0.0 npx puddinhead

The CLI starts a server with:

  • POST /analyze
  • POST /sanitize

Local Server Example

import { createSanitizeServer } from "puddinhead";

const server = createSanitizeServer();

server.listen(3000, "127.0.0.1", () => {
  console.log("puddinhead listening on http://127.0.0.1:3000");
});