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

@ultimat3/mail

v19.0.0

Published

Transactional email as data: one template renders HTML and text, sent through a job.

Readme

@ultimat3/mail ✉️

Transactional email as data. One template renders both an HTML part and a plain-text part, every string is an i18n key, every colour is a design token, and delivery is a job.

import { defineMail, send, blocks, t } from '@ultimat3/mail';

export const receiptMail = defineMail({
  id: 'receipt',
  subject: 'mail.receipt.subject',            // an i18n key, never a literal
  input: t.object({ name: t.string, url: t.url }),
  template: ({ data }) => [
    blocks.heading('mail.receipt.heading', { name: data.name }),
    blocks.paragraph('mail.receipt.body'),
    blocks.button('mail.receipt.cta', data.url),
  ],
});

await send(receiptMail, { name: user.name, url }, { to: user.email, locale: ctx.locale });

send validates data through the mail's schema, renders, then enqueues mail.send. It delivers inline only when { sync: true } is passed or no job driver is configured.

Rules

| Rule | Why | |---|---| | locale is required by the type | a mail is read hours later; there is no ambient request locale to fall back to. X_MAIL_LOCALE_MISSING is the backstop for JS callers | | Text part is mandatory | HTML-only mail scores as spam and is unreadable to screen readers. Empty text ⇒ X_MAIL_TEXT_MISSING | | Text is derived from blocks | never scraped out of the HTML, so the two parts cannot drift | | Every string is a key | mail.<id>.<slot>; English lives in src/catalog.ts and app catalogs override it | | Every colour is a token | MAIL_TOKENS in layout.ts holds light + dark hexes; templates never see a hex | | Every date takes an IANA zone | options.tz, else ctx.tz, else UTC | | No CR/LF in a header-bound field | checked in renderMessage and again in sendMailJob, so every driver refuses the same message (X_MAIL_HEADER_INVALID). mime.ts keeps its own gate for the headers the SMTP transport mints itself | | Sending is a job | retry: { attempts: 5, backoff: 'exponential' }, idempotency key derived from (mailId, recipients, hash(rendered)), or (mailId, your key) when you pass one — a caller's key is scoped to its mail so two templates cannot dedupe each other away |

Drivers

setMailDriver(driver) once at boot. Asking for one that was never set is X_MAIL_DRIVER_UNAVAILABLE.

| Driver | Use | Behaviour | |---|---|---| | createMemoryDriver({ clock? }) | dev, tests | retains messages; outbox() / lastTo() feed the /_x mail panel, ordered by SentMail.at — pass a frozenClock() to choose it | | createLogDriver() | workers without credentials | one structured line per message through core's logger; bodies never logged | | createUnconfiguredDriver(env) | a deploy that configured no transport | refuses every send with X_MAIL_CREDENTIAL_MISSING; delivers nothing and claims nothing | | createSmtpDriver({ url, from }) | prod | real ESMTP over Bun.connect: STARTTLS, AUTH PLAIN/LOGIN, quoted-printable MIME | | createResendDriver({ apiKey, from }) | prod | one POST /emails, Idempotency-Key on every request |

Which one a boot installs

selectMailDriver(env) is the one answer, and x dev and runRole both call it. Nothing about the app changes between environments; the credential does.

| env | driver | |---|---| | (nothing set), development / test | createMemoryDriver() — caught, never sent | | (nothing set), staging / production | createUnconfiguredDriver(...) — every send is X_MAIL_CREDENTIAL_MISSING | | SMTP_URL + MAIL_FROM | createSmtpDriver(...), MAIL_POOL_SIZE optional | | RESEND_API_KEY + MAIL_FROM | createResendDriver(...) |

No credential outside development is a refusal, not the embedded default. The memory driver there answered accepted for mail that never left the process — password resets, receipts and invitations all reported as sent, none delivered, no error anywhere. The refusal lands on the send, not on the boot, so an app that sends no mail still deploys. Which environment this is comes from core's isLocal() (ULTIMATE_ENV, else NODE_ENV, else development) — mail does not own a second reading of it.

