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

@devalxui/kova-mail

v1.0.1

Published

The last email toolkit you'll ever need. Send, receive, filter, auto-reply, track opens, schedule, and manage emails across Gmail, Outlook, and any IMAP/SMTP provider.

Readme

kova-mail

The last email toolkit you'll ever need.

Send, receive, filter, auto-reply, track opens, schedule, and manage emails — across Gmail, Outlook, and any IMAP/SMTP provider.

npm version license TypeScript Node.js


Why kova-mail?

Most email libraries do one thing. kova-mail does everything:

  • Send — single, bulk, templated, scheduled, with attachments
  • Receive — read inbox, fetch unread, search across folders
  • Filter — 7 operators, AND/OR logic, chain rules
  • Auto-reply — rule-based automatic responses with cooldowns
  • Track — open tracking via invisible pixel, stats dashboard
  • Schedule — send later with cancel support
  • Templates{{variable}} interpolation engine
  • Multi-provider — Gmail, Outlook, any SMTP/IMAP server

One API. Every email feature you need.


Install

npm install kova-mail

Quick Start

import { KovaMail } from "kova-mail";

const mail = new KovaMail({
  type: "smtp",
  config: {
    host: "smtp.gmail.com",
    port: 465,
    secure: true,
    auth: { user: "[email protected]", pass: "your-app-password" },
  },
});

// Send an email
await mail.send({
  from: "[email protected]",
  to: "[email protected]",
  subject: "Let's talk growth",
  html: "<h1>Hey!</h1><p>Ready to scale?</p>",
});

Providers

Gmail (SMTP)

const mail = new KovaMail({
  type: "smtp",
  config: {
    host: "smtp.gmail.com",
    port: 465,
    secure: true,
    auth: { user: "[email protected]", pass: "your-app-password" },
  },
});

Getting an App Password:

  1. Enable 2-Step Verification at https://myaccount.google.com/security
  2. Go to https://myaccount.google.com/apppasswords
  3. Generate a password for "Mail"

Gmail (OAuth2)

const mail = new KovaMail({
  type: "gmail",
  config: {
    clientId: "your-client-id.apps.googleusercontent.com",
    clientSecret: "your-client-secret",
    refreshToken: "your-refresh-token",
    from: "[email protected]",
  },
});

Outlook / Microsoft 365

const mail = new KovaMail({
  type: "outlook",
  config: {
    clientId: "your-azure-app-id",
    clientSecret: "your-azure-secret",
    tenantId: "your-tenant-id",
    refreshToken: "your-refresh-token",
  },
});

Any SMTP Server

const mail = new KovaMail({
  type: "smtp",
  config: {
    host: "mail.yourdomain.com",
    port: 587,
    secure: false,
    auth: { user: "[email protected]", pass: "password" },
  },
});

Yahoo Mail

const mail = new KovaMail({
  type: "smtp",
  config: {
    host: "smtp.mail.yahoo.com",
    port: 465,
    secure: true,
    auth: { user: "[email protected]", pass: "your-app-password" },
  },
});

Zoho Mail

const mail = new KovaMail({
  type: "smtp",
  config: {
    host: "smtp.zoho.com",
    port: 465,
    secure: true,
    auth: { user: "[email protected]", pass: "password" },
  },
});

SendGrid

const mail = new KovaMail({
  type: "smtp",
  config: {
    host: "smtp.sendgrid.net",
    port: 465,
    secure: true,
    auth: { user: "apikey", pass: "SG.your-sendgrid-api-key" },
  },
});

Amazon SES

const mail = new KovaMail({
  type: "smtp",
  config: {
    host: "email-smtp.us-east-1.amazonaws.com",
    port: 465,
    secure: true,
    auth: { user: "your-ses-smtp-user", pass: "your-ses-smtp-password" },
  },
});

Sending Emails

Basic Send

