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-resend

v1.0.2

Published

Stateful mock of the Resend email API (send with idempotency, received emails, attachments) with an outbox and Svix-signed inbound webhooks (Mockingbird service contract).

Readme

@crvouga/mockingbird-service-resend

Stateful mock of the Resend email API for test suites. Every send lands in an outbox that tests read (GET /__admin/outbox, and the links in each email). Idempotency-Key replays return the first send's id. Inbound emails are stored and announced with a Svix-signed email.received webhook, and the received-email endpoints serve their content and attachments. With --forward-to-inbox, every sent email is also copied into the Mailosaur mock, so one inbox holds every code and link.

  • Operation coverage: SUPPORT.md
  • openapi.yaml is trimmed from Resend's published API reference to what [email protected] and our backend call.

Install

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

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

Usage

[email protected] reads RESEND_BASE_URL once, when the module is first imported, so set it in the app's environment (or before the first import("resend") in a test). Set the app's RESEND_INBOUND_WEBHOOK_SECRET and pass the same whsec_… value as --webhook-secret.

npx mockingbird-resend serve --port 8794 \
  --webhook-url http://127.0.0.1:3000/messaging/inbound/email \
  --webhook-secret "$RESEND_INBOUND_WEBHOOK_SECRET" \
  --forward-to-inbox http://127.0.0.1:8793
RESEND_BASE_URL=http://127.0.0.1:8794 node app.js
import { createServer } from "@crvouga/mockingbird-service-resend/server"

const mock = await createServer()
process.env.RESEND_BASE_URL = mock.url
const { Resend } = await import("resend")

await new Resend("re_test").emails.send({
  from: "Acme <[email protected]>",
  to: ["[email protected]"],
  subject: "You're invited",
  html: '<a href="https://app.test/family/invitations/claim?token=abc">Join</a>',
})

const { messages } = await (await fetch(`${mock.url}/__admin/[email protected]`)).json()
const { links } = await (await fetch(`${mock.url}/__admin/outbox/${messages[0].id}/links`)).json()
// links[0] === "https://app.test/family/invitations/claim?token=abc"

Whatever sends the mail (the SDK above, or raw HTTP), the outbox is the assertion surface:

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

const mock = await createServer()
await fetch(`${mock.url}/emails`, {
  method: "POST",
  headers: { "content-type": "application/json", authorization: "Bearer re_test" },
  body: JSON.stringify({ from: "[email protected]", to: ["[email protected]"], subject: "Hi", text: "Hello" }),
})
const sent = await (await fetch(`${mock.url}/__admin/[email protected]`)).json()
await mock.close()

Routes

| Route | Behaviour | | --- | --- | | POST /emails | {from, to, subject, html?, text?, cc?, bcc?, reply_to?, headers?, tags?, attachments?, scheduled_at?} → {id} (a UUID). The SDK renders react to HTML before sending, so the mock sees HTML. Violations answer Resend's body {statusCode, name, message}: 422 missing_required_field (Missing \to` field., or no html/text), 422 validation_error for a malformed address (Invalid `from` field. The email address needs to follow …), a tag outside [A-Za-z0-9_-], or any other contract violation. Idempotency-Key: the same key and payload replay the first 200 byte for byte (idempotent-replayed: true); another payload is 409 invalid_idempotent_request; a key still in flight is 409 concurrent_idempotent_requests; a key outside 1–256 characters is 400 invalid_idempotency_key. Only 200s are remembered. | | GET /emails/{id}|emails.get: {object: "email", id, to, from, created_at, subject, html, text, cc, bcc, reply_to, last_event: "delivered" | "scheduled", scheduled_at, tags}. | | GET /emails/receiving/{id}| A received email:{object: "email", id, to, from, cc, bcc, reply_to, created_at, subject, html, text, headers, message_id, attachments[{id, filename, content_type, content_disposition, content_id}]}. | | GET /emails/receiving/{id}/attachments|{object: "list", has_more: false, data: [{id, filename, content_type, content_disposition, content_id, size, download_url, expires_at}]}. | | GET /downloads/inbound/{attachment_id}| Thedownload_url: unauthenticated, the bytes with content-typeandcontent-length. In a non-default namespace the URL carries /ns/`. |

Auth is Authorization: Bearer <key>; any key works; none is 401 missing_api_key.

SDK error mapping ([email protected] never throws): a non-2xx JSON body comes back verbatim as {data: null, error: {statusCode, name, message}}; a non-JSON body becomes {name: "application_error", message: "Internal server error. …"}; a dropped connection becomes {name: "application_error", message: "Unable to fetch data. The request could not be resolved."}.

Webhooks

POST /__admin/inbound stores the email, then posts to every endpoint:

