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

@crvouga/mockingbird-service-mailosaur

v1.0.2

Published

Stateful mock of the Mailosaur email/SMS testing API (messages search, get, delete, long-poll) with an HTTP ingest so other mocks can drop mail in (Mockingbird service contract).

Readme

@crvouga/mockingbird-service-mailosaur

Stateful mock of the Mailosaur email/SMS testing API for test suites, plus an HTTP ingest so anything that "sends" mail (the Resend mock's --forward-to-inbox, the Twilio mock, Cognito hooks, a test) drops it into one inbox. The unmodified mailosaur SDK reads it: messages.get returns within ~20 ms of a message arriving instead of long-polling Mailosaur for up to 120 s, and html.codes / text.codes / html.links are parsed the way Mailosaur parses them.

  • Operation coverage: SUPPORT.md
  • The vendor publishes no OpenAPI spec: openapi.yaml is hand-authored from [email protected] (the requests it sends and the fields its models read) and our consumer's client.

Install

npm install -D @crvouga/mockingbird-service-mailosaur

ESM only. Node >= 22 or Bun >= 1.2. No native dependencies. Serve it with npx mockingbird-mailosaur serve, createServer from ./server (Node), or createRuntime with any Fetch server.

Usage

Pointing the mailosaur SDK at it

The SDK only speaks HTTPS (https.request, whatever the base URL's scheme) and drops the base URL's port (it passes only the hostname and path, so it always connects to 443). So a MAILOSAUR_BASE_URL alone (G-M1) only works if the mock listens on 443. The mock's secure port therefore also acts as an HTTP CONNECT proxy that tunnels every target into the mock (never to the network). The SDK honours HTTPS_PROXY, reading it once, when a client is constructed:

npx mockingbird-mailosaur serve --port 8793 --tls-port 8794 --tls-cert-out /tmp/mailosaur-mock.pem
# in the process that constructs the SDK client:
HTTPS_PROXY=http://127.0.0.1:8794 NODE_EXTRA_CA_CERTS=/tmp/mailosaur-mock.pem

With that, new MailosaurClient(apiKey) keeps its default https://mailosaur.com/ and every call lands in the mock. The generated certificate names localhost, 127.0.0.1 and mailosaur.com. Set HTTPS_PROXY only around the SDK construction if the rest of the process must not see it, since other HTTP clients (axios) also read it and would be tunnelled into the mock too. If the mock can bind 443, --tls-port 443 and new MailosaurClient(key, "https://127.0.0.1/") work without the proxy.

import { createServer } from "@crvouga/mockingbird-service-mailosaur/server"
import MailosaurClient from "mailosaur"

const inbox = await createServer({ tls: true })
// …trust inbox.cert (NODE_EXTRA_CA_CERTS, or tls.setDefaultCACertificates in a test)…
process.env.HTTPS_PROXY = inbox.proxyUrl
const mailosaur = new MailosaurClient("any-key")
delete process.env.HTTPS_PROXY

// Something "sends" the signup email:
await fetch(`${inbox.url}/__admin/ingest`, {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({
    to: "[email protected]",
    subject: "Your verification code",
    text: "Your verification code is 604218. ",
  }),
})
const message = await mailosaur.messages.get("abcd1234", { sentTo: "[email protected]" })
message.text?.codes?.[0]?.value // "604218"

The inbox can also be read without the SDK, through the admin routes:

import { createServer } from "@crvouga/mockingbird-service-mailosaur/server"

const inbox = await createServer()
const response = await fetch(`${inbox.url}/__admin/ingest`, {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ to: "[email protected]", text: "Your verification code is 604218. " }),
})
const { id } = (await response.json()) as { id: string }
const { codes } = (await (await fetch(`${inbox.url}/__admin/outbox/${id}/links`)).json()) as { codes: string[] }
// codes[0] === "604218"
await inbox.close()

Routes