await mail.send({
  from: "[email protected]",
  to: "[email protected]",
  subject: "Hello",
  html: "<h1>Welcome!</h1>",
  text: "Welcome!", // plain text fallback
});

Multiple Recipients

await mail.send({
  from: "[email protected]",
  to: ["[email protected]", "[email protected]"],
  cc: "[email protected]",
  bcc: ["[email protected]"],
  subject: "Team Update",
  html: "<p>Here's the latest...</p>",
});

With Attachments

import { readFileSync } from "fs";

await mail.send({
  from: "[email protected]",
  to: "[email protected]",
  subject: "Proposal Attached",
  html: "<p>Please find the proposal attached.</p>",
  attachments: [
    {
      filename: "proposal.pdf",
      content: readFileSync("./proposal.pdf"),
      contentType: "application/pdf",
    },
    {
      filename: "logo.png",
      path: "./assets/logo.png", // or use file path
    },
  ],
});

Reply-To

await mail.send({
  from: "[email protected]",
  to: "[email protected]",
  replyTo: "[email protected]",
  subject: "Your Order",
  html: "<p>Thanks for your order!</p>",
});

Priority

await mail.send(
  { from: "[email protected]", to: "[email protected]", subject: "URGENT", html: "<p>Read now!</p>" },
  { priority: "high" }
);

Bulk Send

const results = await mail.sendBulk([
  { email: { from: "[email protected]", to: "[email protected]", subject: "Hey", html: "<p>Hi!</p>" } },
  { email: { from: "[email protected]", to: "[email protected]", subject: "Hey", html: "<p>Hi!</p>" } },
  { email: { from: "[email protected]", to: "[email protected]", subject: "Hey", html: "<p>Hi!</p>" } },
]);

// Check results
results.forEach((r, i) => {
  console.log(`Email ${i + 1}: ${r.success ? "Sent" : "Failed: " + r.error}`);
});

Bulk sends are automatically rate-limited (100ms between emails for SMTP, 200ms for Gmail/Outlook) to avoid provider throttling.


Templates

Register a Template

mail.registerTemplate("welcome", {
  subject: "Welcome to {{company}}, {{name}}!",
  html: `
    <div style="font-family: sans-serif; max-width: 600px;">
      <h1>Hey {{name}}!</h1>
      <p>Welcome to <strong>{{company}}</strong>. We're excited to have you.</p>
      <p>Your account is ready. Here's what's next:</p>
      <ul>
        <li>Book your strategy call</li>
        <li>Share your brand assets</li>
        <li>We'll build your custom plan</li>
      </ul>
      <p>— The {{company}} Team</p>
    </div>
  `,
  text: "Hey {{name}}! Welcome to {{company}}.",
});

Send with Template

await mail.send(
  { from: "[email protected]", to: "[email protected]", subject: "", html: "" },
  {
    template: "welcome",
    templateVars: { name: "Jake", company: "KOVA" },
  }
);

Multiple Templates

// Follow-up template
mail.registerTemplate("follow-up", {
  subject: "Following up, {{name}}",
  html: `<p>Hey {{name}}, just checking in about {{topic}}. Let me know if you have any questions.</p>`,
});

// Proposal template
mail.registerTemplate("proposal", {
  subject: "Your Custom Marketing Proposal — {{company}}",
  html: `
    <h2>{{company}} Marketing Proposal</h2>
    <p>Hey {{name}},</p>
    <p>Based on our call, here's what we recommend:</p>
    <p><strong>Monthly Investment:</strong> {{price}}</p>
    <p><strong>Services:</strong> {{services}}</p>
    <p>Ready to get started? Reply to this email.</p>
  `,
});

// Invoice template
mail.registerTemplate("invoice", {
  subject: "Invoice #{{invoiceNumber}} — {{company}}",
  html: `
    <h2>Invoice #{{invoiceNumber}}</h2>
    <p>Amount Due: <strong>{{amount}}</strong></p>
    <p>Due Date: {{dueDate}}</p>
    <p>Pay here: {{paymentLink}}</p>
  `,
});

