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

@appkit/apple-mail

v0.2.0

Published

TypeScript library for reading Apple Mail via SQLite

Readme

@appkit/apple-mail

TypeScript library for reading Apple Mail via SQLite database access.

Features

  • 📖 Read-only access - Safe, no risk of corrupting your Mail database
  • 🔍 Powerful queries - Search, filter, and query messages with flexible options
  • 📦 Zero dependencies - Just better-sqlite3, zod, and debug
  • 🎯 Type-safe - Complete TypeScript types for all entities
  • 🚀 Fast - Synchronous SQLite access with better-sqlite3
  • 🔌 Pluggable - Works in any Node.js service or script
  • 🛡️ Multi-version support - Automatically detects Mail.app V7-V10

Installation

npm install @appkit/apple-mail

Requirements

  • Node.js >= 20.0.0
  • macOS with Mail.app configured
  • Full Disk Access permission (see setup below)

Setting up Full Disk Access

On macOS Catalina and later, you need to grant Full Disk Access:

  1. Open System Settings > Privacy & Security
  2. Scroll to Full Disk Access
  3. Click the + button
  4. Add your terminal app (Terminal.app, iTerm, etc.) or IDE (VS Code, etc.)
  5. Restart your terminal/IDE

Quick Start

import { AmailClient } from '@appkit/apple-mail';

const client = new AmailClient();
await client.connect();

// Get recent messages
const messages = await client.getMessages({ limit: 10 });
console.log(messages);

// Search messages
const results = await client.searchMessages('project update');

// Get unread count
const unread = await client.getUnreadCount();

client.disconnect();

CLI Usage

The package includes a powerful CLI for managing Apple Mail from the command line.

Installation

Install globally for command-line access:

npm install -g @appkit/apple-mail

Or use locally with npx:

npx @appkit/apple-mail check

Quick Start

Check setup and permissions:

amail check

List recent messages:

amail messages list --limit 10

Search messages:

amail messages search "project update"

Show message details:

amail messages show 12345

List mailboxes:

amail mailboxes list

CLI Commands

Global Options:

  • --json - Output as JSON (machine-readable)
  • --limit <n> - Limit results (default: 50)
  • --offset <n> - Skip N results
  • --verbose - Show library debug logs
  • --debug - Enable CLI debug logging
  • --no-color - Disable colors
  • --db-path <path> - Override database path

Commands:

  • amail check - Verify permissions and setup
  • amail messages list [options] - List messages with filtering
  • amail messages search <query> [options] - Search messages
  • amail messages show <id> - Show message details
  • amail mailboxes list - List mailboxes

CLI Examples

Daily Email Management:

# Check unread messages from today
amail messages list --unread --since today

# Check all unread messages
amail messages list --unread

# View flagged/important messages
amail messages list --flagged

# Morning email check
amail messages list --unread --since yesterday --limit 20

Search and Filter:

# Search messages from last week
amail messages search "invoice" --since 7d

# Find messages from specific sender
amail messages list --from [email protected] --since 30d

# Search in message body
amail messages search "meeting notes" --fields subject,body

# Find messages with specific keywords
amail messages search "urgent" --since 3d

Advanced Usage:

# Get JSON output for scripting
amail messages list --limit 5 --json | jq '.data[].subject'

# Export messages to file
amail messages list --since 30d --json > messages.json

# Count unread messages
amail messages list --unread --json | jq '.count'

# View specific message details
amail messages show 12345

# List all mailboxes
amail mailboxes list

Date Filtering:

# Relative dates
amail messages list --since 1d   # Last 24 hours
amail messages list --since 7d   # Last week
amail messages list --since 1m   # Last month

# Natural language
amail messages list --since today
amail messages list --since yesterday

# Specific dates
amail messages list --since 2026-02-01 --before 2026-02-11

Combining Filters:

# Unread messages from specific sender this week
amail messages list --unread --from [email protected] --since 7d

# Flagged messages from last month
amail messages list --flagged --since 1m --limit 50

# Search unread messages
amail messages search "project" --since 7d | grep -i unread

For complete CLI documentation, see docs/CLI.md.

API Overview

High-Level Convenience Methods

// Get messages with filtering
await client.getMessages({ limit: 10, isRead: false });

// Search messages
await client.searchMessages('meeting', { limit: 20 });

// Get specific message
await client.getMessageById(12345);

// Get mailboxes
await client.getMailboxes();

