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

@saastemly/better-email

v0.1.0

Published

One email transport for Better Auth and its plugins — ready-made callbacks, shared wording, and a log of what was sent.

Readme

better-email

One email transport for Better Auth and its plugins — ready-made callbacks, shared wording, and a record of what was sent.

Why

Better Auth has no global email setting, by design: every feature takes its own callback and you write the function.

emailVerification: { sendVerificationEmail: … }
magicLink({ sendMagicLink: … })
emailOTP({ sendVerificationOTP: … })

That is right for a library and awkward for an app, which ends up with the same API key closed over in five places, five slightly different HTML strings, a sign-in email still saying "Acme" three releases after the rename, and no single answer to did the magic link actually go out?

const emailer = createEmailer({
  appName: "Deki",
  from: "Deki <[email protected]>",
  locale: "da",
  transport: apiKey ? resendTransport({ apiKey }) : smtpTransport(), // MailDev
  onSent: logSends(async () => (await getCurrentAuthContext()).context.adapter),
});

magicLink({ sendMagicLink: emailer.auth.sendMagicLink })
emailOTP({ sendVerificationOTP: emailer.auth.sendVerificationOTP })
email({ emailer })   // the plugin: a log, and a test-send endpoint

emailer.send() is there for everything else an app sends, so order confirmations go out with the same sender and land in the same log.

Transports

| | for | delivery | |---|---|---| | resendTransport | production | live | | smtpTransport | MailDev / MailHog / Mailpit on localhost:1025 | reported as not delivery | | consoleTransport | no dependencies at all | reported as not delivery |

smtpTransport speaks the plain-text subset of SMTP with no TLS and no AUTH, because that is exactly what a development catcher listens for. It refuses a non-loopback host unless you pass allowInsecure, so a typo in a hostname cannot put a message on the wire in the clear. It works in a Worker (cloudflare:sockets) and in Node/Bun (node:net).

The decisions worth knowing

  • An auth email never throws. Better Auth's own guidance is not to await these, so a slow provider cannot be timed. Throwing is worse than slow: it turns a mail outage into a failed sign-in, and an address that errors while another does not tells an attacker which accounts exist. Transports return failures; they do not raise them.
  • A recorder is never reported as delivery. MailDev's "sent" means "caught here". transport: "none" marks that, and /email/test reports delivered: false for it — a test message that lied about this would be worse than no test message.
  • Bodies are not stored. A magic link in a log is a credential sitting in a table more people can read than can read the inbox it was sent to. The log keeps recipient, subject, kind, provider id and the provider's own error.
  • A failing log never fails a send. A database that is down should not turn a delivered magic link into an error the customer sees.
  • HTML derived from text is escaped, so a name cannot inject markup.

The log is the point

When a customer says the confirmation never arrived, the question is whether the provider accepted it. Without a log the only answer is a shrug and a resend. With one it is a row carrying the provider's own words — "The from address is not verified" is a five-minute fix or an unanswerable mystery, depending only on whether anybody wrote it down.

GET  /email/log?status=failed
POST /email/test { to }