// Cold outreach
mail.registerTemplate("cold-outreach", {
  subject: "Quick question about {{company}}",
  html: `
    <p>Hey {{name}},</p>
    <p>I came across {{company}} and noticed you're in the {{industry}} space. We recently helped a similar brand increase their ROAS by 3x in 90 days.</p>
    <p>Would you be open to a quick 15-minute chat this week?</p>
    <p>— {{senderName}}</p>
  `,
});

Template Variables

Variables use {{double_braces}} syntax. If a variable isn't provided, the placeholder stays as-is:

// These vars are available:
{ name: "Jake", company: "Pulse Fitness" }

// Template: "Hey {{name}} from {{company}}, re: {{topic}}"
// Result:   "Hey Jake from Pulse Fitness, re: {{topic}}"
// {{topic}} stays because it wasn't provided

Manage Templates

mail.registerTemplate("test", { subject: "Test", html: "<p>Test</p>" });

// List all
const all = mail.templates; // not exposed yet, use registerTemplate

// Templates are stored in memory — register them on app startup

Receiving Emails

Setup with IMAP

const mail = new KovaMail(
  { type: "smtp", config: { /* sending config */ } },
  {
    type: "imap",
    config: {
      host: "imap.gmail.com",
      port: 993,
      tls: true,
      auth: { user: "[email protected]", pass: "your-app-password" },
    },
  }
);

Setup with Gmail API

const mail = new KovaMail(
  { type: "gmail", config: { /* OAuth config */ } },
  {
    type: "gmail",
    config: {
      clientId: "your-client-id",
      clientSecret: "your-secret",
      refreshToken: "your-token",
    },
  }
);

Read Inbox

// Latest 20 emails
const inbox = await mail.inbox(20);

// With offset (pagination)
const page2 = await mail.inbox(20, 20);

// Unread only
const unread = await mail.unread();

Search

// Search by keyword (checks from, subject, body)
const results = await mail.search("proposal");

// Gmail-specific search queries
const results = await mail.search("from:[email protected] subject:invoice");
const results = await mail.search("has:attachment after:2026/01/01");

Email Object

interface Email {
  id?: string;           // message ID
  from: string;          // sender
  to: string | string[]; // recipients
  cc?: string | string[];
  bcc?: string | string[];
  subject: string;
  text?: string;         // plain text body
  html?: string;         // HTML body
  date?: Date;           // send date
  read?: boolean;        // read status
  starred?: boolean;
  labels?: string[];     // Gmail labels
  attachments?: EmailAttachment[];
}

Filtering

Filter Rules

const emails = await mail.inbox(100);

// Filter by sender
const fromClients = mail.filterEmails(emails, [
  { field: "from", operator: "contains", value: "@client.com" },
]);

// Filter by subject
const proposals = mail.filterEmails(emails, [
  { field: "subject", operator: "contains", value: "proposal" },
]);

// Filter by date
const thisWeek = mail.filterEmails(emails, [
  { field: "date", operator: "after", value: "2026-03-25" },
]);

// Regex filter
const invoices = mail.filterEmails(emails, [
  { field: "subject", operator: "regex", value: "invoice\\s*#?\\d+" },
]);

Available Operators

| Operator | Description | Example | |----------|-------------|---------| | contains | Case-insensitive substring match | "proposal" | | equals | Exact match (case-insensitive) | "[email protected]" | | startsWith | Starts with | "Re:" | | endsWith | Ends with | "@gmail.com" | | regex | Regular expression | "invoice\\s*#\\d+" | | before | Date before | "2026-04-01" | | after | Date after | "2026-03-01" |

Available Fields

| Field | What it matches | |-------|----------------| | from | Sender email | | to | Recipient email(s) | | subject | Email subject line | | body | Email body (text or HTML) | | date | Send date | | labels | Gmail labels |

Combine Rules (AND)

