@igniter-js/mail 0.1.0 → 0.1.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/AGENTS.md +54 -41
- package/CHANGELOG.md +7 -8
- package/README.md +55 -44
- package/dist/adapter-BhnIsrlh.d.mts +60 -0
- package/dist/adapter-BhnIsrlh.d.ts +60 -0
- package/dist/adapters/index.d.mts +229 -0
- package/dist/adapters/index.d.ts +229 -0
- package/dist/adapters/index.js +371 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/index.mjs +361 -0
- package/dist/adapters/index.mjs.map +1 -0
- package/dist/index.d.mts +73 -267
- package/dist/index.d.ts +73 -267
- package/dist/index.js +612 -594
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +609 -580
- package/dist/index.mjs.map +1 -1
- package/dist/shim.d.mts +42 -0
- package/dist/shim.d.ts +42 -0
- package/dist/shim.js +83 -0
- package/dist/shim.js.map +1 -0
- package/dist/shim.mjs +72 -0
- package/dist/shim.mjs.map +1 -0
- package/dist/telemetry/index.d.mts +59 -0
- package/dist/telemetry/index.d.ts +59 -0
- package/dist/telemetry/index.js +71 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/index.mjs +69 -0
- package/dist/telemetry/index.mjs.map +1 -0
- package/package.json +27 -2
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
11
11
|
var nodemailer__default = /*#__PURE__*/_interopDefault(nodemailer);
|
|
12
12
|
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
13
13
|
|
|
14
|
-
// src/errors/
|
|
14
|
+
// src/errors/mail.error.ts
|
|
15
15
|
var IgniterMailError = class _IgniterMailError extends core.IgniterError {
|
|
16
16
|
constructor(payload) {
|
|
17
17
|
super({
|
|
@@ -34,560 +34,375 @@ var IgniterMailError = class _IgniterMailError extends core.IgniterError {
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
// src/adapters/postmark.adapter.ts
|
|
37
|
-
var
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
var PostmarkMailAdapter = class _PostmarkMailAdapter {
|
|
38
|
+
/**
|
|
39
|
+
* Creates a new adapter instance.
|
|
40
|
+
*
|
|
41
|
+
* @param credentials - Provider credentials (secret/from).
|
|
42
|
+
* @throws {IgniterMailError} Does not throw on creation; errors surface on send.
|
|
43
|
+
*/
|
|
44
|
+
constructor(credentials = {}) {
|
|
45
|
+
this.credentials = credentials;
|
|
46
46
|
}
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Creates a new adapter instance.
|
|
49
|
+
*
|
|
50
|
+
* @param credentials - Provider credentials (secret/from).
|
|
51
|
+
* @returns A Postmark adapter instance.
|
|
52
|
+
* @throws {IgniterMailError} Does not throw on creation; errors surface on send.
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const adapter = PostmarkMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
static create(credentials) {
|
|
59
|
+
return new _PostmarkMailAdapter(credentials);
|
|
51
60
|
}
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Sends an email using Postmark (HTTP API).
|
|
63
|
+
*
|
|
64
|
+
* @param params - Normalized email parameters.
|
|
65
|
+
* @returns A promise that resolves when the email is accepted.
|
|
66
|
+
* @throws {IgniterMailError} When configuration is invalid or Postmark rejects the request.
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
async send(params) {
|
|
73
|
+
if (!this.credentials.secret) {
|
|
55
74
|
throw new IgniterMailError({
|
|
56
75
|
code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
|
|
57
76
|
message: "Postmark adapter secret is required"
|
|
58
77
|
});
|
|
59
78
|
}
|
|
60
|
-
if (!this.from) {
|
|
79
|
+
if (!this.credentials.from) {
|
|
61
80
|
throw new IgniterMailError({
|
|
62
81
|
code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
|
|
63
82
|
message: "Postmark adapter from is required"
|
|
64
83
|
});
|
|
65
84
|
}
|
|
66
|
-
const token = this.secret;
|
|
67
|
-
const from = this.from;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
metadata: {
|
|
92
|
-
status: response.status,
|
|
93
|
-
body
|
|
94
|
-
}
|
|
95
|
-
});
|
|
85
|
+
const token = this.credentials.secret;
|
|
86
|
+
const from = this.credentials.from;
|
|
87
|
+
const response = await fetch("https://api.postmarkapp.com/email", {
|
|
88
|
+
method: "POST",
|
|
89
|
+
headers: {
|
|
90
|
+
Accept: "application/json",
|
|
91
|
+
"Content-Type": "application/json",
|
|
92
|
+
"X-Postmark-Server-Token": token
|
|
93
|
+
},
|
|
94
|
+
body: JSON.stringify({
|
|
95
|
+
From: from,
|
|
96
|
+
To: params.to,
|
|
97
|
+
Subject: params.subject,
|
|
98
|
+
HtmlBody: params.html,
|
|
99
|
+
TextBody: params.text
|
|
100
|
+
})
|
|
101
|
+
});
|
|
102
|
+
if (!response.ok) {
|
|
103
|
+
const body = await response.text().catch(() => "");
|
|
104
|
+
throw new IgniterMailError({
|
|
105
|
+
code: "MAIL_PROVIDER_SEND_FAILED",
|
|
106
|
+
message: "Postmark send failed",
|
|
107
|
+
metadata: {
|
|
108
|
+
status: response.status,
|
|
109
|
+
body
|
|
96
110
|
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
99
113
|
}
|
|
100
114
|
};
|
|
101
|
-
var
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
this.
|
|
110
|
-
return this;
|
|
115
|
+
var ResendMailAdapter = class _ResendMailAdapter {
|
|
116
|
+
/**
|
|
117
|
+
* Creates an adapter with credentials.
|
|
118
|
+
*
|
|
119
|
+
* @param credentials - Adapter credentials including API secret and default from.
|
|
120
|
+
* @throws {IgniterMailError} Does not throw on creation; errors surface on send.
|
|
121
|
+
*/
|
|
122
|
+
constructor(credentials = {}) {
|
|
123
|
+
this.credentials = credentials;
|
|
111
124
|
}
|
|
112
|
-
/**
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Creates a new adapter instance.
|
|
127
|
+
*
|
|
128
|
+
* @param credentials - Adapter credentials including API secret and default from.
|
|
129
|
+
* @returns A configured Resend adapter.
|
|
130
|
+
* @throws {IgniterMailError} Does not throw on creation; errors surface on send.
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* const adapter = ResendMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
static create(credentials) {
|
|
137
|
+
return new _ResendMailAdapter(credentials);
|
|
116
138
|
}
|
|
117
|
-
/**
|
|
118
|
-
|
|
119
|
-
|
|
139
|
+
/**
|
|
140
|
+
* Sends an email using Resend.
|
|
141
|
+
*
|
|
142
|
+
* @param params - Email payload to send.
|
|
143
|
+
* @returns Resolves when the email is accepted by Resend.
|
|
144
|
+
* @throws {IgniterMailError} When credentials are missing or Resend rejects the request.
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```ts
|
|
148
|
+
* await adapter.send({ to: 'user@example.com', subject: 'Welcome', html: '<p>Hi</p>', text: 'Hi' })
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
async send(params) {
|
|
152
|
+
if (!this.credentials.secret) {
|
|
120
153
|
throw new IgniterMailError({
|
|
121
154
|
code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
|
|
122
155
|
message: "Resend adapter secret is required"
|
|
123
156
|
});
|
|
124
157
|
}
|
|
125
|
-
if (!this.from) {
|
|
158
|
+
if (!this.credentials.from) {
|
|
126
159
|
throw new IgniterMailError({
|
|
127
160
|
code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
|
|
128
161
|
message: "Resend adapter from is required"
|
|
129
162
|
});
|
|
130
163
|
}
|
|
131
|
-
const resend$1 = new resend.Resend(this.secret);
|
|
132
|
-
const from = this.from;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
to,
|
|
142
|
-
from,
|
|
143
|
-
subject,
|
|
144
|
-
html,
|
|
145
|
-
text,
|
|
146
|
-
scheduledAt: scheduledAt?.toISOString()
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
};
|
|
164
|
+
const resend$1 = new resend.Resend(this.credentials.secret);
|
|
165
|
+
const from = this.credentials.from;
|
|
166
|
+
await resend$1.emails.create({
|
|
167
|
+
to: params.to,
|
|
168
|
+
from,
|
|
169
|
+
subject: params.subject,
|
|
170
|
+
html: params.html,
|
|
171
|
+
text: params.text,
|
|
172
|
+
scheduledAt: params.scheduledAt?.toISOString()
|
|
173
|
+
});
|
|
150
174
|
}
|
|
151
175
|
};
|
|
152
|
-
var resendAdapter = (options) => ResendMailAdapterBuilder.create().withSecret(options.secret).withFrom(options.from).build();
|
|
153
176
|
|
|
154
177
|
// src/adapters/sendgrid.adapter.ts
|
|
155
|
-
var
|
|
156
|
-
/**
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
178
|
+
var SendGridMailAdapter = class _SendGridMailAdapter {
|
|
179
|
+
/**
|
|
180
|
+
* Creates an adapter with credentials.
|
|
181
|
+
*
|
|
182
|
+
* @param credentials - Adapter credentials including API secret and default from.
|
|
183
|
+
* @throws {IgniterMailError} Does not throw on creation; errors surface on send.
|
|
184
|
+
*/
|
|
185
|
+
constructor(credentials = {}) {
|
|
186
|
+
this.credentials = credentials;
|
|
164
187
|
}
|
|
165
|
-
/**
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
188
|
+
/**
|
|
189
|
+
* Creates a new adapter instance.
|
|
190
|
+
*
|
|
191
|
+
* @param credentials - Adapter credentials including API secret and default from.
|
|
192
|
+
* @returns A configured SendGrid adapter.
|
|
193
|
+
* @throws {IgniterMailError} Does not throw on creation; errors surface on send.
|
|
194
|
+
* @example
|
|
195
|
+
* ```ts
|
|
196
|
+
* const adapter = SendGridMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
199
|
+
static create(credentials) {
|
|
200
|
+
return new _SendGridMailAdapter(credentials);
|
|
169
201
|
}
|
|
170
|
-
/**
|
|
171
|
-
|
|
172
|
-
|
|
202
|
+
/**
|
|
203
|
+
* Sends an email using SendGrid (HTTP API).
|
|
204
|
+
*
|
|
205
|
+
* @param params - Email payload to send.
|
|
206
|
+
* @returns Resolves when the email is accepted by SendGrid.
|
|
207
|
+
* @throws {IgniterMailError} When credentials are missing or the API fails.
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```ts
|
|
211
|
+
* await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
async send(params) {
|
|
215
|
+
if (!this.credentials.secret) {
|
|
173
216
|
throw new IgniterMailError({
|
|
174
217
|
code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
|
|
175
218
|
message: "SendGrid adapter secret is required"
|
|
176
219
|
});
|
|
177
220
|
}
|
|
178
|
-
if (!this.from) {
|
|
221
|
+
if (!this.credentials.from) {
|
|
179
222
|
throw new IgniterMailError({
|
|
180
223
|
code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
|
|
181
224
|
message: "SendGrid adapter from is required"
|
|
182
225
|
});
|
|
183
226
|
}
|
|
184
|
-
const apiKey = this.secret;
|
|
185
|
-
const from = this.from;
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
body
|
|
215
|
-
}
|
|
216
|
-
});
|
|
227
|
+
const apiKey = this.credentials.secret;
|
|
228
|
+
const from = this.credentials.from;
|
|
229
|
+
const response = await fetch("https://api.sendgrid.com/v3/mail/send", {
|
|
230
|
+
method: "POST",
|
|
231
|
+
headers: {
|
|
232
|
+
Authorization: `Bearer ${apiKey}`,
|
|
233
|
+
"Content-Type": "application/json"
|
|
234
|
+
},
|
|
235
|
+
body: JSON.stringify({
|
|
236
|
+
personalizations: [
|
|
237
|
+
{
|
|
238
|
+
to: [{ email: params.to }]
|
|
239
|
+
}
|
|
240
|
+
],
|
|
241
|
+
from: { email: from },
|
|
242
|
+
subject: params.subject,
|
|
243
|
+
content: [
|
|
244
|
+
{ type: "text/plain", value: params.text },
|
|
245
|
+
{ type: "text/html", value: params.html }
|
|
246
|
+
]
|
|
247
|
+
})
|
|
248
|
+
});
|
|
249
|
+
if (!response.ok) {
|
|
250
|
+
const body = await response.text().catch(() => "");
|
|
251
|
+
throw new IgniterMailError({
|
|
252
|
+
code: "MAIL_PROVIDER_SEND_FAILED",
|
|
253
|
+
message: "SendGrid send failed",
|
|
254
|
+
metadata: {
|
|
255
|
+
status: response.status,
|
|
256
|
+
body
|
|
217
257
|
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
220
260
|
}
|
|
221
261
|
};
|
|
222
|
-
var
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
this.
|
|
231
|
-
return this;
|
|
262
|
+
var SmtpMailAdapter = class _SmtpMailAdapter {
|
|
263
|
+
/**
|
|
264
|
+
* Creates an adapter with credentials.
|
|
265
|
+
*
|
|
266
|
+
* @param credentials - Adapter credentials including SMTP URL and default from.
|
|
267
|
+
* @throws {IgniterMailError} Does not throw on creation; errors surface on send.
|
|
268
|
+
*/
|
|
269
|
+
constructor(credentials = {}) {
|
|
270
|
+
this.credentials = credentials;
|
|
232
271
|
}
|
|
233
|
-
/**
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
272
|
+
/**
|
|
273
|
+
* Creates a new adapter instance.
|
|
274
|
+
*
|
|
275
|
+
* @param credentials - Adapter credentials including SMTP URL and default from.
|
|
276
|
+
* @returns A configured SMTP adapter.
|
|
277
|
+
* @throws {IgniterMailError} Does not throw on creation; errors surface on send.
|
|
278
|
+
* @example
|
|
279
|
+
* ```ts
|
|
280
|
+
* const adapter = SmtpMailAdapter.create({ secret: 'smtps://user:pass@host:465', from: 'no-reply@acme.com' })
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
static create(credentials) {
|
|
284
|
+
return new _SmtpMailAdapter(credentials);
|
|
237
285
|
}
|
|
238
|
-
/**
|
|
239
|
-
|
|
240
|
-
|
|
286
|
+
/**
|
|
287
|
+
* Sends an email using Nodemailer over SMTP.
|
|
288
|
+
*
|
|
289
|
+
* @param params - Email payload to send.
|
|
290
|
+
* @returns Resolves when the email is sent.
|
|
291
|
+
* @throws {IgniterMailError} When credentials are missing or the SMTP send fails.
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* ```ts
|
|
295
|
+
* await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
async send(params) {
|
|
299
|
+
if (!this.credentials.secret) {
|
|
241
300
|
throw new IgniterMailError({
|
|
242
301
|
code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
|
|
243
302
|
message: "SMTP adapter secret is required"
|
|
244
303
|
});
|
|
245
304
|
}
|
|
246
|
-
if (!this.from) {
|
|
305
|
+
if (!this.credentials.from) {
|
|
247
306
|
throw new IgniterMailError({
|
|
248
307
|
code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
|
|
249
308
|
message: "SMTP adapter from is required"
|
|
250
309
|
});
|
|
251
310
|
}
|
|
252
|
-
const smtpUrl = this.secret;
|
|
253
|
-
const from = this.from;
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
greetingTimeout: 5e3,
|
|
261
|
-
socketTimeout: 1e4
|
|
262
|
-
});
|
|
263
|
-
};
|
|
264
|
-
return {
|
|
265
|
-
/** Sends an email using Nodemailer over SMTP. */
|
|
266
|
-
send: async ({ to, subject, html, text }) => {
|
|
267
|
-
const transport = createTransporter();
|
|
268
|
-
const mailOptions = {
|
|
269
|
-
from,
|
|
270
|
-
to,
|
|
271
|
-
subject,
|
|
272
|
-
html,
|
|
273
|
-
text
|
|
274
|
-
};
|
|
275
|
-
try {
|
|
276
|
-
await transport.sendMail(mailOptions);
|
|
277
|
-
transport.close();
|
|
278
|
-
} catch (error) {
|
|
279
|
-
transport.close();
|
|
280
|
-
throw error;
|
|
281
|
-
}
|
|
311
|
+
const smtpUrl = this.credentials.secret;
|
|
312
|
+
const from = this.credentials.from;
|
|
313
|
+
const transport = nodemailer__default.default.createTransport(smtpUrl, {
|
|
314
|
+
connectionTimeout: 1e4,
|
|
315
|
+
greetingTimeout: 5e3,
|
|
316
|
+
socketTimeout: 1e4,
|
|
317
|
+
tls: {
|
|
318
|
+
rejectUnauthorized: false
|
|
282
319
|
}
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
const logger = options.logger ?? console;
|
|
292
|
-
const silent = options.silent ?? false;
|
|
293
|
-
return {
|
|
294
|
-
sent,
|
|
295
|
-
reset: () => {
|
|
296
|
-
sent.length = 0;
|
|
297
|
-
},
|
|
298
|
-
last: () => sent.at(-1),
|
|
299
|
-
send: async (params) => {
|
|
300
|
-
sent.push({ ...params, at: /* @__PURE__ */ new Date() });
|
|
301
|
-
if (!silent) {
|
|
302
|
-
logger.info(
|
|
303
|
-
`[TestMailAdapter] to=${params.to} subject=${params.subject} html=${params.html.length}B text=${params.text.length}B`
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// src/adapters/webhook.adapter.ts
|
|
311
|
-
var WebhookMailAdapterBuilder = class _WebhookMailAdapterBuilder {
|
|
312
|
-
/** Creates a new builder instance. */
|
|
313
|
-
static create() {
|
|
314
|
-
return new _WebhookMailAdapterBuilder();
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Sets the webhook URL.
|
|
318
|
-
*
|
|
319
|
-
* Note: when using `IgniterMailBuilder.withAdapter('webhook', secret)`, the `secret`
|
|
320
|
-
* is treated as the webhook URL.
|
|
321
|
-
*/
|
|
322
|
-
withUrl(url) {
|
|
323
|
-
this.url = url;
|
|
324
|
-
return this;
|
|
325
|
-
}
|
|
326
|
-
/** Sets the default FROM address. */
|
|
327
|
-
withFrom(from) {
|
|
328
|
-
this.from = from;
|
|
329
|
-
return this;
|
|
330
|
-
}
|
|
331
|
-
/** Builds the adapter instance. */
|
|
332
|
-
build() {
|
|
333
|
-
if (!this.url) {
|
|
334
|
-
throw new IgniterMailError({
|
|
335
|
-
code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
|
|
336
|
-
message: "Webhook adapter url is required"
|
|
320
|
+
});
|
|
321
|
+
try {
|
|
322
|
+
await transport.sendMail({
|
|
323
|
+
from,
|
|
324
|
+
to: params.to,
|
|
325
|
+
subject: params.subject,
|
|
326
|
+
html: params.html,
|
|
327
|
+
text: params.text
|
|
337
328
|
});
|
|
338
|
-
}
|
|
339
|
-
if (!this.from) {
|
|
329
|
+
} catch (error) {
|
|
340
330
|
throw new IgniterMailError({
|
|
341
|
-
code: "
|
|
342
|
-
message: "
|
|
331
|
+
code: "MAIL_PROVIDER_SEND_FAILED",
|
|
332
|
+
message: "SMTP send failed",
|
|
333
|
+
metadata: {
|
|
334
|
+
originalError: error
|
|
335
|
+
}
|
|
343
336
|
});
|
|
337
|
+
} finally {
|
|
338
|
+
transport.close();
|
|
344
339
|
}
|
|
345
|
-
const url = this.url;
|
|
346
|
-
const from = this.from;
|
|
347
|
-
return {
|
|
348
|
-
/** Sends an email by POST-ing to the configured webhook URL. */
|
|
349
|
-
send: async ({ to, subject, html, text, scheduledAt }) => {
|
|
350
|
-
const response = await fetch(url, {
|
|
351
|
-
method: "POST",
|
|
352
|
-
headers: {
|
|
353
|
-
"Content-Type": "application/json"
|
|
354
|
-
},
|
|
355
|
-
body: JSON.stringify({
|
|
356
|
-
to,
|
|
357
|
-
from,
|
|
358
|
-
subject,
|
|
359
|
-
html,
|
|
360
|
-
text,
|
|
361
|
-
scheduledAt: scheduledAt?.toISOString()
|
|
362
|
-
})
|
|
363
|
-
});
|
|
364
|
-
if (!response.ok) {
|
|
365
|
-
const body = await response.text().catch(() => "");
|
|
366
|
-
throw new IgniterMailError({
|
|
367
|
-
code: "MAIL_PROVIDER_SEND_FAILED",
|
|
368
|
-
message: "Webhook send failed",
|
|
369
|
-
metadata: {
|
|
370
|
-
status: response.status,
|
|
371
|
-
body
|
|
372
|
-
}
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
};
|
|
377
340
|
}
|
|
378
341
|
};
|
|
379
|
-
var webhookAdapter = (options) => WebhookMailAdapterBuilder.create().withUrl(options.secret).withFrom(options.from).build();
|
|
380
342
|
|
|
381
|
-
// src/
|
|
382
|
-
var
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
withLogger(logger) {
|
|
400
|
-
this.logger = logger;
|
|
401
|
-
return this;
|
|
402
|
-
}
|
|
343
|
+
// src/types/telemetry.ts
|
|
344
|
+
var IGNITER_MAIL_TELEMETRY_EVENTS = {
|
|
345
|
+
/** Emitted when mail send operation starts */
|
|
346
|
+
SEND_STARTED: "igniter.mail.send.started",
|
|
347
|
+
/** Emitted when mail send operation succeeds */
|
|
348
|
+
SEND_SUCCESS: "igniter.mail.send.success",
|
|
349
|
+
/** Emitted when mail send operation fails */
|
|
350
|
+
SEND_ERROR: "igniter.mail.send.error",
|
|
351
|
+
/** Emitted when mail schedule operation starts */
|
|
352
|
+
SCHEDULE_STARTED: "igniter.mail.schedule.started",
|
|
353
|
+
/** Emitted when mail schedule operation succeeds */
|
|
354
|
+
SCHEDULE_SUCCESS: "igniter.mail.schedule.success",
|
|
355
|
+
/** Emitted when mail schedule operation fails */
|
|
356
|
+
SCHEDULE_ERROR: "igniter.mail.schedule.error"
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// src/utils/schema.ts
|
|
360
|
+
var IgniterMailSchema = class {
|
|
403
361
|
/**
|
|
404
|
-
*
|
|
362
|
+
* Validates an unknown input using `StandardSchemaV1` when the schema provides `~standard.validate`.
|
|
405
363
|
*
|
|
406
|
-
* If
|
|
407
|
-
*/
|
|
408
|
-
withQueue(adapter, options) {
|
|
409
|
-
this.queue = { adapter, options };
|
|
410
|
-
return this;
|
|
411
|
-
}
|
|
412
|
-
withAdapter(adapterOrProvider, secret) {
|
|
413
|
-
if (typeof adapterOrProvider === "string") {
|
|
414
|
-
if (!secret) {
|
|
415
|
-
throw new IgniterMailError({
|
|
416
|
-
code: "MAIL_PROVIDER_ADAPTER_SECRET_REQUIRED",
|
|
417
|
-
message: "MAIL_PROVIDER_ADAPTER_SECRET_REQUIRED",
|
|
418
|
-
logger: this.logger
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
this.adapter = {
|
|
422
|
-
kind: "provider",
|
|
423
|
-
provider: adapterOrProvider,
|
|
424
|
-
secret
|
|
425
|
-
};
|
|
426
|
-
return this;
|
|
427
|
-
}
|
|
428
|
-
this.adapter = {
|
|
429
|
-
kind: "adapter",
|
|
430
|
-
adapter: adapterOrProvider
|
|
431
|
-
};
|
|
432
|
-
return this;
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Registers a template.
|
|
436
|
-
*/
|
|
437
|
-
addTemplate(key, template) {
|
|
438
|
-
this.templates[key] = template;
|
|
439
|
-
return this;
|
|
440
|
-
}
|
|
441
|
-
/** Hook invoked before sending. */
|
|
442
|
-
withOnSendStarted(onSendStarted) {
|
|
443
|
-
this.onSendStarted = onSendStarted;
|
|
444
|
-
return this;
|
|
445
|
-
}
|
|
446
|
-
/** Hook invoked on error. */
|
|
447
|
-
withOnSendError(onSendError) {
|
|
448
|
-
this.onSendError = onSendError;
|
|
449
|
-
return this;
|
|
450
|
-
}
|
|
451
|
-
/** Hook invoked on success. */
|
|
452
|
-
withOnSendSuccess(onSendSuccess) {
|
|
453
|
-
this.onSendSuccess = onSendSuccess;
|
|
454
|
-
return this;
|
|
455
|
-
}
|
|
456
|
-
/**
|
|
457
|
-
* Builds the {@link IgniterMail} instance.
|
|
364
|
+
* If the schema does not provide a validator, this method returns the input as-is.
|
|
458
365
|
*/
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
message: "MAIL_PROVIDER_FROM_REQUIRED",
|
|
464
|
-
logger: this.logger
|
|
465
|
-
});
|
|
366
|
+
static async validateInput(schema, input) {
|
|
367
|
+
const standard = schema?.["~standard"];
|
|
368
|
+
if (!standard?.validate) {
|
|
369
|
+
return input;
|
|
466
370
|
}
|
|
467
|
-
|
|
371
|
+
const result = await standard.validate(input);
|
|
372
|
+
if (result?.issues?.length) {
|
|
468
373
|
throw new IgniterMailError({
|
|
469
|
-
code: "
|
|
470
|
-
message: "
|
|
471
|
-
|
|
374
|
+
code: "MAIL_PROVIDER_TEMPLATE_DATA_INVALID",
|
|
375
|
+
message: "Invalid mail template payload",
|
|
376
|
+
statusCode: 400,
|
|
377
|
+
details: result.issues
|
|
472
378
|
});
|
|
473
379
|
}
|
|
474
|
-
|
|
475
|
-
switch (this.adapter.provider) {
|
|
476
|
-
case "resend":
|
|
477
|
-
return ResendMailAdapterBuilder.create().withSecret(this.adapter.secret).withFrom(this.from).build();
|
|
478
|
-
case "smtp":
|
|
479
|
-
return SmtpMailAdapterBuilder.create().withSecret(this.adapter.secret).withFrom(this.from).build();
|
|
480
|
-
case "postmark":
|
|
481
|
-
return PostmarkMailAdapterBuilder.create().withSecret(this.adapter.secret).withFrom(this.from).build();
|
|
482
|
-
case "sendgrid":
|
|
483
|
-
return SendGridMailAdapterBuilder.create().withSecret(this.adapter.secret).withFrom(this.from).build();
|
|
484
|
-
case "webhook":
|
|
485
|
-
return WebhookMailAdapterBuilder.create().withUrl(this.adapter.secret).withFrom(this.from).build();
|
|
486
|
-
default:
|
|
487
|
-
throw new IgniterMailError({
|
|
488
|
-
code: "MAIL_PROVIDER_ADAPTER_NOT_FOUND",
|
|
489
|
-
message: `MAIL_PROVIDER_ADAPTER_NOT_FOUND: ${this.adapter.provider}`,
|
|
490
|
-
logger: this.logger,
|
|
491
|
-
metadata: {
|
|
492
|
-
provider: this.adapter.provider
|
|
493
|
-
}
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
})();
|
|
497
|
-
return this.factory({
|
|
498
|
-
from: this.from,
|
|
499
|
-
adapter: resolvedAdapter,
|
|
500
|
-
templates: this.templates,
|
|
501
|
-
onSendStarted: this.onSendStarted,
|
|
502
|
-
onSendError: this.onSendError,
|
|
503
|
-
onSendSuccess: this.onSendSuccess,
|
|
504
|
-
logger: this.logger,
|
|
505
|
-
queue: this.queue ? {
|
|
506
|
-
adapter: this.queue.adapter,
|
|
507
|
-
id: `${this.queue.options?.namespace ?? "mail"}.${this.queue.options?.task ?? "send"}`,
|
|
508
|
-
options: this.queue.options
|
|
509
|
-
} : void 0
|
|
510
|
-
});
|
|
380
|
+
return result?.value ?? input;
|
|
511
381
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
/** Creates a new builder instance. */
|
|
517
|
-
static create() {
|
|
518
|
-
return new _MailTemplateBuilder();
|
|
519
|
-
}
|
|
520
|
-
/** Sets the default subject for the template. */
|
|
521
|
-
withSubject(subject) {
|
|
522
|
-
this.subject = subject;
|
|
523
|
-
return this;
|
|
524
|
-
}
|
|
525
|
-
/** Attaches the schema used to validate and infer payload types. */
|
|
526
|
-
withSchema(schema) {
|
|
527
|
-
this.schema = schema;
|
|
528
|
-
return this;
|
|
529
|
-
}
|
|
530
|
-
/** Sets the React Email render function for the template. */
|
|
531
|
-
withRender(render2) {
|
|
532
|
-
this.render = render2;
|
|
533
|
-
return this;
|
|
534
|
-
}
|
|
535
|
-
/** Builds the template definition. */
|
|
536
|
-
build() {
|
|
537
|
-
if (!this.subject) {
|
|
538
|
-
throw new IgniterMailError({
|
|
539
|
-
code: "MAIL_TEMPLATE_CONFIGURATION_INVALID",
|
|
540
|
-
message: "Mail template subject is required"
|
|
541
|
-
});
|
|
542
|
-
}
|
|
543
|
-
if (!this.schema) {
|
|
544
|
-
throw new IgniterMailError({
|
|
545
|
-
code: "MAIL_TEMPLATE_CONFIGURATION_INVALID",
|
|
546
|
-
message: "Mail template schema is required"
|
|
547
|
-
});
|
|
548
|
-
}
|
|
549
|
-
if (!this.render) {
|
|
550
|
-
throw new IgniterMailError({
|
|
551
|
-
code: "MAIL_TEMPLATE_CONFIGURATION_INVALID",
|
|
552
|
-
message: "Mail template render is required"
|
|
553
|
-
});
|
|
554
|
-
}
|
|
382
|
+
/**
|
|
383
|
+
* Creates a passthrough StandardSchema validator.
|
|
384
|
+
*/
|
|
385
|
+
static createPassthroughSchema() {
|
|
555
386
|
return {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
387
|
+
"~standard": {
|
|
388
|
+
vendor: "@igniter-js/mail",
|
|
389
|
+
version: 1,
|
|
390
|
+
validate: async (value) => ({ value })
|
|
391
|
+
}
|
|
559
392
|
};
|
|
560
393
|
}
|
|
561
394
|
};
|
|
562
395
|
|
|
563
|
-
// src/
|
|
564
|
-
|
|
565
|
-
const standard = schema?.["~standard"];
|
|
566
|
-
if (!standard?.validate) {
|
|
567
|
-
return input;
|
|
568
|
-
}
|
|
569
|
-
const result = await standard.validate(input);
|
|
570
|
-
if (result?.issues?.length) {
|
|
571
|
-
throw new IgniterMailError({
|
|
572
|
-
code: "MAIL_PROVIDER_TEMPLATE_DATA_INVALID",
|
|
573
|
-
message: "Invalid mail template payload",
|
|
574
|
-
statusCode: 400,
|
|
575
|
-
details: result.issues
|
|
576
|
-
});
|
|
577
|
-
}
|
|
578
|
-
return result?.value ?? input;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
// src/core/igniter-mail.tsx
|
|
582
|
-
var _IgniterMail = class _IgniterMail {
|
|
396
|
+
// src/core/manager.tsx
|
|
397
|
+
var IgniterMailManagerCore = class {
|
|
583
398
|
constructor(options) {
|
|
584
399
|
this.queueJobRegistered = false;
|
|
585
400
|
/**
|
|
586
401
|
* Type inference helper.
|
|
587
402
|
* Access via `typeof mail.$Infer` (type-level only).
|
|
588
403
|
*/
|
|
589
|
-
this.$Infer =
|
|
590
|
-
const { adapter, templates, logger, queue, ...rest } = options;
|
|
404
|
+
this.$Infer = {};
|
|
405
|
+
const { adapter, templates, logger, telemetry, queue, ...rest } = options;
|
|
591
406
|
if (!adapter) {
|
|
592
407
|
throw new IgniterMailError({
|
|
593
408
|
code: "MAIL_PROVIDER_ADAPTER_REQUIRED",
|
|
@@ -605,6 +420,7 @@ var _IgniterMail = class _IgniterMail {
|
|
|
605
420
|
this.adapter = adapter;
|
|
606
421
|
this.templates = templates;
|
|
607
422
|
this.logger = logger;
|
|
423
|
+
this.telemetry = telemetry;
|
|
608
424
|
this.queue = queue;
|
|
609
425
|
this.options = rest;
|
|
610
426
|
}
|
|
@@ -618,30 +434,20 @@ var _IgniterMail = class _IgniterMail {
|
|
|
618
434
|
}
|
|
619
435
|
this.queueJobRegistering = (async () => {
|
|
620
436
|
const queueOptions = queue.options;
|
|
621
|
-
const
|
|
622
|
-
|
|
623
|
-
"
|
|
624
|
-
vendor: "@igniter-js/mail",
|
|
625
|
-
version: 1,
|
|
626
|
-
validate: async (value) => ({ value })
|
|
627
|
-
}
|
|
628
|
-
};
|
|
629
|
-
const definition = queue.adapter.register({
|
|
630
|
-
name,
|
|
437
|
+
const passthroughSchema = IgniterMailSchema.createPassthroughSchema();
|
|
438
|
+
queue.adapter.register({
|
|
439
|
+
name: queueOptions?.job ?? "send",
|
|
631
440
|
input: passthroughSchema,
|
|
632
|
-
handler: async ({ input }) => {
|
|
633
|
-
await this.send(input);
|
|
634
|
-
},
|
|
635
|
-
queue: queueOptions?.queue,
|
|
636
441
|
attempts: queueOptions?.attempts,
|
|
637
442
|
priority: queueOptions?.priority,
|
|
638
443
|
removeOnComplete: queueOptions?.removeOnComplete,
|
|
639
444
|
removeOnFail: queueOptions?.removeOnFail,
|
|
640
445
|
metadata: queueOptions?.metadata,
|
|
641
|
-
limiter: queueOptions?.limiter
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
446
|
+
limiter: queueOptions?.limiter,
|
|
447
|
+
queue: { name: queueOptions?.queue ?? "mail" },
|
|
448
|
+
handler: async ({ input }) => {
|
|
449
|
+
await this.send(input);
|
|
450
|
+
}
|
|
645
451
|
});
|
|
646
452
|
this.queueJobRegistered = true;
|
|
647
453
|
})();
|
|
@@ -653,7 +459,7 @@ var _IgniterMail = class _IgniterMail {
|
|
|
653
459
|
}
|
|
654
460
|
async validateTemplateData(template, data) {
|
|
655
461
|
try {
|
|
656
|
-
return await
|
|
462
|
+
return await IgniterMailSchema.validateInput(
|
|
657
463
|
template.schema,
|
|
658
464
|
data
|
|
659
465
|
);
|
|
@@ -672,11 +478,20 @@ var _IgniterMail = class _IgniterMail {
|
|
|
672
478
|
* Sends an email immediately.
|
|
673
479
|
*/
|
|
674
480
|
async send(params) {
|
|
481
|
+
const startTime = Date.now();
|
|
675
482
|
try {
|
|
676
483
|
this.logger?.debug("IgniterMail.send started", {
|
|
677
484
|
to: params.to,
|
|
678
485
|
template: String(params.template)
|
|
679
486
|
});
|
|
487
|
+
this.telemetry?.emit(IGNITER_MAIL_TELEMETRY_EVENTS.SEND_STARTED, {
|
|
488
|
+
level: "debug",
|
|
489
|
+
attributes: {
|
|
490
|
+
"mail.to": params.to,
|
|
491
|
+
"mail.template": String(params.template),
|
|
492
|
+
"mail.subject": params.subject
|
|
493
|
+
}
|
|
494
|
+
});
|
|
680
495
|
await this.onSendStarted(params);
|
|
681
496
|
const template = this.templates[params.template];
|
|
682
497
|
if (!template) {
|
|
@@ -705,9 +520,20 @@ var _IgniterMail = class _IgniterMail {
|
|
|
705
520
|
text
|
|
706
521
|
});
|
|
707
522
|
await this.onSendSuccess(params);
|
|
523
|
+
const durationMs = Date.now() - startTime;
|
|
524
|
+
this.telemetry?.emit(IGNITER_MAIL_TELEMETRY_EVENTS.SEND_SUCCESS, {
|
|
525
|
+
level: "info",
|
|
526
|
+
attributes: {
|
|
527
|
+
"mail.to": params.to,
|
|
528
|
+
"mail.template": String(params.template),
|
|
529
|
+
"mail.subject": params.subject || template.subject,
|
|
530
|
+
"mail.duration_ms": durationMs
|
|
531
|
+
}
|
|
532
|
+
});
|
|
708
533
|
this.logger?.info("IgniterMail.send success", {
|
|
709
534
|
to: params.to,
|
|
710
|
-
template: String(params.template)
|
|
535
|
+
template: String(params.template),
|
|
536
|
+
durationMs
|
|
711
537
|
});
|
|
712
538
|
} catch (error) {
|
|
713
539
|
const normalizedError = IgniterMailError.is(error) ? error : new IgniterMailError({
|
|
@@ -720,6 +546,18 @@ var _IgniterMail = class _IgniterMail {
|
|
|
720
546
|
template: String(params.template)
|
|
721
547
|
}
|
|
722
548
|
});
|
|
549
|
+
const durationMs = Date.now() - startTime;
|
|
550
|
+
this.telemetry?.emit(IGNITER_MAIL_TELEMETRY_EVENTS.SEND_ERROR, {
|
|
551
|
+
level: "error",
|
|
552
|
+
attributes: {
|
|
553
|
+
"mail.to": params.to,
|
|
554
|
+
"mail.template": String(params.template),
|
|
555
|
+
"mail.subject": params.subject,
|
|
556
|
+
"mail.error.code": normalizedError.code,
|
|
557
|
+
"mail.error.message": normalizedError.message,
|
|
558
|
+
"mail.duration_ms": durationMs
|
|
559
|
+
}
|
|
560
|
+
});
|
|
723
561
|
this.logger?.error("IgniterMail.send failed", normalizedError);
|
|
724
562
|
await this.onSendError(params, normalizedError);
|
|
725
563
|
throw normalizedError;
|
|
@@ -728,10 +566,10 @@ var _IgniterMail = class _IgniterMail {
|
|
|
728
566
|
/**
|
|
729
567
|
* Schedules an email for a future date.
|
|
730
568
|
*
|
|
731
|
-
*
|
|
732
|
-
* Otherwise, it uses a best-effort `setTimeout`.
|
|
569
|
+
* Requires a queue adapter; otherwise it throws.
|
|
733
570
|
*/
|
|
734
571
|
async schedule(params, date) {
|
|
572
|
+
const startTime = Date.now();
|
|
735
573
|
if (date.getTime() <= Date.now()) {
|
|
736
574
|
throw new IgniterMailError({
|
|
737
575
|
code: "MAIL_PROVIDER_SCHEDULE_DATE_INVALID",
|
|
@@ -739,24 +577,60 @@ var _IgniterMail = class _IgniterMail {
|
|
|
739
577
|
logger: this.logger
|
|
740
578
|
});
|
|
741
579
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
580
|
+
const delay = Math.max(0, date.getTime() - Date.now());
|
|
581
|
+
this.logger?.debug("IgniterMail.schedule started", {
|
|
582
|
+
to: params.to,
|
|
583
|
+
template: String(params.template),
|
|
584
|
+
scheduledAt: date.toISOString(),
|
|
585
|
+
delayMs: delay
|
|
586
|
+
});
|
|
587
|
+
this.telemetry?.emit(IGNITER_MAIL_TELEMETRY_EVENTS.SCHEDULE_STARTED, {
|
|
588
|
+
level: "debug",
|
|
589
|
+
attributes: {
|
|
590
|
+
"mail.to": params.to,
|
|
591
|
+
"mail.template": String(params.template),
|
|
592
|
+
"mail.scheduled_at": date.toISOString(),
|
|
593
|
+
"mail.delay_ms": delay
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
if (!this.queue) {
|
|
597
|
+
throw new IgniterMailError({
|
|
598
|
+
code: "MAIL_PROVIDER_SCHEDULE_QUEUE_NOT_CONFIGURED",
|
|
599
|
+
message: "MAIL_PROVIDER_SCHEDULE_QUEUE_NOT_CONFIGURED",
|
|
600
|
+
logger: this.logger
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
try {
|
|
604
|
+
await this.ensureQueueJobRegistered();
|
|
605
|
+
const queue = this.queue.options?.queue ?? "mail";
|
|
606
|
+
const job = this.queue.options?.job ?? "send";
|
|
607
|
+
const id = `${queue}.${job}`;
|
|
608
|
+
this.logger?.info("IgniterMail.schedule enqueued", {
|
|
609
|
+
to: params.to,
|
|
610
|
+
template: String(params.template),
|
|
611
|
+
delay,
|
|
612
|
+
durationMs: Date.now() - startTime
|
|
613
|
+
});
|
|
614
|
+
await this.queue.adapter.invoke({
|
|
615
|
+
id,
|
|
616
|
+
input: params,
|
|
617
|
+
delay
|
|
618
|
+
});
|
|
619
|
+
this.telemetry?.emit(IGNITER_MAIL_TELEMETRY_EVENTS.SCHEDULE_SUCCESS, {
|
|
620
|
+
level: "info",
|
|
621
|
+
attributes: {
|
|
622
|
+
"mail.to": params.to,
|
|
623
|
+
"mail.template": String(params.template),
|
|
624
|
+
"mail.scheduled_at": date.toISOString(),
|
|
625
|
+
"mail.delay_ms": delay,
|
|
626
|
+
"mail.queue_id": this.queue.options?.queue ?? "mail"
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
return;
|
|
630
|
+
} catch (error) {
|
|
631
|
+
let normalizedError = error;
|
|
632
|
+
if (!IgniterMailError.is(error)) {
|
|
633
|
+
normalizedError = new IgniterMailError({
|
|
760
634
|
code: "MAIL_PROVIDER_SCHEDULE_FAILED",
|
|
761
635
|
message: "MAIL_PROVIDER_SCHEDULE_FAILED",
|
|
762
636
|
cause: error,
|
|
@@ -764,19 +638,23 @@ var _IgniterMail = class _IgniterMail {
|
|
|
764
638
|
metadata: {
|
|
765
639
|
to: params.to,
|
|
766
640
|
template: String(params.template),
|
|
767
|
-
|
|
641
|
+
scheduledAt: date.toISOString()
|
|
768
642
|
}
|
|
769
643
|
});
|
|
770
|
-
this.logger?.error("IgniterMail.schedule failed", normalizedError);
|
|
771
|
-
throw normalizedError;
|
|
772
644
|
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
645
|
+
this.telemetry?.emit(IGNITER_MAIL_TELEMETRY_EVENTS.SCHEDULE_ERROR, {
|
|
646
|
+
level: "error",
|
|
647
|
+
attributes: {
|
|
648
|
+
"mail.to": params.to,
|
|
649
|
+
"mail.template": String(params.template),
|
|
650
|
+
"mail.scheduled_at": date.toISOString(),
|
|
651
|
+
"mail.error.code": normalizedError.code,
|
|
652
|
+
"mail.error.message": normalizedError.message
|
|
653
|
+
}
|
|
778
654
|
});
|
|
779
|
-
|
|
655
|
+
this.logger?.error("IgniterMail.schedule failed", normalizedError);
|
|
656
|
+
throw normalizedError;
|
|
657
|
+
}
|
|
780
658
|
}
|
|
781
659
|
async onSendStarted(params) {
|
|
782
660
|
await this.options.onSendStarted?.(params);
|
|
@@ -788,93 +666,233 @@ var _IgniterMail = class _IgniterMail {
|
|
|
788
666
|
await this.options.onSendSuccess?.(params);
|
|
789
667
|
}
|
|
790
668
|
};
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
669
|
+
|
|
670
|
+
// src/builders/main.builder.ts
|
|
671
|
+
var IgniterMailBuilder = class _IgniterMailBuilder {
|
|
672
|
+
constructor(options) {
|
|
673
|
+
this.templates = {};
|
|
674
|
+
this.from = options.from;
|
|
675
|
+
this.adapter = options.adapter;
|
|
676
|
+
this.templates = options.templates;
|
|
677
|
+
this.logger = options.logger;
|
|
678
|
+
this.telemetry = options.telemetry;
|
|
679
|
+
this.onSendStartedHandler = options.onSendStarted;
|
|
680
|
+
this.onSendErrorHandler = options.onSendError;
|
|
681
|
+
this.onSendSuccessHandler = options.onSendSuccess;
|
|
682
|
+
if (options.queue) {
|
|
683
|
+
this.queue = {
|
|
684
|
+
adapter: options.queue.adapter,
|
|
685
|
+
options: options.queue.options
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Creates a new builder.
|
|
691
|
+
*/
|
|
692
|
+
static create() {
|
|
693
|
+
return new _IgniterMailBuilder({ from: "", adapter: {}, templates: {} });
|
|
694
|
+
}
|
|
695
|
+
/** Sets the default FROM address. */
|
|
696
|
+
withFrom(from) {
|
|
697
|
+
this.from = from;
|
|
698
|
+
return this;
|
|
699
|
+
}
|
|
700
|
+
/** Attaches a logger instance. */
|
|
701
|
+
withLogger(logger) {
|
|
702
|
+
this.logger = logger;
|
|
703
|
+
return this;
|
|
704
|
+
}
|
|
705
|
+
/** Attaches a telemetry instance for observability. */
|
|
706
|
+
withTelemetry(telemetry) {
|
|
707
|
+
this.telemetry = telemetry;
|
|
708
|
+
return this;
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Enables queue delivery.
|
|
712
|
+
*
|
|
713
|
+
* When configured, `IgniterMail.schedule()` will enqueue jobs using the queue adapter.
|
|
714
|
+
*/
|
|
715
|
+
withQueue(adapter, options) {
|
|
716
|
+
this.queue = { adapter, options };
|
|
717
|
+
return this;
|
|
718
|
+
}
|
|
719
|
+
withAdapter(adapterOrProvider, secret) {
|
|
720
|
+
if (typeof adapterOrProvider === "string") {
|
|
721
|
+
if (!secret) {
|
|
722
|
+
throw new IgniterMailError({
|
|
723
|
+
code: "MAIL_PROVIDER_ADAPTER_SECRET_REQUIRED",
|
|
724
|
+
message: "MAIL_PROVIDER_ADAPTER_SECRET_REQUIRED",
|
|
725
|
+
logger: this.logger
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
switch (adapterOrProvider) {
|
|
729
|
+
case "resend":
|
|
730
|
+
this.adapter = ResendMailAdapter.create({
|
|
731
|
+
secret,
|
|
732
|
+
from: this.from
|
|
733
|
+
});
|
|
734
|
+
return this;
|
|
735
|
+
case "smtp":
|
|
736
|
+
this.adapter = SmtpMailAdapter.create({
|
|
737
|
+
secret,
|
|
738
|
+
from: this.from
|
|
739
|
+
});
|
|
740
|
+
return this;
|
|
741
|
+
case "postmark":
|
|
742
|
+
this.adapter = PostmarkMailAdapter.create({
|
|
743
|
+
secret,
|
|
744
|
+
from: this.from
|
|
745
|
+
});
|
|
746
|
+
return this;
|
|
747
|
+
case "sendgrid":
|
|
748
|
+
this.adapter = SendGridMailAdapter.create({
|
|
749
|
+
secret,
|
|
750
|
+
from: this.from
|
|
751
|
+
});
|
|
752
|
+
return this;
|
|
753
|
+
default:
|
|
754
|
+
throw new IgniterMailError({
|
|
755
|
+
code: "MAIL_PROVIDER_ADAPTER_NOT_FOUND",
|
|
756
|
+
message: `MAIL_PROVIDER_ADAPTER_NOT_FOUND: ${adapterOrProvider}`,
|
|
757
|
+
logger: this.logger,
|
|
758
|
+
metadata: {
|
|
759
|
+
provider: adapterOrProvider
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
this.adapter = adapterOrProvider;
|
|
765
|
+
return this;
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Registers a template.
|
|
769
|
+
*/
|
|
770
|
+
addTemplate(key, template) {
|
|
771
|
+
return new _IgniterMailBuilder({
|
|
772
|
+
from: this.from,
|
|
773
|
+
adapter: this.adapter,
|
|
774
|
+
templates: {
|
|
775
|
+
...this.templates,
|
|
776
|
+
[key]: template
|
|
777
|
+
},
|
|
778
|
+
logger: this.logger,
|
|
779
|
+
telemetry: this.telemetry,
|
|
780
|
+
onSendStarted: this.onSendStartedHandler,
|
|
781
|
+
onSendError: this.onSendErrorHandler,
|
|
782
|
+
onSendSuccess: this.onSendSuccessHandler,
|
|
783
|
+
queue: this.queue ? {
|
|
784
|
+
adapter: this.queue.adapter,
|
|
785
|
+
options: this.queue.options
|
|
786
|
+
} : void 0
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
/** Hook invoked before sending. */
|
|
790
|
+
onSendStarted(handler) {
|
|
791
|
+
this.onSendStartedHandler = handler;
|
|
792
|
+
return this;
|
|
793
|
+
}
|
|
794
|
+
/** Hook invoked on error. */
|
|
795
|
+
onSendError(handler) {
|
|
796
|
+
this.onSendErrorHandler = handler;
|
|
797
|
+
return this;
|
|
798
|
+
}
|
|
799
|
+
/** Hook invoked on success. */
|
|
800
|
+
onSendSuccess(handler) {
|
|
801
|
+
this.onSendSuccessHandler = handler;
|
|
802
|
+
return this;
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Builds the {@link IgniterMail} instance.
|
|
806
|
+
*/
|
|
807
|
+
build() {
|
|
808
|
+
if (!this.from) {
|
|
809
|
+
throw new IgniterMailError({
|
|
810
|
+
code: "MAIL_PROVIDER_FROM_REQUIRED",
|
|
811
|
+
message: "MAIL_PROVIDER_FROM_REQUIRED",
|
|
812
|
+
logger: this.logger
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
if (!this.adapter) {
|
|
816
|
+
throw new IgniterMailError({
|
|
817
|
+
code: "MAIL_PROVIDER_ADAPTER_REQUIRED",
|
|
818
|
+
message: "MAIL_PROVIDER_ADAPTER_REQUIRED",
|
|
819
|
+
logger: this.logger
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
return new IgniterMailManagerCore({
|
|
823
|
+
from: this.from,
|
|
824
|
+
adapter: this.adapter,
|
|
825
|
+
templates: this.templates,
|
|
826
|
+
onSendStarted: this.onSendStartedHandler,
|
|
827
|
+
onSendError: this.onSendErrorHandler,
|
|
828
|
+
onSendSuccess: this.onSendSuccessHandler,
|
|
829
|
+
telemetry: this.telemetry,
|
|
830
|
+
logger: this.logger,
|
|
831
|
+
queue: this.queue ? {
|
|
832
|
+
adapter: this.queue.adapter,
|
|
833
|
+
options: this.queue.options
|
|
834
|
+
} : void 0
|
|
818
835
|
});
|
|
819
|
-
return _IgniterMail.instance;
|
|
820
836
|
}
|
|
821
|
-
_IgniterMail.instance = new _IgniterMail(
|
|
822
|
-
options
|
|
823
|
-
);
|
|
824
|
-
return _IgniterMail.instance;
|
|
825
837
|
};
|
|
826
|
-
var IgniterMail =
|
|
838
|
+
var IgniterMail = IgniterMailBuilder;
|
|
827
839
|
|
|
828
|
-
// src/
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
840
|
+
// src/builders/template.builder.ts
|
|
841
|
+
var IgniterMailTemplateBuilder = class _IgniterMailTemplateBuilder {
|
|
842
|
+
/** Creates a new builder instance. */
|
|
843
|
+
static create() {
|
|
844
|
+
return new _IgniterMailTemplateBuilder();
|
|
845
|
+
}
|
|
846
|
+
/** Sets the default subject for the template. */
|
|
847
|
+
withSubject(subject) {
|
|
848
|
+
this.subject = subject;
|
|
849
|
+
return this;
|
|
850
|
+
}
|
|
851
|
+
/** Attaches the schema used to validate and infer payload types. */
|
|
852
|
+
withSchema(schema) {
|
|
853
|
+
this.schema = schema;
|
|
854
|
+
return this;
|
|
855
|
+
}
|
|
856
|
+
/** Sets the React Email render function for the template. */
|
|
857
|
+
withRender(render2) {
|
|
858
|
+
this.render = render2;
|
|
859
|
+
return this;
|
|
860
|
+
}
|
|
861
|
+
/** Builds the template definition. */
|
|
862
|
+
build() {
|
|
863
|
+
if (!this.subject) {
|
|
864
|
+
throw new IgniterMailError({
|
|
865
|
+
code: "MAIL_TEMPLATE_CONFIGURATION_INVALID",
|
|
866
|
+
message: "Mail template subject is required"
|
|
867
|
+
});
|
|
835
868
|
}
|
|
836
|
-
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
// src/utils/get-adapter.ts
|
|
840
|
-
var getAdapter = (adapter) => {
|
|
841
|
-
switch (adapter) {
|
|
842
|
-
case "resend":
|
|
843
|
-
return resendAdapter;
|
|
844
|
-
case "smtp":
|
|
845
|
-
return smtpAdapter;
|
|
846
|
-
case "postmark":
|
|
847
|
-
return postmarkAdapter;
|
|
848
|
-
case "sendgrid":
|
|
849
|
-
return sendgridAdapter;
|
|
850
|
-
case "webhook":
|
|
851
|
-
return webhookAdapter;
|
|
852
|
-
default:
|
|
869
|
+
if (!this.schema) {
|
|
853
870
|
throw new IgniterMailError({
|
|
854
|
-
code: "
|
|
855
|
-
message:
|
|
856
|
-
metadata: { adapter }
|
|
871
|
+
code: "MAIL_TEMPLATE_CONFIGURATION_INVALID",
|
|
872
|
+
message: "Mail template schema is required"
|
|
857
873
|
});
|
|
874
|
+
}
|
|
875
|
+
if (!this.render) {
|
|
876
|
+
throw new IgniterMailError({
|
|
877
|
+
code: "MAIL_TEMPLATE_CONFIGURATION_INVALID",
|
|
878
|
+
message: "Mail template render is required"
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
return {
|
|
882
|
+
subject: this.subject,
|
|
883
|
+
schema: this.schema,
|
|
884
|
+
render: this.render
|
|
885
|
+
};
|
|
858
886
|
}
|
|
859
887
|
};
|
|
888
|
+
var IgniterMailTemplate = IgniterMailTemplateBuilder;
|
|
860
889
|
|
|
861
890
|
exports.IgniterMail = IgniterMail;
|
|
862
891
|
exports.IgniterMailBuilder = IgniterMailBuilder;
|
|
863
892
|
exports.IgniterMailError = IgniterMailError;
|
|
864
|
-
exports.
|
|
865
|
-
exports.
|
|
866
|
-
exports.
|
|
867
|
-
exports.
|
|
868
|
-
exports.SmtpMailAdapterBuilder = SmtpMailAdapterBuilder;
|
|
869
|
-
exports.WebhookMailAdapterBuilder = WebhookMailAdapterBuilder;
|
|
870
|
-
exports.createPassthroughSchema = createPassthroughSchema;
|
|
871
|
-
exports.createTestMailAdapter = createTestMailAdapter;
|
|
872
|
-
exports.getAdapter = getAdapter;
|
|
873
|
-
exports.postmarkAdapter = postmarkAdapter;
|
|
874
|
-
exports.resendAdapter = resendAdapter;
|
|
875
|
-
exports.sendgridAdapter = sendgridAdapter;
|
|
876
|
-
exports.smtpAdapter = smtpAdapter;
|
|
877
|
-
exports.validateStandardSchemaInput = validateStandardSchemaInput;
|
|
878
|
-
exports.webhookAdapter = webhookAdapter;
|
|
893
|
+
exports.IgniterMailManagerCore = IgniterMailManagerCore;
|
|
894
|
+
exports.IgniterMailSchema = IgniterMailSchema;
|
|
895
|
+
exports.IgniterMailTemplate = IgniterMailTemplate;
|
|
896
|
+
exports.IgniterMailTemplateBuilder = IgniterMailTemplateBuilder;
|
|
879
897
|
//# sourceMappingURL=index.js.map
|
|
880
898
|
//# sourceMappingURL=index.js.map
|