@molecule/api-emails-mailgun 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.
Files changed (2) hide show
  1. package/README.md +277 -0
  2. package/package.json +7 -6
package/README.md ADDED
@@ -0,0 +1,277 @@
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.181Z
7
+ -->
8
+
9
+ # @molecule/api-emails-mailgun
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
+ Mailgun email provider for molecule.dev.
16
+
17
+ ## Quick Start
18
+
19
+ ```typescript
20
+ import { setTransport } from '@molecule/api-emails'
21
+ import { provider } from '@molecule/api-emails-mailgun'
22
+
23
+ setTransport(provider)
24
+ ```
25
+
26
+ ## Type
27
+
28
+ `provider`
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ npm install @molecule/api-emails-mailgun @molecule/api-bond @molecule/api-emails @molecule/api-secrets nodemailer nodemailer-mailgun-transport
34
+ npm install -D @types/nodemailer @types/nodemailer-mailgun-transport
35
+ ```
36
+
37
+ ## API
38
+
39
+ ### Interfaces
40
+
41
+ #### `EmailMessage`
42
+
43
+ Email message options.
44
+
45
+ ```typescript
46
+ interface EmailMessage {
47
+ /**
48
+ * Sender address.
49
+ */
50
+ from: string | EmailAddress
51
+ /**
52
+ * Recipient(s).
53
+ */
54
+ to: string | EmailAddress | (string | EmailAddress)[]
55
+ /**
56
+ * CC recipient(s).
57
+ */
58
+ cc?: string | EmailAddress | (string | EmailAddress)[]
59
+ /**
60
+ * BCC recipient(s).
61
+ */
62
+ bcc?: string | EmailAddress | (string | EmailAddress)[]
63
+ /**
64
+ * Reply-to address.
65
+ */
66
+ replyTo?: string | EmailAddress
67
+ /**
68
+ * Email subject.
69
+ */
70
+ subject: string
71
+ /**
72
+ * Plain text body.
73
+ */
74
+ text?: string
75
+ /**
76
+ * HTML body.
77
+ */
78
+ html?: string
79
+ /**
80
+ * File attachments.
81
+ */
82
+ attachments?: EmailAttachment[]
83
+ /**
84
+ * i18n key for the subject (for client-side translation).
85
+ */
86
+ subjectKey?: string
87
+ /**
88
+ * i18n key for the plain text body (for client-side translation).
89
+ */
90
+ textKey?: string
91
+ /**
92
+ * i18n key for the HTML body (for client-side translation).
93
+ */
94
+ htmlKey?: string
95
+ }
96
+ ```
97
+
98
+ #### `EmailSendResult`
99
+
100
+ Result of sending an email.
101
+
102
+ ```typescript
103
+ interface EmailSendResult {
104
+ /**
105
+ * Whether the email was accepted for delivery.
106
+ */
107
+ accepted: string[]
108
+ /**
109
+ * Addresses that were rejected.
110
+ */
111
+ rejected: string[]
112
+ /**
113
+ * Message ID from the provider.
114
+ */
115
+ messageId?: string
116
+ /**
117
+ * Raw response from the provider.
118
+ */
119
+ response?: string
120
+ }
121
+ ```
122
+
123
+ #### `EmailTransport`
124
+
125
+ Email transport interface.
126
+
127
+ All email providers must implement this interface.
128
+
129
+ ```typescript
130
+ interface EmailTransport {
131
+ /**
132
+ * Sends an email message.
133
+ * @returns The send result.
134
+ */
135
+ sendMail(message: EmailMessage): Promise<EmailSendResult>
136
+ }
137
+ ```
138
+
139
+ ### Functions
140
+
141
+ #### `sendMail(message)`
142
+
143
+ Sends an email message via the Mailgun API, with automatic test-mode handling for sandbox domains.
144
+
145
+ ```typescript
146
+ function sendMail(message: EmailMessage): Promise<EmailSendResult>
147
+ ```
148
+
149
+ - `message` — The email message (to, from, subject, text/html, attachments).
150
+
151
+ **Returns:** Send result with accepted/rejected addresses and message ID.
152
+
153
+ ### Constants
154
+
155
+ #### `email` _(deprecated)_
156
+
157
+ Raw nodemailer transport alias.
158
+
159
+ ```typescript
160
+ const email: { sendMail: (msg: nodemailer.SendMailOptions) => Promise<any> }
161
+ ```
162
+
163
+ #### `mailgunSecretDefinitions`
164
+
165
+ Secret definitions required by the Mailgun email bond.
166
+
167
+ ```typescript
168
+ const mailgunSecretDefinitions: SecretDefinition[]
169
+ ```
170
+
171
+ #### `provider`
172
+
173
+ The Mailgun email provider implementing the `EmailTransport` interface.
174
+
175
+ ```typescript
176
+ const provider: EmailTransport
177
+ ```
178
+
179
+ #### `transport` _(deprecated)_
180
+
181
+ Raw nodemailer transport for direct access.
182
+
183
+ ```typescript
184
+ const transport: { sendMail: (msg: nodemailer.SendMailOptions) => Promise<any> }
185
+ ```
186
+
187
+ ## Core Interface
188
+
189
+ Implements `@molecule/api-emails` interface.
190
+
191
+ ## Bond Wiring
192
+
193
+ Setup function to register this provider with the core interface:
194
+
195
+ ```typescript
196
+ import { setTransport } from '@molecule/api-emails'
197
+ import { provider } from '@molecule/api-emails-mailgun'
198
+
199
+ export function setupEmailsMailgun(): void {
200
+ setTransport(provider)
201
+ }
202
+ ```
203
+
204
+ ## Injection Notes
205
+
206
+ ### Requirements
207
+
208
+ Peer dependencies:
209
+
210
+ - `@molecule/api-bond` ^1.0.1
211
+ - `@molecule/api-emails` ^1.0.1
212
+ - `@molecule/api-secrets` ^1.0.1
213
+
214
+ ### Environment Variables
215
+
216
+ - `MAILGUN_API_KEY` _(required)_ — Mailgun API key
217
+ - Setup: Mailgun dashboard → Settings → API Security → create/copy a sending API key.
218
+ - Get it here: [https://app.mailgun.com/settings/api_security](https://app.mailgun.com/settings/api_security)
219
+ - `MAILGUN_DOMAIN` _(required)_ — Mailgun sending domain
220
+ - Setup: Add and verify a sending domain in Mailgun (sandbox domains work for testing to authorized recipients).
221
+ - Get it here: [https://app.mailgun.com/mg/sending/domains](https://app.mailgun.com/mg/sending/domains)
222
+ - Example: `mg.example.com`
223
+ - `MAILGUN_API_HOST` _(optional)_ — Mailgun API host
224
+ - Setup: Only needed for EU-region Mailgun accounts (api.eu.mailgun.net) or a self-hosted/broker endpoint; leave unset for US-region accounts.
225
+ - Example: `api.eu.mailgun.net`
226
+
227
+ ### Runtime Dependencies
228
+
229
+ - `@molecule/api-bond`
230
+ - `@molecule/api-emails`
231
+ - `@molecule/api-secrets`
232
+ - `nodemailer`
233
+ - `nodemailer-mailgun-transport`
234
+
235
+ - **EU-region Mailgun accounts must set `MAILGUN_API_HOST=api.eu.mailgun.net`**
236
+ (optional env; defaults to Mailgun's US endpoint). Without it every send
237
+ fails upstream with 401 even though the key is valid — wrong region, not
238
+ wrong key.
239
+ - **Sandbox domains auto-enable Mailgun test mode**: when `MAILGUN_DOMAIN`
240
+ matches `sandbox*.mailgun.org` (or `MAILGUN_TEST_MODE=true`), sends carry
241
+ `o:testmode=yes` — Mailgun accepts, validates, and assigns a message id
242
+ but NEVER delivers. A sandbox 403 for an unauthorized recipient is
243
+ reported as a synthetic success (`response: 'sandbox-test-mode'`). "Send
244
+ succeeded but no email arrived" in dev is this behavior, not a bug.
245
+ - Credentials are read lazily on first send and fail fast with a tagged
246
+ `config.notConfigured` error naming the missing key (`MAILGUN_API_KEY`,
247
+ then `MAILGUN_DOMAIN`). On success `accepted` echoes the message's own
248
+ recipients (Mailgun's transport returns no per-recipient verdict).
249
+ - **The `from` address's domain must equal `MAILGUN_DOMAIN`** (Mailgun sends
250
+ through, and signs SPF/DKIM for, that verified domain). A `from` on any other
251
+ domain — a hardcoded `noreply@example.com`, `noreply@store.com`, etc. — is
252
+ rejected or unsigned (spam). Default the sender to the sending domain:
253
+ `` process.env.EMAIL_FROM ?? `no-reply@${process.env.MAILGUN_DOMAIN}` `` — never
254
+ a literal placeholder domain.
255
+
256
+ ## E2E Tests
257
+
258
+ Integration checklist — drive the real UI (live preview, no mocks). The
259
+ sandbox CAPTURES outbound email instead of sending — read each message with
260
+ the `read_activity` tool (filter type 'email'); the verification/reset link
261
+ is in its payload. Never mock the send or modify production code to expose
262
+ it. Adapt each item to this app's actual screens/flows, and check every box
263
+ off one by one. A box you can't check is an integration bug to fix — not a
264
+ skip:
265
+
266
+ - [ ] Each email-triggering flow (signup verification, password-reset request,
267
+ invites/notifications the app defines) confirms the send in the UI ("check
268
+ your inbox") and a message actually reaches the transport.
269
+ - [ ] The password-reset round-trip completes: request a reset → open the
270
+ captured message → follow its single-use link → set a new password → log
271
+ in with it (and the old password no longer works).
272
+ - [ ] The message body contains a LINK, never the raw token/secret, and renders
273
+ with the app's real name/content (no `undefined` placeholders).
274
+ - [ ] Requesting a reset for an unknown email shows the same neutral UI response
275
+ as a known one (no account-existence oracle).
276
+ - [ ] Account emails go only to the account's own address — no UI or endpoint
277
+ 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-mailgun",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Mailgun 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",
@@ -31,7 +32,7 @@
31
32
  "nodemailer-mailgun-transport": "2.1.5"
32
33
  },
33
34
  "devDependencies": {
34
- "@molecule/api-bond": "1.0.0",
35
+ "@molecule/api-bond": "1.0.1",
35
36
  "@types/node": "26.1.2",
36
37
  "@types/nodemailer": "8.0.1",
37
38
  "@types/nodemailer-mailgun-transport": "1.4.6",
@@ -39,9 +40,9 @@
39
40
  "vitest": "4.1.10"
40
41
  },
41
42
  "peerDependencies": {
42
- "@molecule/api-bond": "^1.0.0",
43
- "@molecule/api-emails": "^1.0.0",
44
- "@molecule/api-secrets": "^1.0.0"
43
+ "@molecule/api-bond": "^1.0.1",
44
+ "@molecule/api-emails": "^1.0.1",
45
+ "@molecule/api-secrets": "^1.0.1"
45
46
  },
46
47
  "repository": {
47
48
  "type": "git",