@btravstack/mailer / index
index
Classes
Mailer
Defined in: packages/mailer/src/mailer.ts:61
The port an application depends on. send answers when the transport has ACCEPTED the message, which is not the same as delivered — a caller needing delivery semantics asks its provider's webhooks.
Retries are not here. A failed send is a modeled MailNotSent, and what to do about it belongs to the caller's transport, whose own retry budget owns redelivery rather than a policy this package invented.
Extends
PortInstance<"Mailer",MailerService>
Constructors
Constructor
new Mailer(): Mailer;Defined in: packages/di/dist/index.d.mts:16
Returns
Inherited from
Port("Mailer")<MailerService>.constructorProperties
| Property | Modifier | Type | Inherited from | Defined in |
|---|---|---|---|---|
[ID] | readonly | "Mailer" | Port("Mailer").[ID] | packages/di/dist/index.d.mts:12 |
[SERVICE] | readonly | MailerService | Port("Mailer").[SERVICE] | packages/di/dist/index.d.mts:13 |
portId | readonly | "Mailer" | Port("Mailer").portId | packages/di/dist/index.d.mts:17 |
MailerBackend
Defined in: packages/mailer/src/mailer.ts:70
The port every adapter provides, and the one an application never depends on.
di allows one provider per port per graph, so the instrumented composition cannot be a layer over the plain one: an adapter targets this, and mailer() turns it into Mailer. A spec overrides THIS port.
Extends
PortInstance<"MailerBackend",MailerService>
Constructors
Constructor
new MailerBackend(): MailerBackend;Defined in: packages/di/dist/index.d.mts:16
Returns
Inherited from
Port("MailerBackend")<MailerService>.constructorProperties
| Property | Modifier | Type | Inherited from | Defined in |
|---|---|---|---|---|
[ID] | readonly | "MailerBackend" | Port("MailerBackend").[ID] | packages/di/dist/index.d.mts:12 |
[SERVICE] | readonly | MailerService | Port("MailerBackend").[SERVICE] | packages/di/dist/index.d.mts:13 |
portId | readonly | "MailerBackend" | Port("MailerBackend").portId | packages/di/dist/index.d.mts:17 |
MailNotSent
Defined in: packages/mailer/src/mailer.ts:42
The send did not happen. It carries to and subject and never the body: a failure is logged, and a body is the one part of a message that is reliably somebody's personal data.
Extends
TaggedErrorInstance<"MailNotSent", {reason:string;subject:string;to: readonlystring[]; }>
Constructors
Constructor
new MailNotSent(args): MailNotSent;Defined in: node_modules/.pnpm/unthrown@5.8.0/node_modules/unthrown/dist/index.d.mts:2034
Parameters
| Parameter | Type |
|---|---|
args | object & object |
Returns
Inherited from
TaggedError("MailNotSent")<{
readonly to: readonly string[];
readonly subject: string;
readonly reason: string;
}>.constructorProperties
| Property | Modifier | Type | Inherited from | Defined in |
|---|---|---|---|---|
_tag | readonly | "MailNotSent" | TaggedError("MailNotSent")._tag | node_modules/.pnpm/unthrown@5.8.0/node_modules/unthrown/dist/index.d.mts:2011 |
cause? | public | unknown | TaggedError("MailNotSent").cause | node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.error.d.ts:24 |
message | public | string | TaggedError("MailNotSent").message | node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1075 |
name | public | string | TaggedError("MailNotSent").name | node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1074 |
reason | readonly | string | TaggedError("MailNotSent").reason | packages/mailer/src/mailer.ts:45 |
stack? | public | string | TaggedError("MailNotSent").stack | node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1076 |
subject | readonly | string | TaggedError("MailNotSent").subject | packages/mailer/src/mailer.ts:44 |
to | readonly | readonly string[] | TaggedError("MailNotSent").to | packages/mailer/src/mailer.ts:43 |
Type Aliases
Mail
type Mail = object;Defined in: packages/mailer/src/mailer.ts:23
One message: the envelope, a subject and a body. No templating — that is an application's decision with a library of its own — but everything an envelope carries, because an invoice mail with an attachment is roughly the second one a service sends, and a port that cannot carry it gets bypassed rather than extended, losing the recorder and the instrumentation with it.
Properties
| Property | Modifier | Type | Description | Defined in |
|---|---|---|---|---|
attachments? | readonly | readonly MailAttachment[] | - | packages/mailer/src/mailer.ts:32 |
bcc? | readonly | readonly string[] | - | packages/mailer/src/mailer.ts:27 |
cc? | readonly | readonly string[] | - | packages/mailer/src/mailer.ts:26 |
from | readonly | string | - | packages/mailer/src/mailer.ts:24 |
headers? | readonly | Readonly<Record<string, string>> | Extra headers, verbatim. What an adapter already sets it sets — this does not replace an envelope field. | packages/mailer/src/mailer.ts:34 |
html? | readonly | string | - | packages/mailer/src/mailer.ts:31 |
subject | readonly | string | - | packages/mailer/src/mailer.ts:28 |
text | readonly | string | The plain-text body. Required: a mail with only HTML is a mail some clients cannot read. | packages/mailer/src/mailer.ts:30 |
to | readonly | readonly string[] | - | packages/mailer/src/mailer.ts:25 |
MailAttachment
type MailAttachment = object;Defined in: packages/mailer/src/mailer.ts:9
One file on a message. content is bytes or a string and never a path or a stream: a port that took either would be asking every adapter to own a filesystem and a lifetime, where a caller that has a file can read it.
Properties
| Property | Modifier | Type | Description | Defined in |
|---|---|---|---|---|
content | readonly | Uint8Array | string | - | packages/mailer/src/mailer.ts:11 |
contentType? | readonly | string | Defaults to whatever the adapter infers from filename. | packages/mailer/src/mailer.ts:13 |
filename | readonly | string | - | packages/mailer/src/mailer.ts:10 |
MailerOptions
type MailerOptions<E, N> = object;Defined in: packages/mailer/src/module.ts:7
Type Parameters
| Type Parameter |
|---|
E |
N |
Properties
| Property | Modifier | Type | Description | Defined in |
|---|---|---|---|---|
adapter | readonly | Module<MailerBackend, E, N> | The adapter module: recordingMailer(recorder) from this entry point, smtpMailer() from @btravstack/mailer/smtp, or one an application wrote itself over MailerBackend. | packages/mailer/src/module.ts:13 |
MailerService
type MailerService = object;Defined in: packages/mailer/src/mailer.ts:48
Properties
| Property | Modifier | Type | Defined in |
|---|---|---|---|
send | readonly | (mail) => AsyncResult<void, MailNotSent> | packages/mailer/src/mailer.ts:49 |
MailRecorder
type MailRecorder = object;Defined in: packages/mailer/src/recording.ts:12
What a spec asserts against: the mails that would have been sent. record is the adapter's half and sent/only the test's, on one object, so nothing reaches into an array somebody else owns. only() throws unless there is exactly one — a test that read it too early is a broken test.
Properties
| Property | Modifier | Type | Defined in |
|---|---|---|---|
only | readonly | () => Mail | packages/mailer/src/recording.ts:15 |
record | readonly | (mail) => void | packages/mailer/src/recording.ts:13 |
sent | readonly | () => readonly Mail[] | packages/mailer/src/recording.ts:14 |
Functions
mailer()
function mailer<E, N>(__namedParameters): Module<Mailer, E, N>;Defined in: packages/mailer/src/module.ts:33
The mailer starter: an adapter, and Mailer provided from it.
mailer({ adapter: smtpMailer() });Two ports, because di allows one provider per port per graph: the port an application depends on must not be the one an adapter provides, which is what lets this function be the seam.
There is no instrumented flag. Every send is handed to whatever contributed to Observers; a graph that composed no observability has only this module's own no-op member, so it costs one call per send and nothing else. What rides the signals is the ENVELOPE — a recipient COUNT rather than the addresses, since a recipient list is personal data.
Type Parameters
| Type Parameter |
|---|
E |
N |
Parameters
| Parameter | Type |
|---|---|
__namedParameters | MailerOptions<E, N> |
Returns
Module<Mailer, E, N>
mailRecorder()
function mailRecorder(): MailRecorder;Defined in: packages/mailer/src/recording.ts:18
Returns
recordingMailer()
function recordingMailer(recorder): Module<MailerBackend, never, never>;Defined in: packages/mailer/src/recording.ts:57
The adapter as a module, which is the shape mailer({ adapter }) takes.
Parameters
| Parameter | Type |
|---|---|
recorder | MailRecorder |
Returns
Module<MailerBackend, never, never>
recordingMailerBackend()
function recordingMailerBackend(recorder): MailerService;Defined in: packages/mailer/src/recording.ts:45
The testable-by-default adapter: it sends nothing and keeps everything, since the question in a suite is almost never "did SMTP work" but "would this code have sent the right message".
It cannot fail. A spec that needs the failure arm composes an adapter of its own; a configurable failure mode would put a policy in a fixture whose whole value is having none.
Parameters
| Parameter | Type |
|---|---|
recorder | MailRecorder |
Returns
recordingMailerProvider()
function recordingMailerProvider(recorder): Provider<MailerBackend, never, never> & object;Defined in: packages/mailer/src/recording.ts:53
The adapter as a provider, which is the shape @btravstack/testing's overridden takes.
Parameters
| Parameter | Type |
|---|---|
recorder | MailRecorder |
Returns
Provider<MailerBackend, never, never> & object