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

pm-email-validator

v1.0.1

Published

The explainable email intelligence library. Not just valid/invalid - understand WHY with provider detection, canonical normalization, risk signals, and confidence scoring.

Readme

pm-email-validator

npm version Node >=18 License: MIT

Deliverability-first email validation for Node.js with explainable results.

pm-email-validator is built for real product flows where syntax-only checks are not enough. It combines syntax validation, typo detection, disposable-domain checks, DNS signals, optional SMTP probing, and a transparent scoring model.

Why This Package

Most validators answer only one question: "Does this look like an email?"

This package answers the questions teams actually care about:

  • Is this likely deliverable?
  • Is this disposable/temp mail?
  • Is this risky for B2B outreach or user quality?
  • Why did we classify it this way?

Documentation

  • Quick usage and API: this README
  • Full code-flow guide: docs/ARCHITECTURE.md

Features

  • Lean default response for fast production usage
  • Optional full detail mode for intelligence/risk analysis
  • Deterministic scoring with configurable weights and thresholds
  • DNS checks: MX, SPF, DMARC, A/AAAA existence
  • Optional SMTP probing (rate-limited, best-effort)
  • Catch-all detection (optional)
  • Role-email and suspicious pattern detection
  • Batch validation helpers and stream API
  • CLI for one-off and file-based validation
  • Debug API with step-by-step trace

Install

npm install pm-email-validator

Node Version

  • Node.js >= 18

Quick Start

import { verifyEmail } from "pm-email-validator";

const result = await verifyEmail("[email protected]");
console.log(result.verdict);
console.log(result.reasons);

Output Modes

Default: output: "basic"

verifyEmail() returns a lean payload by default.

import { verifyEmail } from "pm-email-validator";

const result = await verifyEmail("[email protected]");
// lean response

Detailed: output: "full"

Use this when you need intelligence and score internals.

const detailed = await verifyEmail("[email protected]", { output: "full" });
// includes intelligence, risks, scoreBreakdown

Trace: debugVerifyEmail()

Use this for debugging and education.

import { debugVerifyEmail } from "pm-email-validator";

const { result, trace } = await debugVerifyEmail("[email protected]");
console.log(result.verdict);
console.log(trace);

Result Types

PMEmailResult (lean)

export interface PMEmailResult {
  email: string;
  normalizedEmail: string;
  verdict: "valid" | "risky" | "unknown" | "invalid";
  confidence: number;
  reasons: string[];
  checks: {
    syntax: { ok: boolean; mode: "basic" | "rfc"; reason?: string };
    typo: { ok: boolean; suggestedEmail?: string };
    disposable: { ok: boolean; source?: string };
    dns: {
      domainExists?: boolean;
      mx?: boolean;
      spf?: boolean;
      dmarc?: boolean;
      mxHosts?: string[];
    };
    smtp: { enabled: boolean; status: "valid" | "invalid" | "unknown"; code?: string };
    catchAll?: { checked: boolean; isCatchAll: boolean; confidence: string };
    roleEmail: { isRole: boolean; prefix?: string; category?: string };
    patterns: { detected: boolean; signals: string[] };
  };
}

PMEmailDetailedResult (full)

export interface PMEmailDetailedResult extends PMEmailResult {
  intelligence: Intelligence;
  risks: RiskItem[];
  scoreBreakdown: ScoreBreakdownItem[];
}

Validation Flow

The internal pipeline is deterministic and ordered:

  1. Normalize input
  2. Syntax validation (basic or rfc)
  3. DNS checks
  4. Provider detection
  5. Canonicalization
  6. Role-email detection
  7. Pattern detection
  8. Typo detection
  9. Disposable-domain detection
  10. DNS score impacts
  11. Optional SMTP probe
  12. Optional catch-all check
  13. Final score and verdict

Configuration

import { verifyEmail } from "pm-email-validator";

const result = await verifyEmail("[email protected]", {
  output: "basic", // "basic" (default) | "full"
  mode: "basic", // "basic" | "rfc"
  typoSuggestions: true,
  disposable: { enabled: true },
  dns: { mx: true, spf: true, dmarc: true, timeoutMs: 4000 },
  smtp: { enabled: false, timeoutMs: 4000, maxConnectionsPerDomainPerMinute: 10 },
  catchAll: { enabled: false, timeoutMs: 5000 },
  patterns: { enabled: true },
  roleEmail: { enabled: true },
  scoring: {
    weights: {
      typo: 25,
      disposable: 60,
      noMx: 50,
      noSpf: 10,
      noDmarc: 10,
      smtpInvalid: 80,
    },
    thresholds: { valid: 85, risky: 60, unknown: 30 },
  },
  cache: { dnsTtlMs: 10 * 60 * 1000, maxEntries: 2000 },
});