// Must match ALL rules
const results = mail.filterEmails(emails, [
  { field: "from", operator: "contains", value: "client" },
  { field: "subject", operator: "contains", value: "urgent" },
  { field: "date", operator: "after", value: "2026-03-01" },
]);

OR Logic

import { EmailFilter } from "kova-mail";

const filter = new EmailFilter();
const email = emails[0];

// Check if email matches ANY rule
const isImportant = filter.matchesAny(email, [
  { field: "from", operator: "contains", value: "ceo@" },
  { field: "subject", operator: "contains", value: "urgent" },
  { field: "subject", operator: "contains", value: "asap" },
]);

Auto-Reply

Basic Auto-Reply

mail.addAutoReply({
  name: "out-of-office",
  filters: [
    { field: "to", operator: "contains", value: "[email protected]" },
  ],
  reply: {
    subject: "Out of Office",
    html: "<p>Thanks for your email! I'm currently out of office and will respond within 24 hours.</p>",
  },
  enabled: true,
  cooldownMinutes: 60, // don't reply to same sender within 60 min
});

Auto-Reply with Template

mail.registerTemplate("auto-response", {
  subject: "Re: {{originalSubject}}",
  html: `
    <p>Hey! Thanks for reaching out.</p>
    <p>We've received your message and our team will get back to you within 2 hours during business hours (Mon-Fri 9am-6pm EST).</p>
    <p>— KOVA Team</p>
  `,
});

mail.addAutoReply({
  name: "business-hours",
  filters: [
    { field: "from", operator: "regex", value: ".*" }, // match all
  ],
  reply: {
    template: "auto-response",
  },
  enabled: true,
  cooldownMinutes: 120,
  maxRepliesPerSender: 3, // max 3 auto-replies per sender
});

Process Auto-Replies

// Call this on a schedule (e.g., every 5 minutes)
const results = await mail.processAutoReplies();
console.log(`Sent ${results.length} auto-replies`);

Manage Rules

mail.addAutoReply({ name: "rule1", /* ... */ });
mail.removeAutoReply("rule1");

Open Tracking

Track Email Opens

const result = await mail.send(
  {
    from: "[email protected]",
    to: "[email protected]",
    subject: "Your Proposal",
    html: "<h2>Here's your proposal</h2><p>See attached.</p>",
  },
  { trackOpens: true }
);

console.log(result.trackingId); // "1711234567890-abc123"

Handle Tracking Pixel

Set up an endpoint in your app to serve the tracking pixel:

// Next.js API route example
export async function GET(req: Request) {
  const url = new URL(req.url);
  const id = url.searchParams.get("id");
  if (id) mail.handleTrackingPixel(id);

  // Return 1x1 transparent PNG
  return new Response(Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==", "base64"), {
    headers: { "Content-Type": "image/png", "Cache-Control": "no-store" },
  });
}

Check Stats

const stats = mail.getOpenStats();
stats.forEach((data, trackingId) => {
  console.log(`${trackingId}: opened ${data.count} times, first at ${data.openedAt}`);
});

Scheduling

Send Later

// Send in 1 hour
await mail.send(
  { from: "[email protected]", to: "[email protected]", subject: "Follow up", html: "<p>Hey!</p>" },
  { scheduledAt: new Date(Date.now() + 60 * 60 * 1000) }
);

// Send tomorrow at 9am
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(9, 0, 0, 0);

await mail.send(
  { from: "[email protected]", to: "[email protected]", subject: "Good morning", html: "<p>Ready to chat?</p>" },
  { scheduledAt: tomorrow }
);

View & Cancel Scheduled

// View all scheduled
const scheduled = mail.getScheduled();
scheduled.forEach((email, id) => {
  console.log(`${id}: "${email.subject}" → ${email.scheduledAt}`);
});

// Cancel a scheduled email
mail.cancelScheduled("sched-1711234567890-abc123");

Advanced Usage

Error Handling

const result = await mail.send({ /* ... */ });