| Route | Behaviour | | --- | --- | | POST /api/messages/search?server=&page=&itemsPerPage=&receivedAfter=&dir= | Body {sentTo?, sentFrom?, subject?, body?, match?: "ALL"\|"ANY"}. sentTo / sentFrom match an address exactly (any of to/cc/bcc, case-insensitive); subject / body are case-insensitive contains. {items: [summary]}, newest first (dir=Ascending flips). Answers at once, with x-ms-delay: 20 (the SDK's poll interval while nothing matches), which is how messages.get returns ~20 ms after arrival. | | POST /api/messages/await?server=&receivedAfter=&timeout= | Server-side long-poll: the full message the moment one matches, or 404 {type: "search_timeout"} after timeout ms (default 10000, at most 300000). GET takes the criteria as query parameters. | | GET /api/messages?server= | messages.list: summaries, newest first. | | POST /api/messages?server= | messages.create: stores {to, subject, text?, html?, from?, cc?} in the server. | | DELETE /api/messages?server= | messages.deleteAll: 204. | | GET /api/messages/{id} | messages.getById: the full message (from[], to[], cc[], bcc[], received, subject, html{body, links[{href,text}], codes[{value}], images[]}, text{body, links, codes}, attachments[], metadata, server), or 404. | | DELETE /api/messages/{id} | messages.del: 204, or 404. |

Auth is Authorization: Basic base64(<api key>:) (what the SDK sends); any key works, none is a 401 authentication_error. A 400 names the field the way the SDK's error parser expects ({errors: [{field, detail: [{description}]}]}).

Servers are implicit. A message's server is, in order: the ingest's server, the id in a <server>.mailosaur.net recipient, or * (visible from every server id). receivedAfter keeps messages received at or after the instant (on the mock clock).

Codes and links. codes[] lists every distinct standalone run of 4–8 digits in the readable text (HTML without head, styles, scripts, tags; entities decoded), ignoring digits inside URLs. Our consumer keeps the first 6-digit one. html.links is every <a href> with its text; text.links every URL in the text body.

Admin (beyond the standard contract)

| Route | Effect | | --- | --- | | POST /__admin/ingest | {to, from?, cc?, bcc?, subject?, html?, text?, server?, type?: "Email"\|"SMS", headers?, attachments?: [{filename, content (base64), contentType}]} → 201 with the parsed message. Addresses may be "Name <[email protected]>", bare emails, phone numbers (SMS) or arrays of them. Resend's POST /emails body is accepted as is. Wakes every waiting search at once. | | GET /__admin/outbox?to=&since=&server=&limit= | Every stored message ({id, to, createdAt, server, message}), oldest first. GET /__admin/outbox/:id returns one. | | GET /__admin/outbox/:id/links | {id, links: [href…], codes: [value…]}. | | GET / PUT /__admin/settings | {pollDelaysMs: [20]}: the x-ms-delay sent while a search matches nothing. |

Fault presets (POST /__admin/faults {"preset": "<name>", "count"?: n}; GET /__admin/faults/presets): auth_failed (401, SDK authentication_error), rate_limited (429 on search, SDK api_error), server_error (500), search_never_matches (searches find nothing, so messages.get ends in search_timeout), slow_search (2 s latency).

Namespaces

The SDK cannot add headers, so a namespace can be chosen by API key: PUT /__admin/credentials {"credentials": {"<MAILOSAUR_API_KEY>": "<namespace>"}}. Also x-mockingbird-namespace or a /ns/<name> prefix for raw HTTP callers. Ingest into a namespace with x-mockingbird-namespace (the Resend mock forwards with its own namespace name).

Deliberately not modelled

  • Real delivery: there is no SMTP listener. Mail arrives only through the ingest route, messages.create, or another mock's --forward-to-inbox.
  • Servers, usage, devices (TOTP), previews, spam/deliverability analysis, forward and reply, and file downloads (attachment urls are placeholders; ingest keeps only attachment metadata).
  • Mailosaur's exact code detector is not published. The mock's rule (standalone 4–8 digit runs, not inside URLs) reproduces it for our templates (Cognito's "Your verification code is {####}.").
  • Server ids are not validated against an account; any 8-character id is an (empty) inbox.

API

| Export | Kind | Description | | --- | --- | --- | | MailosaurAPI | class | The in-process mock: fetch(request), reset(), ingest(input), messages(), state. Options: sqlite, now, namespace, settings. | | createRuntime | function | The mock with the full service contract (health, admin, namespaces, credentials, presets, ingest). Options: settings, clock, seed, adminKey, onLog, sqlite. | | MAILOSAUR_PRESETS | object | Every named fault preset. | | MAILOSAUR_NAMESPACE | string | The service name, "mailosaur". | | ANY_SERVER | string | "*": the server of mail ingested without one (visible from every server id). | | DEFAULT_AWAIT_TIMEOUT_MS, MAX_AWAIT_TIMEOUT_MS | numbers | The await long-poll's default and maximum timeout. | | DEFAULT_SETTINGS | object | {pollDelaysMs: [20]}. | | matchesCriteria | function | Whether a message matches {sentTo, sentFrom, subject, body, match}. | | findCodes, htmlContent, textContent, parseAddresses | functions | Mailosaur's parsing: codes, {body, links, codes, images} content, Name <email> / phone addresses. | | document, operationIds, supportedOperationIds | values | The vendored OpenAPI contract and its operation ids. | | createServer, serveTarget, DEFAULT_PORT (./server) | Node | Serve over node:http, plus tls: true for the HTTPS + CONNECT door (tlsUrl, proxyUrl, cert); the serve CLI target (--tls-port, --tls-cert, --tls-key, --tls-cert-out, --poll-delay); port 8793. | | selfSignedCertificate, CERTIFICATE_HOSTS (./server) | Node | Generate the in-memory certificate the door presents, and the hosts it names. |

Part of mockingbird.