Skip to content

feat(providers): add Mailinator email provider as alternative to emailsink #58

Description

@Satyam0000000

This proposes adding a Mailinator provider to src/providers/mailinator.ts
as a second built-in email provider alongside the existing emailsink provider.

Background

The EmailProvider interface in config.ts is already designed to support
multiple implementations, but currently only one provider ships with Passmark
(emailsink).

This PR exercises that abstraction with a second implementation and helps
verify that the provider interface is sufficiently generic to support different
email retrieval strategies.

Why Mailinator specifically

Considerations

Mailinator public inboxes are best suited for experimentation and lightweight
testing. Teams requiring isolated inboxes, higher reliability, or private
domains can use Mailinator's API-key-backed offering.

The provider supports both workflows through the same API.

1. Different extraction approach

emailsink sends your email + prompt to Bug0's own server which does
AI extraction externally.
Mailinator returns the raw email body and lets Passmark's own AI
handle extraction.

2. Zero friction for new users

Public Mailinator inboxes allow users to quickly experiment with email-driven
workflows without creating an account or configuring credentials.

For more isolated and reliable testing, users can optionally provide an API key
and use Mailinator's private inbox capabilities.

// works instantly, no credentials needed
configure({
  email: mailinatorProvider()
});

Compare this to emailsink's free tier which, while also keyless,
routes through Bug0 infrastructure. Having an alternative provider gives users a choice of email retrieval
backends and reduces reliance on a single service.

3. Optional upgrade path

Users who need private inboxes can provide an API key for
Mailinator's paid tier — the same optional pattern emailsink uses:

configure({
  email: mailinatorProvider({ apiKey: process.env.MAILINATOR_API_KEY })
});

What changes

This implementation introduces no breaking changes and does not modify any
existing provider behavior.

This implementation introduces no breaking changes and does not modify any
existing provider behavior.

src/providers/

emailsink.ts ← unchanged
mailinator.ts ← new file

tests/providers/

mailinator.test.ts ← new test file

Usage after this change

import { configure } from "passmark";
import { mailinatorProvider } from "passmark/providers/mailinator";

// zero config — works immediately
configure({
  email: mailinatorProvider()
});

// with API key for private domains
configure({
  email: mailinatorProvider({ 
    apiKey: process.env.MAILINATOR_API_KEY 
  })
});

Works with all existing placeholder patterns:

steps: [
  { 
    description: "Enter email", 
    data: { value: "{{run.dynamicEmail}}" } 
  },
  { description: "Click send verification code" },
  { 
    description: "Enter OTP",
    data: { 
      value: "{{email.otp:get the 6 digit code:{{run.dynamicEmail}}}}" 
    }
  },
]

Tests

Added 7 unit tests covering:

  • inbox retrieval
  • empty inbox handling
  • message retrieval
  • empty body handling
  • API key authentication
  • error responses
  • successful extraction

Full test suite remains green:

  • 13 test files passed
  • 162 tests passed

If this approach aligns with the project's goals, I'm happy to open a PR with
the implementation and tests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions