Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.8 KiB
6.8 KiB
@suaveplan/emailer - Universal email service with plugin transports (Dummy / SMTP via injectable SmtpClientLike / Gmail REST via FetchLike), nine lifecycle hooks, retry, queue dispatch, Zod-validated boundaries, OpenTelemetry-instrumented hot paths, injectable Clock/Rng/Logger/FetchLike/TimersLike, stable defineCodes('EMAILER', [...]) error registry.
Universal email service with pluggable transports. Ships
EmailService, three transports (Dummy / SMTP via injectable
SmtpClientLike / Gmail REST via injectable FetchLike), nine lifecycle
hooks, deterministic retry, structural queue dispatch, Zod-validated option
boundaries, OpenTelemetry-instrumented hot paths, and a stable
defineCodes("EMAILER", [...]) error registry.
Documentation tree
- email-service —
EmailService<TTransport>is the public entry point for@suaveplan/emailer. It orchestrates message validation, life…- base-email-transport —
BaseEmailTransportis the abstract helper that every concrete transport (DummyTransport,SmtpTransport, `GmailRes… - email-service —
EmailService<TTransport>is the public entry point for@suaveplan/emailer. It orchestrates message validation, life…
- base-email-transport —
- retry — Deterministic retry helper used by
EmailService.send. The helper takes aRetryPolicy, an injectableTimersLike, a…- retry — Deterministic retry helper used by
EmailService.send. The helper takes aRetryPolicy, an injectableTimersLike, a…
- retry — Deterministic retry helper used by
- validate — Shared address-formatting and message-validation helpers used by every concrete transport and by
EmailServiceitself.…- validate — Shared address-formatting and message-validation helpers used by every concrete transport and by
EmailServiceitself.…
- validate — Shared address-formatting and message-validation helpers used by every concrete transport and by
- error-codes —
@suaveplan/emailerships a single stable error-code registry that every failure path in the package routes through. T…- error-codes —
@suaveplan/emailerships a single stable error-code registry that every failure path in the package routes through. T…
- error-codes —
- fake-fetch — Deterministic test double for
FetchLike, used byGmailRestTransporttests. Queues scripted responses and captures e… - fake-smtp-client — Deterministic test double for
SmtpClientLike. Tests queue scripted responses, invoke the wrappedSmtpTransport, and… - otel — Single source of truth for every OpenTelemetry instrument emitted by
@suaveplan/emailer. Every production path that t…- metrics — Single source of truth for every OpenTelemetry instrument emitted by
@suaveplan/emailer. Every production path that t…
- metrics — Single source of truth for every OpenTelemetry instrument emitted by
- schemas — Centralised Zod schemas for every public option bundle and wire payload in
@suaveplan/emailer. The schemas exist to e…- schemas — Centralised Zod schemas for every public option bundle and wire payload in
@suaveplan/emailer. The schemas exist to e…
- schemas — Centralised Zod schemas for every public option bundle and wire payload in
- dummy —
DummyTransportis a deterministic in-memory transport designed for tests, fixtures, and local development. It records…- dummy-transport —
DummyTransportis a deterministic in-memory transport designed for tests, fixtures, and local development. It records…
- dummy-transport —
- gmail-rest —
GmailRestTransportsends mail through Gmail's REST API directly, over an injectableFetchLike. The transport builds…- gmail-rest-transport —
GmailRestTransportsends mail through Gmail's REST API directly, over an injectableFetchLike. The transport builds…
- gmail-rest-transport —
- smtp —
SmtpTransportis a structural SMTP transport: it accepts a caller-suppliedSmtpClientLikeadapter (the contract is…- smtp-transport —
SmtpTransportis a structural SMTP transport: it accepts a caller-suppliedSmtpClientLikeadapter (the contract is…
- smtp-transport —
Why universal
The upstream @suaveplan/emailer was server-only because it imported
nodemailer and googleapis directly. The rewrite drops both hard
dependencies in favour of injectable contracts:
- SMTP: callers supply a thin
SmtpClientLikeadapter so nodemailer (or any other library) lives in the consumer's app shell, not in this package. - Gmail: send is implemented directly against the Gmail REST API via
FetchLike, so token acquisition is the caller's concern and the transport runs unchanged in Bun, Node, and the browser.
Modules
src/core/email-service/email-service.md—EmailService<TTransport>orchestratorsrc/core/email-service/base-email-transport.md— abstract helpersrc/core/retry/retry.md— deterministic retry helpersrc/core/validate/validate.md— address-formatting helperssrc/transports/dummy/dummy-transport.md— in-memory test transportsrc/transports/smtp/smtp-transport.md— SMTP via adaptersrc/transports/smtp/smtp-client-like.types.md— adapter contractsrc/transports/gmail-rest/gmail-rest-transport.md— Gmail REST transportsrc/error-codes.md— error registrysrc/schemas/schemas.md— Zod boundary schemassrc/types/emailer.types.md— public typessrc/otel/metrics.md— OTel instrumentssrc/fakes/fake-smtp-client.md— deterministic SMTP fakesrc/fakes/fake-fetch.md— deterministic fetch fake
Quick start
import { EmailService } from "@suaveplan/emailer";
import { createDummyTransport } from "@suaveplan/emailer/transports/dummy";
const transport = createDummyTransport();
const service = new EmailService({
transport,
transportOptions: {},
defaultFrom: "no-reply@example.com",
});
await service.initialize();
const result = await service.send({
to: "user@example.com",
subject: "hello",
html: "<p>hi</p>",
});
License
MIT