// Get unread count
await client.getUnreadCount();

Service-Level Advanced Access

// Message operations
await client.messages.query({ sender: '[email protected]' });
await client.messages.getUnread({ limit: 50 });
await client.messages.getFlagged();
await client.messages.getByDateRange(startDate, endDate);

// Mailbox operations
await client.mailboxes.getByName('INBOX');
await client.mailboxes.getUnreadCount(mailboxId);

// Address operations
await client.addresses.getFrequentContacts(20);
await client.addresses.searchByEmail('john');

// Attachment operations
await client.attachments.getByMessageId(messageId);
await client.attachments.search('.pdf');

Raw SQL Queries

const results = await client.executeRawQuery<{ count: number }>(
  'SELECT COUNT(*) as count FROM messages WHERE read = ?',
  [0]
);

Usage Examples

Filter by Date Range

const startDate = new Date('2024-01-01');
const endDate = new Date('2024-01-31');

const messages = await client.messages.getByDateRange(startDate, endDate, {
  limit: 100
});

Get Messages with Attachments

const withAttachments = await client.getMessages({
  hasAttachments: true,
  limit: 20
});

// Get attachment details
for (const message of withAttachments) {
  const attachments = await client.attachments.getByMessageId(message.id);
  console.log(`${message.subject} has ${attachments.length} attachments`);
}

Complex Query

const messages = await client.messages.query({
  sender: '[email protected]',
  dateRange: {
    start: new Date('2024-11-01'),
    end: new Date('2024-11-30')
  },
  isRead: false,
  hasAttachments: true,
  orderBy: 'date',
  orderDirection: 'desc',
  limit: 50
});

Get Frequent Contacts

const contacts = await client.addresses.getFrequentContacts(20);

contacts.forEach(contact => {
  console.log(`${contact.email} - ${contact.messageCount} messages`);
});

Configuration Options

const client = new AmailClient({
  databasePath: '/custom/path/to/Envelope Index',  // Optional: override default
  readOnly: true,                                  // Always true (safety)
  timeout: 5000,                                   // Query timeout in ms
  logLevel: 'info'                                 // debug, info, warn, error
});

Debugging

Enable debug logging:

DEBUG=amail:* npm run inspect

Use the included debug scripts:

# Inspect database and show statistics
npm run inspect

# Test various queries
npm run query

Type Definitions

Message

interface Message {
  id: number;
  subject: string;
  sender: Address;
  recipients: Address[];
  dateSent: Date;
  dateReceived: Date;
  mailboxId: number;
  mailboxName: string;
  isRead: boolean;
  isFlagged: boolean;
  hasAttachments: boolean;
  attachmentCount: number;
  metadata: {
    messageId: string;
    inReplyTo?: string;
    references?: string[];
  };
}

Mailbox

interface Mailbox {
  id: number;
  name: string;
  accountId: number;
  accountName: string;
  type: MailboxType;  // inbox, sent, drafts, trash, etc.
  unreadCount?: number;
  totalCount?: number;
}

Development

# Install dependencies
npm install

# Build
npm run build

# Run tests
npm test

# Watch tests
npm run test:watch

# Type check
npm run typecheck

# Inspect database
npm run inspect

# Test queries
npm run query

Architecture

src/
├── core/           # Database connection and query builder
├── services/       # Message, Mailbox, Address, Attachment services
├── types/          # TypeScript type definitions
├── utils/          # Utilities (logging, permissions, validators)
└── index.ts        # Main AmailClient class

Limitations

  • Read-only - Cannot modify, delete, or send messages
  • Metadata only - Cannot read message body content (requires parsing .emlx files)
  • No file access - Attachment metadata only (no file content access)
  • macOS only - Requires Mail.app and SQLite database

Security & Privacy

  • ✅ Read-only access (no write operations)
  • ✅ Local processing only (no external communication)
  • ✅ No telemetry or tracking
  • ✅ Your email data never leaves your machine

Troubleshooting

Database locked error

If you get SQLITE_BUSY errors:

  • Mail.app may be performing indexing
  • Try quitting Mail.app temporarily
  • Increase timeout in options

Permission denied

  • Ensure Full Disk Access is granted
  • Restart terminal/IDE after granting permission
  • Run npm run inspect to verify permissions

Database not found

  • Ensure Mail.app has been run at least once
  • Check that Mail.app is configured with at least one account

License

MIT

Author

Dave Weaver [email protected]