{"type": "email.received", "created_at": "…",
 "data": {"email_id": "…", "created_at": "…", "from": "…", "to": ["[email protected]"], "cc": [], "bcc": [],
          "subject": "…", "message_id": "<…>", "attachments": [{"id", "filename", "content_type", "content_disposition", "content_id"}]}}

Like Resend's, the body carries no text, html, headers or download URLs, so a receiver hydrates them through the received-email routes. Pass "inline": true to include them. Each delivery is signed the Svix way: svix-id, svix-timestamp (wall clock, even when the mock clock moves), svix-signature: v1,<base64 HMAC-SHA256(secret bytes, "id.ts.body")>. The official svix Webhook.verify accepts them. Non-2xx answers are retried (immediately, 5 s, 5 min, 30 min, 2 h). GET /__admin/webhooks, …/events, …/flush, …/:id/replay and PUT /__admin/webhook-endpoints (per-namespace receivers) come with the contract.

Admin (beyond the standard contract)

| Route | Effect | | --- | --- | | GET /__admin/outbox?to=&tag=<name>:<value>&since=&limit= | Sent emails, oldest first: {id, from, to (bare, lower-cased), toHeader, cc, bcc, replyTo, subject, html, text, tags, headers, attachments (filename, contentType, size), idempotencyKey, scheduledAt, createdAt}. tag=category matches any value. GET /__admin/outbox/:id returns one. | | GET /__admin/outbox/:id/links | {id, links}: every href in the HTML, entity-decoded (every URL in the text when there is no HTML). | | POST /__admin/inbound | {from, to, cc?, bcc?, replyTo?, subject?, text?, html?, headers?, messageId?, attachments?: [{filename, content (base64), contentType?, contentId?, contentDisposition?}], inline?} → 201 {id, webhook, event}. | | GET /__admin/inbound | Received emails, oldest first. | | GET /__admin/forwarding | {target, forwarded, failed, lastError} for --forward-to-inbox. |

--forward-to-inbox <url> copies every accepted send (not replays) to the Mailosaur mock's POST /__admin/ingest, under the same namespace name, and waits up to 2 s for it. A failing inbox never fails the send; it is counted in /__admin/forwarding. Map the same key to the same namespace on both mocks and a worker reads its forwarded mail through the Mailosaur SDK.

Fault presets (POST /__admin/faults {"preset": "<name>", "count"?: n}; GET /__admin/faults/presets): send_422 ({statusCode: 422, name: "validation_error", message}), send_429 (rate_limit_exceeded with retry-after and ratelimit-* headers), send_500 (internal_server_error), non_json_500 (an HTML 500 page), network_drop (the connection dies before an answer), receiving_500 (the received-email routes answer 500), webhook_duplicate, webhook_reorder, webhook_drop.

Namespaces

new Resend(key) cannot add headers, so map API keys to namespaces: PUT /__admin/credentials {"credentials": {"<RESEND_API_KEY>": "<namespace>"}}. Also x-mockingbird-namespace, or a /ns/<name> prefix on RESEND_BASE_URL.

Deliberately not modelled

  • Delivery itself: no SMTP, bounces, complaints, opens or clicks, and no email.sent / email.delivered webhooks. last_event is delivered at once (scheduled with scheduled_at, which is stored but never fires).
  • Batch send, templates, domains, API keys, audiences, contacts and broadcasts (our code calls none).
  • Attachment path URLs are not fetched; sent attachments keep metadata only.
  • Rate limits, except through send_429.
  • The idempotency window: keys never expire (Resend keeps them 24 h).

API

| Export | Kind | Description | | --- | --- | --- | | ResendAPI | class | The in-process mock: fetch(request), reset(), sent(), inbound(), receive(input, origin), state. Options: sqlite, now, namespace, publicNamespace, onSent. | | createRuntime | function | The mock with the full service contract (health, admin, namespaces, credentials, presets, Svix webhooks, outbox). Options: webhooks: {url, secret, retryDelaysMs?, fetch?}, forwardToInbox: {url, adminKey?, timeoutMs?, fetch?}, clock, seed, adminKey, onLog, sqlite. | | forwardToInbox | function | Copy one sent email into a Mailosaur mock's ingest route. | | RESEND_PRESETS | object | Every named fault preset. | | RESEND_NAMESPACE | string | The service name, "resend". | | DOWNLOAD_URL_TTL_MS | number | The advertised lifetime of a download_url (1 h). | | bareAddress | function | Name <[email protected]> → [email protected] (how the outbox's ?to= compares). | | document, operationIds, supportedOperationIds | values | The vendored OpenAPI contract and its operation ids. | | createServer, serveTarget, DEFAULT_PORT (./server) | Node | Serve over node:http; the serve CLI target (--webhook-url, --webhook-secret, --forward-to-inbox); port 8794. |

Part of mockingbird.