Compared with alternatives
@alexify/mailer is an email library in the place of the libraries below, built towards a reliability layer none of them has. The table compares fourteen solutions in five directions: SMTP clients (Nodemailer, emailjs), unified libraries over many providers (Upyo, unemail, sently), SMTP for edge runtimes (worker-mailer, edge-mailer), a MIME builder (MIMEText) and the official SDKs of the providers (Resend, SendGrid, Mailgun, Postmark, Mailtrap, Amazon SES). It follows the idea of Upyo's own comparison, with every feature in one table rather than a few convenient libraries in a few convenient rows. @alexify/mailer itself is the first column, in teal and, on a wide screen, pinned beside the features as the table scrolls, so every row reads against it.
What the table shows
- None of the fourteen has a persistent queue, a local audit log and one model of delivery events. unemail plans a queue. This is the gap @alexify/mailer is built for; see Why @alexify/mailer?.
- Breadth and correctness pull against each other. sently covers SMS, push, WhatsApp and fourteen HTTP providers, but reading its code points to a bug in the canonicalization of the DKIM body and in SendGrid batches. Nodemailer and unemail are narrower and more precise.
- Edge runtimes and SMTP barely mix. It works only through
cloudflare:sockets(sently, worker-mailer, edge-mailer), and port 25 is blocked in Workers. On the edge, an HTTP provider is the realistic option. - Idempotency is in 2 of the 14 (Resend and unemail). SES v2 has no
ClientToken, so a retry after a timeout can send a message twice.
The table
| Feature | @alexify/mailer unified + reliability | Nodemailer 10 SMTP/MIME | emailjs 5 SMTP | Upyo unified | unemail unified | sently unified + multi-channel | worker-mailer edge SMTP | edge-mailer edge SMTP | MIMEText MIME builder | Resend SDK SDK | SendGrid SDK SDK | Mailgun.js SDK | Postmark.js SDK | Mailtrap SDK SDK | AWS SES v3 SDK, client-sesv2 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Runtimes (from imports and CI, not run) | |||||||||||||||
| Node.js, minimum version | ✅ ≥22, CI on 22 and 24 | ✅ ≥20 | ⚠️ ≥18, CI 20+ | ✅ ≥20 | ✅ ≥20.11 | ✅ ≥18 | ❌ Workers only | ⚠️ needs a connector of your own | ✅ no engines field | ✅ ≥20 | ✅ ≥12 | ✅ ≥18 | ✅ ≥18 | ✅ ≥16.20 | ✅ ≥20 |
| Deno | ✅ CI: unit and runtime suites1 | ❌ not declared | ❌ not declared | ✅ CI: deno test | ⚠️ HTTP drivers, not SMTP | ✅ Deno.connect | ❌ | ✅ edge-mailer/deno | ⚠️ browser-entry | ⚠️ fetch, not tested | ⚠️ through Node compatibility | ⚠️ not declared | ⚠️ fetch, not declared | ⚠️ not declared | ⚠️ through npm: |
| Bun | ✅ CI: unit and runtime suites | ✅ CI: test:bun | ❌ not declared | ✅ CI: bun test | ✅ CI: bun | ✅ CI: bun test | ❌ | ❌ no connector | ⚠️ browser-entry | ⚠️ fetch | ⚠️ through Node compatibility | ⚠️ not declared | ⚠️ not declared | ⚠️ not declared | ⚠️ through Node compatibility |
| Cloudflare Workers / edge | ❌ not a target yet | ⚠️ only with nodejs_compat | ❌ | ⚠️ HTTP packages yes, SMTP no | ⚠️ HTTP + 3 Cloudflare drivers, no SMTP | ✅ HTTP + SMTP (cloudflare:sockets) | ✅ the only target | ✅ no node:* | ⚠️ browser-entry | ⚠️ fetch, not tested | ⚠️ fs, axios; not declared | ⚠️ fetch mode | ⚠️ declared in 5.0.0 | ❌ http/https agents | ⚠️ explicit credentials needed |
| Browser | ❌ by design | ❌ | ❌ | ⚠️ no target, no tests | ✅ HTTP drivers only | ⚠️ throws | ❌ | ❌ | ✅ browser-bundle | ⚠️ secret key in the client | ❌ | ✅ needs a CORS proxy | ❌ | ❌ | ✅ explicit credentials |
| Transports and delivery reliability | |||||||||||||||
| SMTP client | ✅ PIPELINING, SIZE, DSN | ✅ SMTP + LMTP | ✅ no LMTP | ✅ PIPELINING, SIZE, DSN | ✅ own implementation | ✅ own implementation | ✅ | ✅ | — | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Sendmail / local binary | ✅ ./sendmail, exit codes classified | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | — | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| JMAP | ✅ RFC 8620/8621 | ❌ | ❌ | ✅ RFC 8620/8621 | ❌ | ❌ | ❌ | ❌ | — | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| HTTP providers, count | 18 | 1 (SES, needs the SDK) | 0 | 8 | 18 + http + 2 CF | 14 + CF binding | 0 | 0 | — | 1 | 1 | 1 | 1 | 1 | 1 |
| Failover between providers | ✅ RR, weighted, priority, circuit breaker2 | ⚠️ host DNS fallback only | ❌ | ✅ RR, weighted, priority | ✅ fallback, RR, weighted, tee | ✅ fallback + weighted | ❌ | ❌ | — | ❌ | ❌ | ❌ | ❌ | ❌ | ⚠️ multi-region endpoint on the AWS side |
| SMTP connection pool | ✅ on by default, 5, 100 messages each | ✅ max 5, 100 msg | ❌ one connection | ✅ on by default | ✅ off by default | ✅ off by default | ❌ | ✅ | — | — | — | — | — | — | — |
| Retry / backoff | ✅ jitter, Retry-After, no blind resend2 | ⚠️ requeue in the pool only | ⚠️ 1 retry on 450/451 | ✅ jitter, Retry-After | ✅ 5 modes, jitter | ✅ no jitter, retries SMTP 5xx | ❌ | ❌ retryHint only | — | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ standard / adaptive |
| Rate limiting | ✅ token bucket per key, FIFO, shared store | ✅ rateLimit in the pool | ❌ | ⚠️ sendMany only | ✅ token bucket | ⚠️ pool + sendBulk | ❌ | ❌ | — | ❌ | ⚠️ contact importer only | ❌ | ⚠️ 429 → error | ❌ | ⚠️ adaptive retry |
| Circuit breaker | ✅ one probe in half-open | ❌ | ❌ | ❌ | ✅ half-open without a single probe | ⚠️ cooldownMs | ❌ | ❌ | — | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Timeout / AbortSignal | ✅ signal + timeout over the body | ⚠️ timeouts, no AbortSignal | ⚠️ timeout, no signal | ✅ signal + timeout | ⚠️ signal for HTTP, not SMTP | ⚠️ SMTP connect only | ⚠️ socket timeouts | ✅ signal on connect and send | — | ⚠️ signal, no timeout | ⚠️ global signal only | ⚠️ timeout, no signal | ⚠️ 180 s timeout | ⚠️ hard 10 s | ✅ abortSignal + requestTimeout |
| Persistent queue (survives a restart) | ✅ MongoDB outbox (transactional), BullMQ driver, memory queue3 | ❌ | ❌ | ❌ | 🔜 planned | ❌ | ❌ | ⚠️ sample outbox only | — | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Messages | |||||||||||||||
| Attachments | ✅ path, URL, data URI, stream, bytes, Blob4 | ✅ path, URL, Buffer, stream, data-URI | ⚠️ path, stream, no URL | ⚠️ bytes, Blob, File, factory | ⚠️ bytes, string, URL | ⚠️ bytes + path | ⚠️ base64 only | ⚠️ string, bytes, Blob | ⚠️ base64 only | ⚠️ base64 or URL | ⚠️ base64, helper for Buffer | ✅ Buffer, Blob, stream, 50 MB | ✅ base64 | ✅ string / Buffer | ✅ Uint8Array or Raw MIME |
| Streaming large attachments | ⚠️ SMTP, sendmail; HTTP providers buffer | ⚠️ SMTP yes; DKIM and SES buffer | ✅ | ⚠️ SMTP only | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ⚠️ accepts a stream | ❌ | ❌ | ❌ |
| Inline images (CID) | ✅ multipart/related | ✅ multipart/related | ⚠️ manual Content-ID | ⚠️ mixed, not related; Mailgun bug | ⚠️ mixed, not related | ⚠️ HTTP: 3 providers only | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Sending raw MIME | ✅ SMTP, sendmail, JMAP, SES, Mailgun, SparkPost, Cloudflare | ✅ | ❌ | ⚠️ SMTP/JMAP only | ⚠️ smtp driver only | ❌ | ❌ | ❌ | — builds MIME only | ❌ | ❌ | ✅ /messages.mime | ❌ | ❌ | ✅ Content.Raw |
| iCalendar | ✅ every METHOD | ✅ icalEvent + METHOD | ⚠️ manual | ✅ every METHOD | 🔜 planned | ❌ | ❌ | ✅ | ⚠️ as an attachment | ❌ | ❌ | ⚠️ as an attachment | ⚠️ as an attachment | ⚠️ as an attachment | ⚠️ through Raw MIME |
| Unicode / IDN | ✅ punycode + SMTPUTF8 | ✅ punycode + SMTPUTF8 | ⚠️ Q-encoding, no punycode | ⚠️ SMTPUTF8, no automatic punycode | ⚠️ RFC 2047/2231 | ⚠️ one RFC 2047 word | ⚠️ RFC 2047 | ⚠️ SMTPUTF8, no punycode | ⚠️ Subject as one B-word | ⚠️ UTF-8 JSON | ⚠️ UTF-8 JSON | ❌ | ❌ | ⚠️ UTF-8 JSON | ⚠️ Charset, no IDN |
| List-Unsubscribe | ✅ RFC 8058 one-click, DKIM-signed | ✅ list.unsubscribe | ❌ | ⚠️ manual header | ✅ RFC 8058 oneClick | ❌ | ⚠️ headers | ⚠️ headers | ❌ | ⚠️ headers, topics | ⚠️ headers, asm | ⚠️ headers | ⚠️ headers | ⚠️ headers | ✅ ListManagementOptions |
| Built-in templates | ✅ 9 engines passed in: EJS, Handlebars, Pug, MJML, Liquid, Nunjucks, Eta, Mustache, React Email5 | ❌ | ❌ | ❌ | ⚠️ Renderer interface | ✅ + plugins | ❌ | ❌ | ❌ | ✅ Templates API | ✅ dynamic templates | ✅ Templates API | ✅ Templates API | ✅ Templates API | ✅ Templates API |
| React Email | ✅ render passed in | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Batch / bulk | ✅ native: Resend, Postmark, Mailjet, Mailtrap, JMAP; concurrency elsewhere | ❌ | ❌ | ✅ native: Resend, Mailtrap, JMAP | ✅ sendBatch | ⚠️ sendBulk, SendGrid bug | ⚠️ queue per session | ✅ | — | ✅ up to 100, no attachments | ✅ personalizations | ⚠️ recipient-variables | ✅ batch + bulk API | ✅ batchSend, 500 | ✅ SendBulkEmail |
| Scheduled sending | ✅ queue delay; native in 8 providers6 | ❌ | ❌ | ❌ | ⚠️ 8 drivers | ❌ | ❌ | ❌ | — | ✅ | ✅ | ✅ up to 3 days | ❌ | ⚠️ campaigns only | ❌ |
| Tags / metadata | ✅ metadata in 17 providers, tags in 13 | ⚠️ headers; tags on SES only | ⚠️ headers | ⚠️ tags in 7 providers | ✅ | ⚠️ headers only | ⚠️ headers | ⚠️ headers | ⚠️ headers | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Open/click tracking | ✅ per message in 10 providers | ❌ | ❌ | ⚠️ config only | ✅ 10 drivers | ❌ | ❌ | ❌ | — | ⚠️ per domain | ✅ | ✅ | ✅ | ⚠️ per domain | ✅ per-message override |
| Idempotency key | ✅ decorator with reserved keys, a MongoDB store + 5 providers | ❌ | ❌ | ⚠️ Resend, Lettermint, Maileroo | ✅ middleware + providers | ⚠️ decorator; batch bypasses it | ❌ | ❌ | — | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Security and authentication | |||||||||||||||
| DKIM signing in the library | ✅ RSA + Ed25519, streaming, over-signing | ✅ RSA-SHA256 | ❌ | ✅ RSA + Ed25519, streaming | ✅ RSA + Ed25519, SMTP only | ⚠️ body canonicalization bug | ❌ | ✅ RSA | ❌ | ⚠️ keys through the API | ⚠️ whitelabel, generic | ⚠️ keys through the API | ⚠️ keys through the API | ⚠️ domain DNS records | ⚠️ Easy / BYO DKIM |
| OAuth2 for SMTP | ✅ refresh, OAUTHBEARER | ✅ refresh + service account | ⚠️ bearer token only | ✅ refresh, OAUTHBEARER | ⚠️ callback, no refresh | ✅ refresh client | ❌ | ⚠️ no refresh | — | — | — | — | — | — | — |
| ARC | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | — | — | — | — | — | — |
| DSN | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ | ✅ | — | — | — | — | — | — | — |
| Proxy | ✅ SMTP: HTTP CONNECT or any connect; HTTP: fetch | ⚠️ HTTP CONNECT, SOCKS through a module | ❌ | ❌ | ⚠️ custom fetch | ❌ | ❌ | ❌ | — | ⚠️ baseUrl only | ⚠️ axios proxy | ✅ | ⚠️ custom fetch + undici | ⚠️ environment only | ⚠️ through requestHandler |
verify() / connection check | ✅ SMTP, sendmail, JMAP, 13 HTTP providers7 | ✅ | ⚠️ VRFY, not a check | ⚠️ SMTP/JMAP only | ⚠️ isAvailable(), nominal | ✅ | ❌ | ⚠️ probe() without auth | — | — | — | ⚠️ domain DNS only | ⚠️ getServer() | ⚠️ getAllAccounts() | ⚠️ GetAccount |
| SMTP AUTH mechanisms | PLAIN, LOGIN, XOAUTH2, OAUTHBEARER | PLAIN, LOGIN, CRAM-MD5, XOAUTH2 | CRAM-MD5, LOGIN, PLAIN, XOAUTH2 | PLAIN, LOGIN, XOAUTH2, OAUTHBEARER | PLAIN, LOGIN, CRAM-MD5, XOAUTH2 | PLAIN, LOGIN, CRAM-MD5, XOAUTH2 | PLAIN, LOGIN, CRAM-MD5 | PLAIN, LOGIN, CRAM-MD5, XOAUTH2 | — | — | — | — | — | — | — |
| SMTP TLS policies | ✅ requireTls, SNI, CA, cert, minVersion | ✅ REQUIRETLS, SNI, no DANE | ⚠️ STARTTLS accepts any certificate | ✅ requireTls, CA, cert, minVersion | ✅ requireTLS, off by default | ✅ requireTLS by default with auth | ⚠️ falls back to plaintext + AUTH | ✅ tlsPolicy ×4, REQUIRETLS | — | — | — | — | — | — | — |
| Platform features (events, domains, provider API) | |||||||||||||||
| Webhooks: parsing and signature verification | ✅ 15 providers, verified before parsing8 | ❌ | ❌ | ❌ | 🔜 planned | ✅ 10 parsers, HMAC in 5 | ❌ | ❌ | — | ✅ standardwebhooks | ✅ ECDSA | ⚠️ CRUD, no verification | ⚠️ CRUD + types, no verification | ✅ HMAC verify | ❌ SNS signature not in the SDK |
| Inbound mail | ❌ out of scope | ❌ | ❌ | ❌ | 🔜 planned | ❌ | ❌ | ❌ | — | ✅ | ⚠️ parser with a bug | ⚠️ routes | ✅ | ✅ | ❌ legacy client-ses only |
| Suppression / bounce API | 🔜 per tenant, before the queue9 | ❌ | ❌ | ❌ | 🔜 planned | ❌ | ❌ | ❌ | — | ✅ | ⚠️ generic client | ✅ | ✅ | ✅ | ✅ |
| Domain management (API) | ❌ out of scope | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | — | ✅ | ⚠️ generic client | ✅ | ✅ | ✅ | ✅ identities |
| Address validation | ⚠️ RFC 5322/6531 syntax | ⚠️ parser only | ⚠️ parser only | ⚠️ RFC 5322/6531 syntax | ⚠️ regex | ⚠️ regex | ❌ | ❌ syntax assert | ❌ | ❌ | ❌ | ✅ /v4/address/validate | ❌ | ❌ | ✅ GetEmailAddressInsights |
| Sending log on the provider side | — | — | — | — | — | — | — | — | — | ✅ logs API | ⚠️ generic /v3/messages | ✅ logs API | ⚠️ events, opens, clicks | ⚠️ emailLogs | ⚠️ event destinations + archiving |
| Provider API coverage | — | — | — | — | — | — | — | — | — | 18 namespaces, ~117 calls | only mail/send typed; ~251 endpoints in USAGE.md | 21 clients, ~155 calls | 85 methods | ~126 calls | 116 commands |
| Multi-channel: SMS, WhatsApp, push | ❌ out of scope | ❌ | ❌ | ❌ | ❌ | ✅ SMS, WhatsApp, webpush, FCM | ❌ | ❌ | — | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Observability and extensibility | |||||||||||||||
| OpenTelemetry | ✅ decorator: spans + metrics, and those of the queue's worker | ❌ | ❌ | ✅ decorator: spans + metrics | ⚠️ withLogger | ⚠️ hooks + consoleObserver | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ⚠️ middlewareStack |
| Hooks / middleware | ✅ ./hooks, decorators, a pino-like logger | ✅ use(compile|stream) | ⚠️ logger function | ⚠️ decorators, LogTape | ✅ Middleware pipeline | ✅ hooks + plugins | ⚠️ logger | ✅ onEvent | ⚠️ dump() | ⚠️ console.error | ⚠️ setClient | ❌ | ❌ | ❌ | ✅ logger + middlewareStack |
| Mock / test mode | ✅ ./mock | ✅ jsonTransport, streamTransport, Ethereal | ⚠️ Message#read() | ✅ @upyo/mock | ✅ mock + Mailpit | ⚠️ preview, Mailpit | ❌ | ❌ | — | ❌ | ✅ sandboxMode | ✅ testmode | ⚠️ Sandbox server | ✅ Sandbox API | ⚠️ TestRenderEmailTemplate |
| Local audit log / event journal | ⚠️ state model + sinks, MongoDB among them; crypto-shredding next10 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ⚠️ events, not stored | — | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Quality, license, maturity | |||||||||||||||
| TypeScript types | ✅ hand-written, tsd | ✅ built in | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ hand-written .d.ts | ✅ | ✅ | ✅ | ✅ generated |
| Runtime dependencies | 0 | 0 | 0 | 0–1 (@noble/hashes) | 0 | 0 | 0 | 0 | 4 | 2 | 2+ (axios) | 3 (axios) | 0 | 2 (axios, qs) | 9 |
| Module format | CJS, ESM import | ESM + CJS | ESM only | ESM + CJS + JSR | ESM only | ESM only | ESM + CJS | ESM + CJS + JSR | ESM + CJS | ESM + CJS | CJS only | ESM + CJS | CJS only | CJS only | ESM + CJS |
| License | MIT | MIT-0 | MIT | MIT | MIT | MIT | MIT | MIT | MIT | MIT | MIT | MIT | MIT | MIT | Apache-2.0 |
| Version (2026-10-07) | 1.2.0 | 10.0.16 | 5.0.3 | 0.7.0 (unreleased; release 0.6.0) | 0.7.0 | 1.2.2 | 1.2.1 | 0.8.5 | 3.0.24 (tag 3.0.28) | 6.32.1 | 8.1.6 | 14.0.2 | 5.1.0 | 4.10.0 (HEAD ahead) | 3.1147.0 |
| Last commit | 2026-10 (this release) | 2026-10-07 | 2026-08-26 | 2026-09-09 | 2026-09-02 | 2026-08-19 | 2025-11-15 | 2026-09-08 | 2026-01-28 | 2026-10-06 | 2025-09-19 | 2026-10-07 | 2026-07-10 | 2026-10-07 | 2026-10-06 |
| Tests: files / cases | 183 / ~2690 | 61 / ~1330 | 15 / ~153 | 109 / ~1300 | 40 / ~773 | 66 / ~532 | 4 / ~189 | 14 / ~258 | 6 / ~30 | 39 / ~441 | 43 / ~450 | 49 files | 23 (16 integration) | 55 files | 2 smoke (the rest in the monorepo) |
| Owner / maintenance risk | 1 author, Alexis Technologies | 1 maintainer | no SECURITY.md | 1 main author | 1 author | 1 author, 0.1 → 1.2 in ~80 days | 1 author | 1 author, 0.x | no SECURITY.md | vendor | vendor, old toolchain | vendor | vendor | vendor | vendor |
✅ in the code · ⚠️ partial, or only on the provider side · ❌ none · 🔜 planned by the author, no code yet · — does not apply. Every cell was decided by reading the source of the project as of 2026-10-07, not its README; the column of @alexify/mailer is this repository as of 2026-10-18.
- Through
npm:@alexify/mailer. Deno’sfetchsends a request again when a reused connection closes before its answer, so the docs give it afetchof its own (known issues). - A failure after which the provider may have taken the message is marked
deliveryUnknown:./retrydoes not retry it and./pooldoes not fail over with it. - Permanent failures fail at once,
Retry-Afteris a minimum, and a delivery of unknown outcome is never sent blindly again; in MongoDB, a job is written in the transaction of its cause. Jobs are signed (HMAC) and may be sealed, their message dropped once done with; tenants take turns, and a job that kills its worker is given up. - Nothing is read implicitly:
fromPathandfromUrlare called by the application,fromUrlchecks every redirect againstallowUrl(no private address by default) and bounds time and bytes. - Names, never paths; locales, layouts, front-matter subjects, CSS inlined without remote fetches, text derived.
- A transport that cannot schedule refuses the message before sending it, rather than sending it at once.
- The other five have no request that checks a sending key without sending a message.
- A signature where the provider signs (SNS, Svix, ECDSA, HMAC), its Basic credentials or token where it does not; a window of time, and an id kept as taken only once the handler commits its events.
- On the roadmap of the reliability layer: designed, with no code yet.
- Each state of a message, from the queue to the webhooks, one event per change, written once by its id. The keyed HMAC and encrypted recipients are on the roadmap.
How the table was made
- From the code, not the README. Every cell was decided by reading the source of a local clone of each project as of 2026-10-07.
- The column of @alexify/mailer is this repository, as of 2026-10-18. ✅ and ⚠️ are code that is here: the transport ported from upyo, with the upstream bugs fixed since, and what 1.1 took from nodemailer, unemail and sently, with their bugs fixed in the port. 🔜 is the reliability layer of the plan, designed but with no code yet. Its gaps are shown as such: no edge runtime, streaming only over SMTP and sendmail, and scheduling only where the provider schedules.
- Defects were not reproduced, except in emailjs, which was run against fake SMTP servers. A cell that names a bug is a finding from reading the code; reproduce it before relying on it.
- The runtimes of the others were not run. Their Deno, Bun and Workers support is inferred from imports,
exportsmaps and CI configurations. The suites of @alexify/mailer run on Bun and Deno in its own CI. - The clones are shallow (one commit), so the history, the number of maintainers and the pace of releases are not taken from git. Test cases were counted with a regular expression.
- Amazon SES means the newest SES client of the AWS SDK for JavaScript v3,
@aws-sdk/client-sesv23.1147.0 (API 2019-09-27, 116 commands). The olderclient-ses(API 2010-12-01, 71 commands) is still supported and is needed only for inbound rules. Its retry internals, the defaults ofNodeHttpHandlerandAbortSignallive in@smithypackages and were judged from their configuration types. - Size limits of messages and attachments are set by the providers' documentation, not by the code of their SDKs, and are not in the table.