@molecule/api-emails-sendmail 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +266 -0
- package/package.json +6 -5
package/README.md
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AUTO-GENERATED — DO NOT EDIT THIS FILE.
|
|
3
|
+
Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
|
|
4
|
+
Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
|
|
5
|
+
To change this document, edit the module-level JSDoc in src/index.ts.
|
|
6
|
+
Generated: 2026-08-04T01:48:04.838Z
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
# @molecule/api-emails-sendmail
|
|
10
|
+
|
|
11
|
+
> **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
|
|
12
|
+
> It is written to be read by coding agents as much as by people, and is generated from this
|
|
13
|
+
> package's source — edit `src/index.ts` JSDoc, not this file.
|
|
14
|
+
|
|
15
|
+
Sendmail email provider for molecule.dev.
|
|
16
|
+
|
|
17
|
+
Uses the local sendmail command to send emails.
|
|
18
|
+
|
|
19
|
+
Note: For this to work, your server must have `sendmail` installed and
|
|
20
|
+
configured. The binary path defaults to `/usr/sbin/sendmail`; set the
|
|
21
|
+
`SENDMAIL_PATH` environment variable to use a different binary (e.g.
|
|
22
|
+
`/usr/lib/sendmail`, or an msmtp/mhsendmail shim in containers). The path
|
|
23
|
+
is read once at module load.
|
|
24
|
+
|
|
25
|
+
## Type
|
|
26
|
+
|
|
27
|
+
`provider`
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @molecule/api-emails-sendmail @molecule/api-bond @molecule/api-emails nodemailer
|
|
33
|
+
npm install -D @types/nodemailer
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## API
|
|
37
|
+
|
|
38
|
+
### Interfaces
|
|
39
|
+
|
|
40
|
+
#### `EmailMessage`
|
|
41
|
+
|
|
42
|
+
Email message options.
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
interface EmailMessage {
|
|
46
|
+
/**
|
|
47
|
+
* Sender address.
|
|
48
|
+
*/
|
|
49
|
+
from: string | EmailAddress
|
|
50
|
+
/**
|
|
51
|
+
* Recipient(s).
|
|
52
|
+
*/
|
|
53
|
+
to: string | EmailAddress | (string | EmailAddress)[]
|
|
54
|
+
/**
|
|
55
|
+
* CC recipient(s).
|
|
56
|
+
*/
|
|
57
|
+
cc?: string | EmailAddress | (string | EmailAddress)[]
|
|
58
|
+
/**
|
|
59
|
+
* BCC recipient(s).
|
|
60
|
+
*/
|
|
61
|
+
bcc?: string | EmailAddress | (string | EmailAddress)[]
|
|
62
|
+
/**
|
|
63
|
+
* Reply-to address.
|
|
64
|
+
*/
|
|
65
|
+
replyTo?: string | EmailAddress
|
|
66
|
+
/**
|
|
67
|
+
* Email subject.
|
|
68
|
+
*/
|
|
69
|
+
subject: string
|
|
70
|
+
/**
|
|
71
|
+
* Plain text body.
|
|
72
|
+
*/
|
|
73
|
+
text?: string
|
|
74
|
+
/**
|
|
75
|
+
* HTML body.
|
|
76
|
+
*/
|
|
77
|
+
html?: string
|
|
78
|
+
/**
|
|
79
|
+
* File attachments.
|
|
80
|
+
*/
|
|
81
|
+
attachments?: EmailAttachment[]
|
|
82
|
+
/**
|
|
83
|
+
* i18n key for the subject (for client-side translation).
|
|
84
|
+
*/
|
|
85
|
+
subjectKey?: string
|
|
86
|
+
/**
|
|
87
|
+
* i18n key for the plain text body (for client-side translation).
|
|
88
|
+
*/
|
|
89
|
+
textKey?: string
|
|
90
|
+
/**
|
|
91
|
+
* i18n key for the HTML body (for client-side translation).
|
|
92
|
+
*/
|
|
93
|
+
htmlKey?: string
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### `EmailSendResult`
|
|
98
|
+
|
|
99
|
+
Result of sending an email.
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
interface EmailSendResult {
|
|
103
|
+
/**
|
|
104
|
+
* Whether the email was accepted for delivery.
|
|
105
|
+
*/
|
|
106
|
+
accepted: string[]
|
|
107
|
+
/**
|
|
108
|
+
* Addresses that were rejected.
|
|
109
|
+
*/
|
|
110
|
+
rejected: string[]
|
|
111
|
+
/**
|
|
112
|
+
* Message ID from the provider.
|
|
113
|
+
*/
|
|
114
|
+
messageId?: string
|
|
115
|
+
/**
|
|
116
|
+
* Raw response from the provider.
|
|
117
|
+
*/
|
|
118
|
+
response?: string
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### `EmailTransport`
|
|
123
|
+
|
|
124
|
+
Email transport interface.
|
|
125
|
+
|
|
126
|
+
All email providers must implement this interface.
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
interface EmailTransport {
|
|
130
|
+
/**
|
|
131
|
+
* Sends an email message.
|
|
132
|
+
* @returns The send result.
|
|
133
|
+
*/
|
|
134
|
+
sendMail(message: EmailMessage): Promise<EmailSendResult>
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Functions
|
|
139
|
+
|
|
140
|
+
#### `sendMail(message)`
|
|
141
|
+
|
|
142
|
+
Sends an email using the local sendmail binary via nodemailer.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
function sendMail(message: EmailMessage): Promise<EmailSendResult>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
- `message` — The email message (to, from, subject, text/html, attachments).
|
|
149
|
+
|
|
150
|
+
**Returns:** Send result with accepted/rejected addresses and message ID.
|
|
151
|
+
|
|
152
|
+
### Constants
|
|
153
|
+
|
|
154
|
+
#### `email` _(deprecated)_
|
|
155
|
+
|
|
156
|
+
Legacy export - the raw nodemailer transport.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const email: nodemailer.Transporter<SentMessageInfo, Options>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### `nodemailerTransport`
|
|
163
|
+
|
|
164
|
+
The underlying nodemailer transport.
|
|
165
|
+
|
|
166
|
+
The sendmail binary path defaults to `/usr/sbin/sendmail` and can be
|
|
167
|
+
overridden with the `SENDMAIL_PATH` environment variable (e.g.
|
|
168
|
+
`/usr/lib/sendmail`, or an msmtp/mhsendmail shim in containers and tests).
|
|
169
|
+
The path is read once at module load; if the binary is missing, sends fail
|
|
170
|
+
with a `spawn ... ENOENT` error at send time — install sendmail or point
|
|
171
|
+
`SENDMAIL_PATH` at a compatible binary.
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
const nodemailerTransport: nodemailer.Transporter<SentMessageInfo, Options>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### `provider`
|
|
178
|
+
|
|
179
|
+
The sendmail email provider implementing the standard interface.
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
const provider: EmailTransport
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### `transport` _(deprecated)_
|
|
186
|
+
|
|
187
|
+
Legacy export - the raw nodemailer transport.
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
const transport: nodemailer.Transporter<SentMessageInfo, Options>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Core Interface
|
|
194
|
+
|
|
195
|
+
Implements `@molecule/api-emails` interface.
|
|
196
|
+
|
|
197
|
+
## Bond Wiring
|
|
198
|
+
|
|
199
|
+
Setup function to register this provider with the core interface:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import { setTransport } from '@molecule/api-emails'
|
|
203
|
+
import { provider } from '@molecule/api-emails-sendmail'
|
|
204
|
+
|
|
205
|
+
export function setupEmailsSendmail(): void {
|
|
206
|
+
setTransport(provider)
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Injection Notes
|
|
211
|
+
|
|
212
|
+
### Requirements
|
|
213
|
+
|
|
214
|
+
Peer dependencies:
|
|
215
|
+
|
|
216
|
+
- `@molecule/api-bond` ^1.0.1
|
|
217
|
+
- `@molecule/api-emails` ^1.0.1
|
|
218
|
+
|
|
219
|
+
### Runtime Dependencies
|
|
220
|
+
|
|
221
|
+
- `@molecule/api-bond`
|
|
222
|
+
- `@molecule/api-emails`
|
|
223
|
+
- `nodemailer`
|
|
224
|
+
|
|
225
|
+
On success, `sendMail()` resolves with `accepted` set to the envelope
|
|
226
|
+
recipients (sendmail queues the message for all of them once the binary
|
|
227
|
+
exits 0) and `response: 'Messages queued for delivery'`. Failures reject
|
|
228
|
+
with distinct errors: a missing binary is a `spawn ... ENOENT` error
|
|
229
|
+
(install sendmail or set `SENDMAIL_PATH`), a binary that exits non-zero is
|
|
230
|
+
`Sendmail exited with code <n>`, and an envelope address starting with `-`
|
|
231
|
+
is rejected up front with `Invalid envelope addresses.` (argument-injection
|
|
232
|
+
guard) — inspect the message/`code` to tell configuration problems apart
|
|
233
|
+
from delivery problems.
|
|
234
|
+
|
|
235
|
+
The `accepted`-from-envelope mapping above exists because `@types/nodemailer`
|
|
236
|
+
declares `accepted`/`rejected`/`pending` on `SendmailTransport.SentMessageInfo`
|
|
237
|
+
(and `SESTransport.SentMessageInfo`), but nodemailer's actual sendmail (and
|
|
238
|
+
SES) transports never set them — only the SMTP transports do. Code written
|
|
239
|
+
against the typings type-checks cleanly and reads `undefined`/`[]` at
|
|
240
|
+
runtime. If you upgrade `nodemailer` or `@types/nodemailer`, re-verify this
|
|
241
|
+
against the transport implementations themselves
|
|
242
|
+
(`lib/sendmail-transport/index.js`), not the shipped `.d.ts` — the typings
|
|
243
|
+
are exactly what drifted last time.
|
|
244
|
+
|
|
245
|
+
## E2E Tests
|
|
246
|
+
|
|
247
|
+
Integration checklist — drive the real UI (live preview, no mocks). The
|
|
248
|
+
sandbox CAPTURES outbound email instead of sending — read each message with
|
|
249
|
+
the `read_activity` tool (filter type 'email'); the verification/reset link
|
|
250
|
+
is in its payload. Never mock the send or modify production code to expose
|
|
251
|
+
it. Adapt each item to this app's actual screens/flows, and check every box
|
|
252
|
+
off one by one. A box you can't check is an integration bug to fix — not a
|
|
253
|
+
skip:
|
|
254
|
+
|
|
255
|
+
- [ ] Each email-triggering flow (signup verification, password-reset request,
|
|
256
|
+
invites/notifications the app defines) confirms the send in the UI ("check
|
|
257
|
+
your inbox") and a message actually reaches the transport.
|
|
258
|
+
- [ ] The password-reset round-trip completes: request a reset → open the
|
|
259
|
+
captured message → follow its single-use link → set a new password → log
|
|
260
|
+
in with it (and the old password no longer works).
|
|
261
|
+
- [ ] The message body contains a LINK, never the raw token/secret, and renders
|
|
262
|
+
with the app's real name/content (no `undefined` placeholders).
|
|
263
|
+
- [ ] Requesting a reset for an unknown email shows the same neutral UI response
|
|
264
|
+
as a known one (no account-existence oracle).
|
|
265
|
+
- [ ] Account emails go only to the account's own address — no UI or endpoint
|
|
266
|
+
lets an unauthenticated caller send to an arbitrary address.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@molecule/api-emails-sendmail",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Sendmail email provider for molecule.dev.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
|
-
"dist"
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md"
|
|
21
22
|
],
|
|
22
23
|
"keywords": [
|
|
23
24
|
"molecule",
|
|
@@ -30,15 +31,15 @@
|
|
|
30
31
|
"nodemailer": "9.0.3"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
|
-
"@molecule/api-bond": "1.0.
|
|
34
|
+
"@molecule/api-bond": "1.0.1",
|
|
34
35
|
"@types/node": "26.1.2",
|
|
35
36
|
"@types/nodemailer": "8.0.1",
|
|
36
37
|
"typescript": "6.0.3",
|
|
37
38
|
"vitest": "4.1.10"
|
|
38
39
|
},
|
|
39
40
|
"peerDependencies": {
|
|
40
|
-
"@molecule/api-bond": "^1.0.
|
|
41
|
-
"@molecule/api-emails": "^1.0.
|
|
41
|
+
"@molecule/api-bond": "^1.0.1",
|
|
42
|
+
"@molecule/api-emails": "^1.0.1"
|
|
42
43
|
},
|
|
43
44
|
"repository": {
|
|
44
45
|
"type": "git",
|