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

@emulon/resend

v0.3.0

Published

```ts import resend from '@emulon/resend'; import { Emulon } from 'emulon'; import { Resend } from 'resend';

Readme

Local Resend reference

import resend from '@emulon/resend';
import { Emulon } from 'emulon';
import { Resend } from 'resend';

await using env = await Emulon.start({ services: { mail: resend() } });
const { apiKey } = await env.services.mail.keys.create();
const client = new Resend(apiKey, { baseUrl: env.endpoints.mail.api });
const { data, error } = await client.emails.send({
  from: 'Acme <[email protected]>',
  to: ['[email protected]'],
  subject: 'Hello',
  html: '<p>Local only</p>',
});
if (error || !data) {
  throw new Error('Local email send failed');
}
console.log(await client.emails.get(data.id));
console.log(await env.services.mail.emails.list());
console.log(await env.services.mail.emails.get({ id: data.id }));
await env.reset();

The official client is tested at [email protected] using its baseUrl option. Install it separately in the consuming application. No email is actually sent. Each environment owns isolated loopback listeners on dynamic ports (or on fixed ports configured for the instance); disposal closes them. Keys are securely generated per instance and reset invalidates them.

For a project running emulon up, the same registry exposes:

emulon mail keys create --json
emulon mail emails list --json
emulon mail emails get --id <email-uuid> --json
emulon mail emails clear --json
emulon reset --json

keys create explicitly returns a secret for configuring the provider client. emails clear deletes emails, retaining keys and event history. reset restores all environment fixtures and removes generated credentials and events. SDK emails.clear() and env.reset() have the corresponding semantics.

Fixtures use resend({ fixtures: { emails: [{ from, to, subject, text }] } }). An optional UUID id fixes an email's identity; generated fixture IDs and creation times remain stable across reset. Fixtures do not publish events.

Supported provider operations are POST /emails and GET /emails/:id, with bearer authentication. Sending accepts from, to, subject, html, text, cc, bcc, and reply_to; at least one body representation is required. HTML-only messages retain null text rather than generating a text alternative. Reading includes the original bodies and last_event: "sent". Send atomically records email.sent in the outbox; eligible subscriptions now receive queued deliveries, which the worker sends with Svix signatures.

Domains, audiences, attachments, templates, scheduling, tags, custom headers, idempotency keys and explicit API versions are unsupported. Unknown routes return 404 Unsupported route; unsupported send fields return JSON with status 501. GET /health remains an unauthenticated lifecycle probe. The npm manifest's emulon.compatibility, generated from src/compatibility.ts, describes this unversioned API subset and its limitations. See ADR 0008.

Events

Publish a synthetic event without creating or changing an email:

emulon mail events publish email.sent --data ./event.json
emulon events --type email.sent
emulon events --follow

The JSON file contains email_id (UUID), from, to (array), and subject. Inline JSON objects are also accepted by --data. The same typed command is env.services.mail.events.publish({ type: "email.sent", data }). Use await env.events.list({ type: "email.sent" }) to read history or await env.events.follow({ type: "email.sent" }) to subscribe before triggering an action. Cancel the returned stream when finished.

Synthetic records have origin published; provider sends have origin service. Private history is in memory; project history persists across restarts. Reset clears history and rejects overlapping reads. Follow emits future commits only, fails on reset (subscribe again afterwards), and ends on environment disposal. Subscribed events produce signed webhook deliveries. See ADR 0009.

Webhook queue

Configure initial destinations with resend({ destinations: [{ id: "app", url: "http://127.0.0.1:3000/hooks", secret: "whsec_bG9jYWwtc2VjcmV0", types: ["email.sent"], enabled: true }] }), or use the same command from CLI/SDK:

emulon mail webhooks configure --id app --url http://127.0.0.1:3000/hooks --secret whsec_bG9jYWwtc2VjcmV0 --types '["email.sent"]' --enabled true
emulon mail webhooks destinations
emulon mail webhooks list

env.services.mail.webhooks.configure({...}) replaces a destination by ID. Disabling cancels queued deliveries; enabling does not backfill old events or revive cancellations. Secret rotation preserves queued IDs and updates the secret for future sending. Lists omit secrets. Reset restores configuration destinations captured at startup. Private SDK starts use memory; project hosts retain resources, keys, destinations and outbox across restart through ADR 0016. Independent process-kill delivery verification is described in delivery verification. See ADR 0010.

The worker signs the exact UTF-8 body with Web Crypto and accepts 2xx responses. It uses a five-second timeout and no automatic retries; failures appear as failed in webhooks list. Provider and management mutations wait for due deliveries to finish; read-only commands observe committed state immediately. Secrets are base64 keys with an optional whsec_ prefix. Claimed sends keep the destination credentials captured at claim time. See ADR 0011.

Direct sending, inspection and redelivery

emulon mail webhooks send email.sent --data ./event.json --destination app
emulon mail webhooks list
emulon mail webhooks inspect <delivery-id>
emulon mail webhooks wait <delivery-id> --status succeeded --timeout 10s
emulon mail webhooks redeliver <delivery-id>

destination is an enabled configured destination ID. Direct sending records an event with origin direct, without changing email state or sending to other subscriptions. The payload is the same validated shape as events publish.

const delivery = await env.services.mail.webhooks.send({
  type: 'email.sent',
  data,
  destination: 'app',
});
console.log(await env.services.mail.webhooks.inspect({ id: delivery.id }));
await env.services.mail.webhooks.redeliver({ id: delivery.id });
await env.services.mail.webhooks.wait({
  id: delivery.id,
  status: 'succeeded',
  timeout: '10s',
});

Send and redeliver return the queued transaction record; list, inspect and wait observe subsequent status. Redelivery retains the exact original body and svix-id, creates a new attempt, and recalculates the timestamp and signature using the current destination secret. A queued or in-flight delivery cannot be redelivered concurrently. Disabled destinations must be enabled first.

Inspection includes the delivery and all attempts, with safe header metadata; secrets, authorization/signature headers and receiver response bodies are omitted. Request bytes contain application event data and must not include application credentials. Wait subscribes to committed changes without polling. Timeouts use positive integer ms, s or m durations and return WAIT_TIMEOUT with current status (nonzero CLI exit). Reset drains outstanding waits; environment disposal closes their state. See ADR 0012.

Delivery fault scenarios and their verification are documented in events and delivery.

Query this declaration with emulon mail compatibility get --json or env.services.mail.compatibility.get({}); see the shared compatibility contract.