Both credentials at once is X_CONFIG_INVALID rather than a winner picked for you, and a transport with no MAIL_FROM is refused at boot instead of on the first send. A host that is not x dev calls selectMailDriver itself, or constructs a driver directly — setMailDriver is the only seam either way.

SMTP

smtps://user:pass@host:465 is implicit TLS; smtp://host:587 starts in the clear and upgrades with STARTTLS. Credentials are percent-decoded, so a password with @ or / works.

| Rule | Why | |---|---| | A server offering no STARTTLS is refused | the message and the password would cross in the clear. allowInsecure: true is the explicit opt-out | | Capabilities are re-read after STARTTLS | most servers only advertise AUTH once encrypted, and a cleartext EHLO can be stripped in flight | | Any rejected recipient fails the send | delivering to three of four addresses and reporting success is the one outcome a caller cannot detect | | poolSize (default 4) caps concurrent connections | a burst of sends queues instead of opening one socket each | | Bcc never reaches a header | it travels in RCPT TO only | | Every envelope address is gated for CR/LF | MAIL FROM/RCPT TO are built by interpolation, and bcc is the one address no header check ever sees. Refused (X_MAIL_ADDRESS_INVALID), never stripped — a rewritten address delivers somewhere else | | The reported id is the Message-ID | an SMTP 250 carries nothing a caller could correlate | | The Message-ID is stable per message | SMTP has no idempotency protocol, so it is the one identifier a receiving mailbox can collapse a retry on. A fresh token per attempt made a timeout past DATA a second email. It is a one-way digest of mailIdempotencyKey, never the key: the key holds the recipient list, and this header is visible to all of them |

Resend

RESEND_API_KEY and a verified sending domain. Every request carries Idempotency-Key: mailIdempotencyKey(message) — a job retry after a timeout hands Resend the identical message, and that header is what makes it one email. 408/409/425/429 and 5xx are retryable (isRetryableStatus, @ultimat3/core); every other non-2xx is a configuration problem that retrying cannot fix.

Framework mails

Registered by importing them — the import IS the registration. FRAMEWORK_MAILS is the list, and registeredMails() / registeredMailIds() answer for an app's own mails as well. There is no x mail command; a host that wants to list or preview a mail calls those and renderMessage().

| id | Input | |---|---| | welcome | { name, appName, url } | | verify-email | { name, url, expiresMinutes } | | reset-password | { name, url, expiresMinutes } | | invite | { inviterName, orgName, url, expiresHours } | | mfa-enrolled | { name, method, at } | | security-alert | { name, event, ip, at } |

Translating them = shipping mail.* keys in an app catalog. Never edit a template.

Errors

| Code | Fix | |---|---| | X_MAIL_LOCALE_MISSING | send(mail, data, { to, locale: ctx.locale }) | | X_MAIL_TEMPLATE_UNKNOWN | export a defineMail({ id }) and import it (also raised for an unregistered layout) | | X_MAIL_DUPLICATE | rename one of two defineMail({ id }) declarations | | X_MAIL_TEXT_MISSING | add a text-bearing block to the template | | X_MAIL_DRIVER_UNAVAILABLE | setMailDriver(createMemoryDriver()) at boot — a wiring bug | | X_MAIL_CREDENTIAL_MISSING | set SMTP_URL (or RESEND_API_KEY) and MAIL_FROM in the deployment — an operations one | | X_MAIL_HEADER_INVALID | strip CR/LF from the interpolated value before it reaches a header | | X_MAIL_ADDRESS_INVALID | pass a bare addr-spec — an envelope address may hold no control character and no </> | | X_MAIL_SEND_FAILED | the cause names the stage, the provider's status and whether a retry can help — and so does error.retry, which is what sendMailJob acts on: terminal dead-letters a 550 or a rejected credential at attempt 1 instead of sending it four more times |

Commands

bun test packages/mail
bun run --filter @ultimat3/mail typecheck