if (!result.success) {
  console.error("Failed:", result.error);
  // Common errors:
  // - "Invalid login" → wrong credentials
  // - "Connection timeout" → server unreachable
  // - "Rate limit exceeded" → slow down
}

Cold Outreach Campaign

mail.registerTemplate("cold-v1", {
  subject: "Quick question about {{company}}",
  html: `<p>Hey {{name}},</p><p>I noticed {{company}} could benefit from better ad performance. We helped a similar brand 3x their ROAS in 90 days.</p><p>Worth a quick chat?</p>`,
});

const leads = [
  { name: "Jake", company: "Pulse Fitness", email: "[email protected]" },
  { name: "Mia", company: "Solara Skin", email: "[email protected]" },
  { name: "Tom", company: "GreenLeaf", email: "[email protected]" },
];

const results = await mail.sendBulk(
  leads.map((lead) => ({
    email: { from: "[email protected]", to: lead.email, subject: "", html: "" },
    options: {
      template: "cold-v1",
      templateVars: { name: lead.name, company: lead.company },
      trackOpens: true,
    },
  }))
);

console.log(`Sent: ${results.filter((r) => r.success).length}/${results.length}`);

Inbox Monitoring Loop

// Check for new emails every 5 minutes
setInterval(async () => {
  const unread = await mail.unread();

  // Auto-categorize
  const urgent = mail.filterEmails(unread, [
    { field: "subject", operator: "regex", value: "(urgent|asap|emergency)" },
  ]);

  const invoices = mail.filterEmails(unread, [
    { field: "subject", operator: "contains", value: "invoice" },
  ]);

  console.log(`${unread.length} unread, ${urgent.length} urgent, ${invoices.length} invoices`);

  // Process auto-replies
  await mail.processAutoReplies();
}, 5 * 60 * 1000);

API Reference

KovaMail

| Method | Description | |--------|-------------| | send(email, options?) | Send a single email | | sendBulk(emails) | Send multiple emails with rate limiting | | inbox(limit?, offset?) | Fetch inbox emails | | unread() | Fetch unread emails | | search(query) | Search emails | | filterEmails(emails, rules) | Filter email array by rules | | registerTemplate(name, template) | Register a reusable template | | addAutoReply(rule) | Add an auto-reply rule | | removeAutoReply(name) | Remove an auto-reply rule | | processAutoReplies() | Process and send auto-replies | | handleTrackingPixel(id) | Record an email open | | getOpenStats() | Get open tracking statistics | | getScheduled() | Get all scheduled emails | | cancelScheduled(id) | Cancel a scheduled email |

EmailFilter

| Method | Description | |--------|-------------| | apply(emails, rules) | Filter emails matching ALL rules | | matchesAll(email, rules) | Check if email matches all rules | | matchesAny(email, rules) | Check if email matches any rule |

TemplateEngine

| Method | Description | |--------|-------------| | register(name, template) | Register a template | | apply(email, name, vars) | Apply template to email | | list() | List all registered templates | | get(name) | Get a template by name | | remove(name) | Remove a template |

OpenTracker

| Method | Description | |--------|-------------| | injectPixel(html, id) | Inject tracking pixel into HTML | | recordOpen(id) | Record an open event | | isOpened(id) | Check if email was opened | | getOpenCount(id) | Get open count for tracking ID | | getStats() | Get all tracking statistics |


TypeScript

kova-mail is written in TypeScript and ships with full type definitions:

import type {
  Email,
  EmailOptions,
  EmailResult,
  EmailAttachment,
  SmtpConfig,
  GmailConfig,
  OutlookConfig,
  ImapConfig,
  FilterRule,
  AutoReplyRule,
  Template,
  TemplateVars,
  ScheduledEmail,
} from "kova-mail";

Contributing

git clone https://github.com/AlexanderGese/kova-mail.git
cd kova-mail
npm install
npm run build
npm test

License

MIT — use it however you want.


Built by KOVA