@kleb/mail (0.2.0)
Installation
@kleb:registry=npm install @kleb/mail@0.2.0"@kleb/mail": "0.2.0"About this package
@kleb/mail
Provider-neutral mail primitives with a server-only SMTP sender.
import { createSmtpMailSender } from "@kleb/mail/server";
const sender = createSmtpMailSender({
host: "smtp.example.com",
port: 587,
secure: false,
pool: true,
maxConnections: 5,
rateLimit: 10,
auth: {
user: "user",
pass: "secret",
},
retry: {
maxAttempts: 3,
delayMs: (attempt) => attempt * 250,
},
});
await sender.send({
from: { address: "hello@example.com", name: "Example" },
to: "ada@example.com",
subject: "Welcome",
html: "<p>Hello Ada</p>",
text: "Hello Ada",
});
Use provider adapters from explicit subpaths when SMTP is not the right transport:
import { createResendMailSender } from "@kleb/mail/resend";
const sender = createResendMailSender({
apiKey: process.env.RESEND_API_KEY,
});
Available provider subpaths are @kleb/mail/resend, @kleb/mail/postmark,
@kleb/mail/sendgrid, and @kleb/mail/ses.
Messages support threading, custom envelopes, list headers, DSN options,
alternatives, AMP, calendar events, inline attachments via cid, SMTP DKIM, and
OAuth2 SMTP auth. File and URL attachments are only available from
@kleb/mail/server and require explicitly enabling Nodemailer file or URL
access on the server sender.
import { createSmtpMailSender, type ServerMailMessage } from "@kleb/mail/server";
const message: ServerMailMessage = {
from: "hello@example.com",
to: "ada@example.com",
subject: "Report",
text: "See attached.",
attachments: [{ filename: "report.pdf", path: "C:\\reports\\report.pdf" }],
};
const sender = createSmtpMailSender({
host: "smtp.example.com",
port: 587,
disableFileAccess: false,
});
Use @kleb/templating to render email HTML/text, then pass the rendered output
to this package.
SMTP senders validate messages before handing them to Nodemailer. Use the provider-neutral helpers when composing custom senders:
import {
assertValidMailMessage,
createRetryingMailSender,
createValidatingMailSender,
} from "@kleb/mail";
const sender = createValidatingMailSender(
createRetryingMailSender(providerSender, {
maxAttempts: 3,
delayMs: 500,
}),
);
assertValidMailMessage(message);
await sender.send(message);
Validation covers address fields, subjects, custom headers, and metadata that becomes SMTP/MIME headers, including attachment filenames, list headers, DSN fields, calendar metadata, message IDs, and references. Control characters in these fields are rejected before a provider SDK or Nodemailer receives the message.
The SendGrid adapter creates an isolated SDK client for each API-key sender. Inject a client explicitly when tests or applications need full control over SendGrid transport state.
Use the outbox interfaces when mail retries need to survive process restarts:
import { enqueueMail, processDueMail } from "@kleb/mail";
await enqueueMail(store, message);
await processDueMail(store, sender);
Explicit outbox IDs must be unique. Reusing an existing ID throws instead of replacing a queued or completed job.
Use the config integration when SMTP settings should be loaded through @kleb/config:
import { kConfig } from "@kleb/config/server";
import { smtpMailConfig } from "@kleb/mail/config";
const loaded = kConfig("app.json").section("smtp", smtpMailConfig()).load();
Dependencies
Dependencies
| ID | Version |
|---|---|
| @types/nodemailer | 8.0.1 |
| nodemailer | ^9.0.3 |
Development dependencies
| ID | Version |
|---|---|
| @aws-sdk/client-sesv2 | 3.1079.0 |
| @sendgrid/mail | 8.1.6 |
| postmark | 4.0.7 |
| resend | 6.17.1 |
Peer dependencies
| ID | Version |
|---|---|
| @aws-sdk/client-sesv2 | ^3.1079.0 |
| @kleb/config | ^0.4.0 |
| @kleb/logging | ^0.3.0 |
| @sendgrid/mail | ^8.1.6 |
| postmark | ^4.0.7 |
| resend | ^6.17.1 |