Disposable / Temp-Mail Handling

This package treats temp mail as disposable mail.

Built-in list

By default, disposable checks use the bundled list.

Custom updatable list from file

Use a file-based provider for easy maintenance:

import { verifyEmail, loadDisposableProviderFromFile } from "pm-email-validator";

const provider = loadDisposableProviderFromFile("data/disposable_domains.txt");

const result = await verifyEmail("[email protected]", {
  disposable: { enabled: true, provider },
});
  • File format: one domain per line
  • Comment lines start with #
  • Recommended source file in this repo: data/disposable_domains.txt

Presets

Available presets:

  • quick
  • standard
  • thorough
  • b2b
  • strict
import { getPresetOptions, verifyEmail } from "pm-email-validator";

const options = getPresetOptions("standard", {
  output: "basic",
});

const result = await verifyEmail("[email protected]", options);

API Reference (Core)

  • verifyEmail(email, options?) -> Promise<PMEmailResult | PMEmailDetailedResult>
  • debugVerifyEmail(email, options?) -> Promise<DebugResult>
  • verifyEmailBatch(emails, options?)
  • verifyEmailStream(emails, options?)
  • validateSyntax(email, mode?)
  • suggestTypo(email)
  • checkDisposable(domain, provider?)
  • checkDNS(domain, opts, cache?)

See src/index.ts for full export surface.

CLI Usage

Single email:

pm-email-validator "[email protected]"

File mode (first CSV column is email):

pm-email-validator --file emails.csv --out report.json --concurrency 20

Local HTTP API (Development)

Start API:

npm run dev:api

Health check:

GET http://localhost:3000/health

Validate:

POST http://localhost:3000/verify
Content-Type: application/json

{
  "email": "[email protected]",
  "options": {
    "output": "basic",
    "mode": "basic"
  }
}

Debug trace:

POST http://localhost:3000/verify/debug
Content-Type: application/json

{
  "email": "[email protected]",
  "options": {
    "output": "full",
    "mode": "rfc"
  }
}

Benchmark

Run local throughput benchmark:

npm run bench

With flags:

npm run bench -- --iterations=5000 --concurrency=50 --output=basic
npm run bench -- --iterations=2000 --concurrency=20 --output=full

Performance Notes

For production throughput:

  • Use output: "basic" unless you need full intelligence payload
  • Keep SMTP disabled unless your use case requires mailbox probing
  • Keep DNS cache enabled (default)
  • Use batch APIs for large imports

Comparison

| Approach | Fast | Disposable Detection | DNS Signals | SMTP Option | Explainable Scoring | |---|---:|---:|---:|---:|---:| | Regex-only validator | Yes | No | No | No | No | | Syntax + MX checker | Yes | No | Partial | No | Limited | | External black-box API | Varies | Varies | Varies | Varies | Usually limited | | pm-email-validator | Yes (basic mode) | Yes | Yes | Yes (opt-in) | Yes |

Security Notes

SMTP probing is opt-in and can be considered intrusive by some providers.

  • Respect provider policies and legal requirements
  • Expect ambiguous unknown outcomes (greylisting, catch-all, policy blocks)

Roadmap

  • Improve typo intelligence with curated typo-to-canonical mapping
  • Add optional provider metadata packs (industry/domain enrichment)
  • Add benchmark profiles for network-heavy scenarios
  • Expand test matrix with real-world anonymized email fixtures
  • Add optional plugin hooks for custom risk signals

Development

Install:

npm install

Build:

npm run build

Test:

npm test

Typecheck:

npm run typecheck

Lint:

npm run lint

Contributing

Contributions are welcome.

  1. Fork the repository
  2. Create a feature branch
  3. Add or update tests for your change
  4. Run npm test and npm run typecheck
  5. Open a PR with:
    • what changed
    • why it changed
    • any compatibility notes

Contribution Guidelines

  • Keep public API changes explicit in PR description
  • Prefer deterministic behavior over opaque heuristics
  • Keep default path fast (output: "basic")
  • Add tests for new scoring or classification logic

License

MIT