Mailer backed by a pluggable provider and a template engine.
send and sendTemplate both funnel through the single MailService.send
path, so the default-from resolution and provider dispatch live in one place.
templates: TemplateEngine
The template engine (built from plugin options)
options: MailServiceOptions
Default sender
isHealthy(): Promise<boolean | undefined>
Reports whether the backing provider's transport is reachable right now.
Delegates to the provider's own optional probe, which is the single
implementation of this question in the package — MailPlugin's health
indicator reads it through here rather than reaching past the service, so
the capability and the indicator cannot disagree about what reachable
means (the one-capability-one-implementation rule).
The cache lives HERE, at the capability boundary, rather than in each
caller. Two independent callers ask this question on one aggregate health
check — the mail indicator, and notification-plugin's email channel
once per configured alias — so a cache per caller would still hit the
transport once per caller, which is what the caching exists to prevent.
Cached here they coalesce into one call however many aliases are
configured. The probe is bounded too, so a transport that stops answering
cannot hold the health endpoint open; a probe that exceeds the bound or
rejects resolves false — it was reached for and did not answer, which is
a different fact from undefined, where it could not be asked.
send(message: MailMessage): Promise<void>
Sends an email, resolving from from the message or the configured default.
sendTemplate(template: string,message: Omit<MailMessage, "html" | "text">,data: Readonly<Record<string, unknown>>): Promise<void>
Renders a named template and sends the result. The subject is taken
verbatim from message; the template supplies the html/text bodies.