@beignet/provider-mail-smtp 0.0.30 → 0.0.32
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/CHANGELOG.md +48 -0
- package/README.md +39 -5
- package/dist/index.d.ts +46 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +71 -51
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +119 -61
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# @beignet/provider-mail-smtp
|
|
2
2
|
|
|
3
|
+
## 0.0.32
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f759611: Provider factory options now resolve through one framework-owned rule:
|
|
8
|
+
`ProviderConfigDef` gains an `overrides` map that merges defined values over
|
|
9
|
+
env-derived input before validation, and every first-party provider factory
|
|
10
|
+
uses it instead of hand-rolled schema defaults and setup-time fallbacks. The
|
|
11
|
+
precedence is now uniform — defined factory options win over environment
|
|
12
|
+
variables in all thirteen providers. This flips the previous env-wins
|
|
13
|
+
behavior of `provider-cache-redis`, `provider-locks-redis`, and the storage,
|
|
14
|
+
search, and blob providers; set the env var (or drop the option) if you
|
|
15
|
+
relied on env winning. `installProviderForTest(...)` gains an `env` option
|
|
16
|
+
that resolves config through the same loader, and unreachable missing-config
|
|
17
|
+
guards in the pino and inngest factories are gone.
|
|
18
|
+
- eb680ef: Provider naming now follows one implementation-first order matching the
|
|
19
|
+
factories. `@beignet/provider-redis` is renamed `@beignet/provider-cache-redis`
|
|
20
|
+
(the package fills the cache port; the raw client stays as the
|
|
21
|
+
`ctx.ports.redis` escape hatch) with `redisCacheProvider`,
|
|
22
|
+
`createRedisCacheProvider`, and provider name `cache-redis` matching
|
|
23
|
+
`locks-redis`. Default consts flip to implementation-first:
|
|
24
|
+
`loggerPinoProvider` is now `pinoLoggerProvider`, `mailResendProvider` is now
|
|
25
|
+
`resendMailProvider`, and `mailSmtpProvider` is now `smtpMailProvider`.
|
|
26
|
+
CLI starter templates and the `redis-cache` provider preset wire the new
|
|
27
|
+
names. No aliases are kept.
|
|
28
|
+
- 671b986: Every provider package now exposes a `createXProvider(options)` factory, so
|
|
29
|
+
env-only configuration is a convenience rather than the only path. New
|
|
30
|
+
factories: `createPinoLoggerProvider`, `createResendMailProvider`,
|
|
31
|
+
`createSmtpMailProvider`, `createStripePaymentsProvider`,
|
|
32
|
+
`createUpstashRateLimitProvider`, and `createInngestJobsProvider`. Options
|
|
33
|
+
mirror each provider's env config fields and override env-derived values; the
|
|
34
|
+
default consts keep their behavior and now simply call their factory. (The
|
|
35
|
+
consts themselves are renamed to implementation-first order —
|
|
36
|
+
`pinoLoggerProvider`, `resendMailProvider`, `smtpMailProvider` — in this
|
|
37
|
+
release's provider naming normalization; `stripePaymentsProvider`,
|
|
38
|
+
`upstashRateLimitProvider`, and `inngestJobsProvider` already followed it.)
|
|
39
|
+
- f759611: Provider resilience baseline. The S3 storage provider now surfaces the AWS
|
|
40
|
+
SDK's built-in retry configuration: pass `maxAttempts` and `retryMode`
|
|
41
|
+
(`"standard"` or `"adaptive"`) as provider options or set
|
|
42
|
+
`STORAGE_S3_MAX_ATTEMPTS` and `STORAGE_S3_RETRY_MODE` env vars; the SDK
|
|
43
|
+
default stays `standard` mode with 3 attempts. Mail providers deliberately do
|
|
44
|
+
not retry — mail delivery is not idempotent — and now throw `MailDeliveryError`
|
|
45
|
+
messages that include the provider name and recipient count while preserving
|
|
46
|
+
the original vendor error as `cause`; dispatch mail from jobs or the outbox
|
|
47
|
+
when retries are needed.
|
|
48
|
+
|
|
49
|
+
## 0.0.31
|
|
50
|
+
|
|
3
51
|
## 0.0.30
|
|
4
52
|
|
|
5
53
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -19,12 +19,12 @@ bun add @beignet/provider-mail-smtp @beignet/core nodemailer
|
|
|
19
19
|
## Setup
|
|
20
20
|
|
|
21
21
|
```typescript
|
|
22
|
-
import {
|
|
22
|
+
import { smtpMailProvider } from "@beignet/provider-mail-smtp";
|
|
23
23
|
import { createServer } from "@beignet/core/server";
|
|
24
24
|
|
|
25
25
|
const server = await createServer({
|
|
26
26
|
ports: basePorts,
|
|
27
|
-
providers: [
|
|
27
|
+
providers: [smtpMailProvider],
|
|
28
28
|
context: ({ ports }) => ({ ports }),
|
|
29
29
|
routes,
|
|
30
30
|
});
|
|
@@ -44,6 +44,26 @@ Required environment variables:
|
|
|
44
44
|
registered in `server/providers.ts` and that all required `MAIL_*` env vars are
|
|
45
45
|
present in app env examples or config.
|
|
46
46
|
|
|
47
|
+
Use `createSmtpMailProvider(...)` when you want to pass config directly
|
|
48
|
+
instead of reading `MAIL_*` env vars. Options override env-derived values:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { createSmtpMailProvider } from "@beignet/provider-mail-smtp";
|
|
52
|
+
|
|
53
|
+
export const providers = [
|
|
54
|
+
createSmtpMailProvider({
|
|
55
|
+
host: "smtp.example.com",
|
|
56
|
+
port: 465,
|
|
57
|
+
user: secrets.smtpUser,
|
|
58
|
+
pass: secrets.smtpPass,
|
|
59
|
+
from: "My App <no-reply@example.com>",
|
|
60
|
+
}),
|
|
61
|
+
];
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`smtpMailProvider` is the default env-backed provider, equivalent to
|
|
65
|
+
`createSmtpMailProvider()`.
|
|
66
|
+
|
|
47
67
|
## Use in application code
|
|
48
68
|
|
|
49
69
|
```typescript
|
|
@@ -99,10 +119,24 @@ await ctx.ports.smtp.transporter.sendMail({
|
|
|
99
119
|
When `ctx.ports.devtools` is installed, this provider records `mail.send`,
|
|
100
120
|
`mail.sent`, and `mail.failed` events under the `mail` watcher.
|
|
101
121
|
|
|
102
|
-
##
|
|
122
|
+
## Failure and retry semantics
|
|
123
|
+
|
|
124
|
+
The provider fails fast and does not retry. Mail delivery is not idempotent: a
|
|
125
|
+
timed-out send may still have been delivered, so a blind provider retry risks
|
|
126
|
+
duplicate emails.
|
|
127
|
+
|
|
128
|
+
Delivery failures throw `MailDeliveryError` from `@beignet/core/mail` with the
|
|
129
|
+
provider name, the recipient count, and the original SMTP error preserved as
|
|
130
|
+
`cause`. Message bodies and credentials are never included.
|
|
131
|
+
|
|
132
|
+
The provider calls `transporter.verify()` during setup, so an unreachable SMTP
|
|
133
|
+
server or rejected credentials fail server startup immediately instead of
|
|
134
|
+
failing the first send. Startup configuration problems also throw during
|
|
135
|
+
provider setup.
|
|
103
136
|
|
|
104
|
-
|
|
105
|
-
|
|
137
|
+
When mail needs retries, dispatch it from a job or an outbox-backed listener
|
|
138
|
+
and own idempotency there, for example by recording one delivery per business
|
|
139
|
+
event before sending.
|
|
106
140
|
|
|
107
141
|
## Local and tests
|
|
108
142
|
|
package/dist/index.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export interface SmtpMailEscapeHatch {
|
|
|
28
28
|
transporter: Transporter;
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
|
-
* Ports contributed by `
|
|
31
|
+
* Ports contributed by `smtpMailProvider`.
|
|
32
32
|
*/
|
|
33
33
|
export interface SmtpMailProviderPorts {
|
|
34
34
|
/**
|
|
@@ -60,18 +60,60 @@ export interface CreateSmtpMailerOptions {
|
|
|
60
60
|
/**
|
|
61
61
|
* Create a Beignet mailer port backed by Nodemailer SMTP.
|
|
62
62
|
*
|
|
63
|
-
* Use this directly for custom wiring, or install `
|
|
63
|
+
* Use this directly for custom wiring, or install `smtpMailProvider` to load
|
|
64
64
|
* config from env and contribute ports during server startup.
|
|
65
65
|
*/
|
|
66
66
|
export declare function createSmtpMailer({ transporter, from, instrumentation: instrumentationTarget, }: CreateSmtpMailerOptions): MailerPort;
|
|
67
67
|
/**
|
|
68
|
-
*
|
|
68
|
+
* Options for the SMTP mail provider factory.
|
|
69
|
+
*/
|
|
70
|
+
export interface CreateSmtpMailProviderOptions {
|
|
71
|
+
/**
|
|
72
|
+
* SMTP server hostname. Overrides `MAIL_HOST`.
|
|
73
|
+
*/
|
|
74
|
+
host?: string;
|
|
75
|
+
/**
|
|
76
|
+
* SMTP server port. Port 465 uses SSL. Overrides `MAIL_PORT`.
|
|
77
|
+
*/
|
|
78
|
+
port?: number;
|
|
79
|
+
/**
|
|
80
|
+
* SMTP username. Overrides `MAIL_USER`.
|
|
81
|
+
*/
|
|
82
|
+
user?: string;
|
|
83
|
+
/**
|
|
84
|
+
* SMTP password. Overrides `MAIL_PASS`.
|
|
85
|
+
*/
|
|
86
|
+
pass?: string;
|
|
87
|
+
/**
|
|
88
|
+
* Default sender address. Overrides `MAIL_FROM`.
|
|
89
|
+
*/
|
|
90
|
+
from?: string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Create an env-backed SMTP mail provider.
|
|
94
|
+
*
|
|
95
|
+
* Options override the corresponding `MAIL_*` env values.
|
|
96
|
+
*/
|
|
97
|
+
export declare function createSmtpMailProvider(options?: CreateSmtpMailProviderOptions): import("@beignet/core/providers").ServiceProvider<unknown, z.ZodObject<{
|
|
98
|
+
HOST: z.ZodString;
|
|
99
|
+
PORT: z.ZodCoercedNumber<unknown>;
|
|
100
|
+
USER: z.ZodString;
|
|
101
|
+
PASS: z.ZodString;
|
|
102
|
+
FROM: z.ZodString;
|
|
103
|
+
}, z.core.$strip>, {
|
|
104
|
+
mailer: MailerPort;
|
|
105
|
+
smtp: {
|
|
106
|
+
transporter: Transporter<import("nodemailer/lib/smtp-transport/index.js").SentMessageInfo, import("nodemailer/lib/smtp-transport/index.js").Options>;
|
|
107
|
+
};
|
|
108
|
+
}, unknown, void>;
|
|
109
|
+
/**
|
|
110
|
+
* Default env-backed SMTP mail provider.
|
|
69
111
|
*
|
|
70
112
|
* Reads `MAIL_HOST`, `MAIL_PORT`, `MAIL_USER`, `MAIL_PASS`, and `MAIL_FROM`,
|
|
71
113
|
* verifies the transporter during setup, contributes `ports.mailer`, and
|
|
72
114
|
* exposes `ports.smtp.transporter` as an escape hatch.
|
|
73
115
|
*/
|
|
74
|
-
export declare const
|
|
116
|
+
export declare const smtpMailProvider: import("@beignet/core/providers").ServiceProvider<unknown, z.ZodObject<{
|
|
75
117
|
HOST: z.ZodString;
|
|
76
118
|
PORT: z.ZodCoercedNumber<unknown>;
|
|
77
119
|
USER: z.ZodString;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAGL,KAAK,WAAW,EAEhB,KAAK,UAAU,EAIhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAGL,KAAK,6BAA6B,EACnC,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAEV,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,QAAA,MAAM,gBAAgB;;;;;;iBAMpB,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,MAAM,EAAE,UAAU,CAAC;IACnB;;OAEG;IACH,IAAI,EAAE,mBAAmB,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,WAAW,EAAE,WAAW,CAAC;IACzB;;OAEG;IACH,IAAI,EAAE,WAAW,CAAC;IAClB;;OAEG;IACH,eAAe,CAAC,EAAE,6BAA6B,CAAC;CACjD;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAGL,KAAK,WAAW,EAEhB,KAAK,UAAU,EAIhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAGL,KAAK,6BAA6B,EACnC,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAEV,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,QAAA,MAAM,gBAAgB;;;;;;iBAMpB,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,MAAM,EAAE,UAAU,CAAC;IACnB;;OAEG;IACH,IAAI,EAAE,mBAAmB,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,WAAW,EAAE,WAAW,CAAC;IACzB;;OAEG;IACH,IAAI,EAAE,WAAW,CAAC;IAClB;;OAEG;IACH,eAAe,CAAC,EAAE,6BAA6B,CAAC;CACjD;AA0DD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,WAAW,EACX,IAAI,EACJ,eAAe,EAAE,qBAAqB,GACvC,EAAE,uBAAuB,GAAG,UAAU,CAqEtC;AAED;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,6BAAkC;;;;;;;;;;;kBA0E5C;AAED;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;iBAA2B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -19,6 +19,10 @@ function addOptional(target, key, value) {
|
|
|
19
19
|
Object.assign(target, { [key]: value });
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
+
function countRecipients(message) {
|
|
23
|
+
const count = (list) => list === undefined ? 0 : Array.isArray(list) ? list.length : 1;
|
|
24
|
+
return count(message.to) + count(message.cc) + count(message.bcc);
|
|
25
|
+
}
|
|
22
26
|
function createSmtpPayload(message, defaultFrom) {
|
|
23
27
|
const normalized = normalizeMailMessage(message, { defaultFrom });
|
|
24
28
|
if (!normalized.from) {
|
|
@@ -43,7 +47,7 @@ function createSmtpPayload(message, defaultFrom) {
|
|
|
43
47
|
/**
|
|
44
48
|
* Create a Beignet mailer port backed by Nodemailer SMTP.
|
|
45
49
|
*
|
|
46
|
-
* Use this directly for custom wiring, or install `
|
|
50
|
+
* Use this directly for custom wiring, or install `smtpMailProvider` to load
|
|
47
51
|
* config from env and contribute ports during server startup.
|
|
48
52
|
*/
|
|
49
53
|
export function createSmtpMailer({ transporter, from, instrumentation: instrumentationTarget, }) {
|
|
@@ -54,6 +58,7 @@ export function createSmtpMailer({ transporter, from, instrumentation: instrumen
|
|
|
54
58
|
return {
|
|
55
59
|
async send(message) {
|
|
56
60
|
const payload = createSmtpPayload(message, from);
|
|
61
|
+
const recipientCount = countRecipients(message);
|
|
57
62
|
instrumentation.custom({
|
|
58
63
|
name: "mail.send",
|
|
59
64
|
label: "Mail send",
|
|
@@ -102,9 +107,7 @@ export function createSmtpMailer({ transporter, from, instrumentation: instrumen
|
|
|
102
107
|
});
|
|
103
108
|
throw new MailDeliveryError({
|
|
104
109
|
provider: "smtp",
|
|
105
|
-
message: error instanceof Error
|
|
106
|
-
? error.message
|
|
107
|
-
: "SMTP failed to send email.",
|
|
110
|
+
message: `SMTP failed to send email to ${recipientCount} recipient(s): ${error instanceof Error ? error.message : String(error)}`,
|
|
108
111
|
cause: error,
|
|
109
112
|
});
|
|
110
113
|
}
|
|
@@ -112,56 +115,73 @@ export function createSmtpMailer({ transporter, from, instrumentation: instrumen
|
|
|
112
115
|
};
|
|
113
116
|
}
|
|
114
117
|
/**
|
|
115
|
-
*
|
|
118
|
+
* Create an env-backed SMTP mail provider.
|
|
119
|
+
*
|
|
120
|
+
* Options override the corresponding `MAIL_*` env values.
|
|
121
|
+
*/
|
|
122
|
+
export function createSmtpMailProvider(options = {}) {
|
|
123
|
+
return createProvider({
|
|
124
|
+
name: "mail-smtp",
|
|
125
|
+
config: {
|
|
126
|
+
schema: MailConfigSchema,
|
|
127
|
+
envPrefix: "MAIL_",
|
|
128
|
+
// Defined options win over env-derived values at config load time.
|
|
129
|
+
overrides: {
|
|
130
|
+
HOST: options.host,
|
|
131
|
+
PORT: options.port,
|
|
132
|
+
USER: options.user,
|
|
133
|
+
PASS: options.pass,
|
|
134
|
+
FROM: options.from,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
async setup({ config, ports }) {
|
|
138
|
+
if (!config) {
|
|
139
|
+
throw new Error("[smtpMailProvider] Missing Mail config. " +
|
|
140
|
+
"Please set MAIL_HOST, MAIL_PORT, MAIL_USER, MAIL_PASS, and MAIL_FROM environment variables.");
|
|
141
|
+
}
|
|
142
|
+
const { HOST: host, PORT: port, USER: user, PASS: pass, FROM: from, } = config;
|
|
143
|
+
const transporter = nodemailer.createTransport({
|
|
144
|
+
host,
|
|
145
|
+
port,
|
|
146
|
+
secure: port === 465,
|
|
147
|
+
auth: {
|
|
148
|
+
user,
|
|
149
|
+
pass,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
try {
|
|
153
|
+
await transporter.verify();
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
throw new Error(`[smtpMailProvider] Failed to connect to mail server at ${host}:${port}: ${error instanceof Error ? error.message : String(error)}`);
|
|
157
|
+
}
|
|
158
|
+
const instrumentation = createProviderInstrumentation(ports, {
|
|
159
|
+
providerName: "mail-smtp",
|
|
160
|
+
watcher: "mail",
|
|
161
|
+
});
|
|
162
|
+
const mailer = createSmtpMailer({
|
|
163
|
+
transporter,
|
|
164
|
+
from,
|
|
165
|
+
instrumentation,
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
ports: {
|
|
169
|
+
mailer,
|
|
170
|
+
smtp: { transporter },
|
|
171
|
+
},
|
|
172
|
+
stop() {
|
|
173
|
+
transporter.close();
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Default env-backed SMTP mail provider.
|
|
116
181
|
*
|
|
117
182
|
* Reads `MAIL_HOST`, `MAIL_PORT`, `MAIL_USER`, `MAIL_PASS`, and `MAIL_FROM`,
|
|
118
183
|
* verifies the transporter during setup, contributes `ports.mailer`, and
|
|
119
184
|
* exposes `ports.smtp.transporter` as an escape hatch.
|
|
120
185
|
*/
|
|
121
|
-
export const
|
|
122
|
-
name: "mail-smtp",
|
|
123
|
-
config: {
|
|
124
|
-
schema: MailConfigSchema,
|
|
125
|
-
envPrefix: "MAIL_",
|
|
126
|
-
},
|
|
127
|
-
async setup({ config, ports }) {
|
|
128
|
-
if (!config) {
|
|
129
|
-
throw new Error("[mailSmtpProvider] Missing Mail config. " +
|
|
130
|
-
"Please set MAIL_HOST, MAIL_PORT, MAIL_USER, MAIL_PASS, and MAIL_FROM environment variables.");
|
|
131
|
-
}
|
|
132
|
-
const transporter = nodemailer.createTransport({
|
|
133
|
-
host: config.HOST,
|
|
134
|
-
port: config.PORT,
|
|
135
|
-
secure: config.PORT === 465,
|
|
136
|
-
auth: {
|
|
137
|
-
user: config.USER,
|
|
138
|
-
pass: config.PASS,
|
|
139
|
-
},
|
|
140
|
-
});
|
|
141
|
-
try {
|
|
142
|
-
await transporter.verify();
|
|
143
|
-
}
|
|
144
|
-
catch (error) {
|
|
145
|
-
throw new Error(`[mailSmtpProvider] Failed to connect to mail server at ${config.HOST}:${config.PORT}: ${error instanceof Error ? error.message : String(error)}`);
|
|
146
|
-
}
|
|
147
|
-
const instrumentation = createProviderInstrumentation(ports, {
|
|
148
|
-
providerName: "mail-smtp",
|
|
149
|
-
watcher: "mail",
|
|
150
|
-
});
|
|
151
|
-
const mailer = createSmtpMailer({
|
|
152
|
-
transporter,
|
|
153
|
-
from: config.FROM,
|
|
154
|
-
instrumentation,
|
|
155
|
-
});
|
|
156
|
-
return {
|
|
157
|
-
ports: {
|
|
158
|
-
mailer,
|
|
159
|
-
smtp: { transporter },
|
|
160
|
-
},
|
|
161
|
-
stop() {
|
|
162
|
-
transporter.close();
|
|
163
|
-
},
|
|
164
|
-
};
|
|
165
|
-
},
|
|
166
|
-
});
|
|
186
|
+
export const smtpMailProvider = createSmtpMailProvider();
|
|
167
187
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beignet/provider-mail-smtp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.32",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "SMTP mail provider for Beignet - adds mailer port using nodemailer",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"registration": {
|
|
103
103
|
"required": true,
|
|
104
104
|
"tokens": [
|
|
105
|
-
"
|
|
105
|
+
"smtpMailProvider"
|
|
106
106
|
]
|
|
107
107
|
}
|
|
108
108
|
}
|
package/src/index.ts
CHANGED
|
@@ -50,7 +50,7 @@ export interface SmtpMailEscapeHatch {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
* Ports contributed by `
|
|
53
|
+
* Ports contributed by `smtpMailProvider`.
|
|
54
54
|
*/
|
|
55
55
|
export interface SmtpMailProviderPorts {
|
|
56
56
|
/**
|
|
@@ -90,6 +90,12 @@ function addOptional<
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
function countRecipients(message: SendMailOptions): number {
|
|
94
|
+
const count = (list: SendMailOptions["cc"]): number =>
|
|
95
|
+
list === undefined ? 0 : Array.isArray(list) ? list.length : 1;
|
|
96
|
+
return count(message.to) + count(message.cc) + count(message.bcc);
|
|
97
|
+
}
|
|
98
|
+
|
|
93
99
|
function createSmtpPayload(
|
|
94
100
|
message: SendMailOptions,
|
|
95
101
|
defaultFrom: MailAddress,
|
|
@@ -134,7 +140,7 @@ function createSmtpPayload(
|
|
|
134
140
|
/**
|
|
135
141
|
* Create a Beignet mailer port backed by Nodemailer SMTP.
|
|
136
142
|
*
|
|
137
|
-
* Use this directly for custom wiring, or install `
|
|
143
|
+
* Use this directly for custom wiring, or install `smtpMailProvider` to load
|
|
138
144
|
* config from env and contribute ports during server startup.
|
|
139
145
|
*/
|
|
140
146
|
export function createSmtpMailer({
|
|
@@ -150,6 +156,7 @@ export function createSmtpMailer({
|
|
|
150
156
|
return {
|
|
151
157
|
async send(message): Promise<SendMailResult> {
|
|
152
158
|
const payload = createSmtpPayload(message, from);
|
|
159
|
+
const recipientCount = countRecipients(message);
|
|
153
160
|
instrumentation.custom({
|
|
154
161
|
name: "mail.send",
|
|
155
162
|
label: "Mail send",
|
|
@@ -201,10 +208,9 @@ export function createSmtpMailer({
|
|
|
201
208
|
});
|
|
202
209
|
throw new MailDeliveryError({
|
|
203
210
|
provider: "smtp",
|
|
204
|
-
message:
|
|
205
|
-
error instanceof Error
|
|
206
|
-
|
|
207
|
-
: "SMTP failed to send email.",
|
|
211
|
+
message: `SMTP failed to send email to ${recipientCount} recipient(s): ${
|
|
212
|
+
error instanceof Error ? error.message : String(error)
|
|
213
|
+
}`,
|
|
208
214
|
cause: error,
|
|
209
215
|
});
|
|
210
216
|
}
|
|
@@ -213,66 +219,118 @@ export function createSmtpMailer({
|
|
|
213
219
|
}
|
|
214
220
|
|
|
215
221
|
/**
|
|
216
|
-
*
|
|
222
|
+
* Options for the SMTP mail provider factory.
|
|
223
|
+
*/
|
|
224
|
+
export interface CreateSmtpMailProviderOptions {
|
|
225
|
+
/**
|
|
226
|
+
* SMTP server hostname. Overrides `MAIL_HOST`.
|
|
227
|
+
*/
|
|
228
|
+
host?: string;
|
|
229
|
+
/**
|
|
230
|
+
* SMTP server port. Port 465 uses SSL. Overrides `MAIL_PORT`.
|
|
231
|
+
*/
|
|
232
|
+
port?: number;
|
|
233
|
+
/**
|
|
234
|
+
* SMTP username. Overrides `MAIL_USER`.
|
|
235
|
+
*/
|
|
236
|
+
user?: string;
|
|
237
|
+
/**
|
|
238
|
+
* SMTP password. Overrides `MAIL_PASS`.
|
|
239
|
+
*/
|
|
240
|
+
pass?: string;
|
|
241
|
+
/**
|
|
242
|
+
* Default sender address. Overrides `MAIL_FROM`.
|
|
243
|
+
*/
|
|
244
|
+
from?: string;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Create an env-backed SMTP mail provider.
|
|
217
249
|
*
|
|
218
|
-
*
|
|
219
|
-
* verifies the transporter during setup, contributes `ports.mailer`, and
|
|
220
|
-
* exposes `ports.smtp.transporter` as an escape hatch.
|
|
250
|
+
* Options override the corresponding `MAIL_*` env values.
|
|
221
251
|
*/
|
|
222
|
-
export
|
|
223
|
-
|
|
252
|
+
export function createSmtpMailProvider(
|
|
253
|
+
options: CreateSmtpMailProviderOptions = {},
|
|
254
|
+
) {
|
|
255
|
+
return createProvider({
|
|
256
|
+
name: "mail-smtp",
|
|
224
257
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
258
|
+
config: {
|
|
259
|
+
schema: MailConfigSchema,
|
|
260
|
+
envPrefix: "MAIL_",
|
|
261
|
+
// Defined options win over env-derived values at config load time.
|
|
262
|
+
overrides: {
|
|
263
|
+
HOST: options.host,
|
|
264
|
+
PORT: options.port,
|
|
265
|
+
USER: options.user,
|
|
266
|
+
PASS: options.pass,
|
|
267
|
+
FROM: options.from,
|
|
268
|
+
},
|
|
269
|
+
},
|
|
229
270
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
271
|
+
async setup({ config, ports }) {
|
|
272
|
+
if (!config) {
|
|
273
|
+
throw new Error(
|
|
274
|
+
"[smtpMailProvider] Missing Mail config. " +
|
|
275
|
+
"Please set MAIL_HOST, MAIL_PORT, MAIL_USER, MAIL_PASS, and MAIL_FROM environment variables.",
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
const {
|
|
279
|
+
HOST: host,
|
|
280
|
+
PORT: port,
|
|
281
|
+
USER: user,
|
|
282
|
+
PASS: pass,
|
|
283
|
+
FROM: from,
|
|
284
|
+
} = config;
|
|
237
285
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
286
|
+
const transporter = nodemailer.createTransport({
|
|
287
|
+
host,
|
|
288
|
+
port,
|
|
289
|
+
secure: port === 465,
|
|
290
|
+
auth: {
|
|
291
|
+
user,
|
|
292
|
+
pass,
|
|
293
|
+
},
|
|
294
|
+
});
|
|
247
295
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
296
|
+
try {
|
|
297
|
+
await transporter.verify();
|
|
298
|
+
} catch (error) {
|
|
299
|
+
throw new Error(
|
|
300
|
+
`[smtpMailProvider] Failed to connect to mail server at ${host}:${port}: ${
|
|
301
|
+
error instanceof Error ? error.message : String(error)
|
|
302
|
+
}`,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
257
305
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
306
|
+
const instrumentation = createProviderInstrumentation(ports, {
|
|
307
|
+
providerName: "mail-smtp",
|
|
308
|
+
watcher: "mail",
|
|
309
|
+
});
|
|
310
|
+
const mailer = createSmtpMailer({
|
|
311
|
+
transporter,
|
|
312
|
+
from,
|
|
313
|
+
instrumentation,
|
|
314
|
+
});
|
|
267
315
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
});
|
|
316
|
+
return {
|
|
317
|
+
ports: {
|
|
318
|
+
mailer,
|
|
319
|
+
smtp: { transporter },
|
|
320
|
+
} satisfies SmtpMailProviderPorts,
|
|
321
|
+
stop() {
|
|
322
|
+
transporter.close();
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Default env-backed SMTP mail provider.
|
|
331
|
+
*
|
|
332
|
+
* Reads `MAIL_HOST`, `MAIL_PORT`, `MAIL_USER`, `MAIL_PASS`, and `MAIL_FROM`,
|
|
333
|
+
* verifies the transporter during setup, contributes `ports.mailer`, and
|
|
334
|
+
* exposes `ports.smtp.transporter` as an escape hatch.
|
|
335
|
+
*/
|
|
336
|
+
export const smtpMailProvider = createSmtpMailProvider();
|