@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.
@@ -0,0 +1,361 @@
1
+ import { IgniterError } from '@igniter-js/core';
2
+ import { Resend } from 'resend';
3
+ import nodemailer from 'nodemailer';
4
+
5
+ // src/errors/mail.error.ts
6
+ var IgniterMailError = class _IgniterMailError extends IgniterError {
7
+ constructor(payload) {
8
+ super({
9
+ code: payload.code,
10
+ message: payload.message,
11
+ statusCode: payload.statusCode,
12
+ causer: "@igniter-js/mail",
13
+ details: payload.details,
14
+ metadata: payload.metadata,
15
+ logger: payload.logger
16
+ });
17
+ this.name = "IgniterMailError";
18
+ }
19
+ /**
20
+ * Type guard utility.
21
+ */
22
+ static is(error) {
23
+ return error instanceof _IgniterMailError;
24
+ }
25
+ };
26
+
27
+ // src/adapters/postmark.adapter.ts
28
+ var PostmarkMailAdapter = class _PostmarkMailAdapter {
29
+ /**
30
+ * Creates a new adapter instance.
31
+ *
32
+ * @param credentials - Provider credentials (secret/from).
33
+ * @throws {IgniterMailError} Does not throw on creation; errors surface on send.
34
+ */
35
+ constructor(credentials = {}) {
36
+ this.credentials = credentials;
37
+ }
38
+ /**
39
+ * Creates a new adapter instance.
40
+ *
41
+ * @param credentials - Provider credentials (secret/from).
42
+ * @returns A Postmark adapter instance.
43
+ * @throws {IgniterMailError} Does not throw on creation; errors surface on send.
44
+ * @example
45
+ * ```ts
46
+ * const adapter = PostmarkMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })
47
+ * ```
48
+ */
49
+ static create(credentials) {
50
+ return new _PostmarkMailAdapter(credentials);
51
+ }
52
+ /**
53
+ * Sends an email using Postmark (HTTP API).
54
+ *
55
+ * @param params - Normalized email parameters.
56
+ * @returns A promise that resolves when the email is accepted.
57
+ * @throws {IgniterMailError} When configuration is invalid or Postmark rejects the request.
58
+ * @example
59
+ * ```ts
60
+ * await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })
61
+ * ```
62
+ */
63
+ async send(params) {
64
+ if (!this.credentials.secret) {
65
+ throw new IgniterMailError({
66
+ code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
67
+ message: "Postmark adapter secret is required"
68
+ });
69
+ }
70
+ if (!this.credentials.from) {
71
+ throw new IgniterMailError({
72
+ code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
73
+ message: "Postmark adapter from is required"
74
+ });
75
+ }
76
+ const token = this.credentials.secret;
77
+ const from = this.credentials.from;
78
+ const response = await fetch("https://api.postmarkapp.com/email", {
79
+ method: "POST",
80
+ headers: {
81
+ Accept: "application/json",
82
+ "Content-Type": "application/json",
83
+ "X-Postmark-Server-Token": token
84
+ },
85
+ body: JSON.stringify({
86
+ From: from,
87
+ To: params.to,
88
+ Subject: params.subject,
89
+ HtmlBody: params.html,
90
+ TextBody: params.text
91
+ })
92
+ });
93
+ if (!response.ok) {
94
+ const body = await response.text().catch(() => "");
95
+ throw new IgniterMailError({
96
+ code: "MAIL_PROVIDER_SEND_FAILED",
97
+ message: "Postmark send failed",
98
+ metadata: {
99
+ status: response.status,
100
+ body
101
+ }
102
+ });
103
+ }
104
+ }
105
+ };
106
+ var ResendMailAdapter = class _ResendMailAdapter {
107
+ /**
108
+ * Creates an adapter with credentials.
109
+ *
110
+ * @param credentials - Adapter credentials including API secret and default from.
111
+ * @throws {IgniterMailError} Does not throw on creation; errors surface on send.
112
+ */
113
+ constructor(credentials = {}) {
114
+ this.credentials = credentials;
115
+ }
116
+ /**
117
+ * Creates a new adapter instance.
118
+ *
119
+ * @param credentials - Adapter credentials including API secret and default from.
120
+ * @returns A configured Resend adapter.
121
+ * @throws {IgniterMailError} Does not throw on creation; errors surface on send.
122
+ * @example
123
+ * ```ts
124
+ * const adapter = ResendMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })
125
+ * ```
126
+ */
127
+ static create(credentials) {
128
+ return new _ResendMailAdapter(credentials);
129
+ }
130
+ /**
131
+ * Sends an email using Resend.
132
+ *
133
+ * @param params - Email payload to send.
134
+ * @returns Resolves when the email is accepted by Resend.
135
+ * @throws {IgniterMailError} When credentials are missing or Resend rejects the request.
136
+ *
137
+ * @example
138
+ * ```ts
139
+ * await adapter.send({ to: 'user@example.com', subject: 'Welcome', html: '<p>Hi</p>', text: 'Hi' })
140
+ * ```
141
+ */
142
+ async send(params) {
143
+ if (!this.credentials.secret) {
144
+ throw new IgniterMailError({
145
+ code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
146
+ message: "Resend adapter secret is required"
147
+ });
148
+ }
149
+ if (!this.credentials.from) {
150
+ throw new IgniterMailError({
151
+ code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
152
+ message: "Resend adapter from is required"
153
+ });
154
+ }
155
+ const resend = new Resend(this.credentials.secret);
156
+ const from = this.credentials.from;
157
+ await resend.emails.create({
158
+ to: params.to,
159
+ from,
160
+ subject: params.subject,
161
+ html: params.html,
162
+ text: params.text,
163
+ scheduledAt: params.scheduledAt?.toISOString()
164
+ });
165
+ }
166
+ };
167
+
168
+ // src/adapters/sendgrid.adapter.ts
169
+ var SendGridMailAdapter = class _SendGridMailAdapter {
170
+ /**
171
+ * Creates an adapter with credentials.
172
+ *
173
+ * @param credentials - Adapter credentials including API secret and default from.
174
+ * @throws {IgniterMailError} Does not throw on creation; errors surface on send.
175
+ */
176
+ constructor(credentials = {}) {
177
+ this.credentials = credentials;
178
+ }
179
+ /**
180
+ * Creates a new adapter instance.
181
+ *
182
+ * @param credentials - Adapter credentials including API secret and default from.
183
+ * @returns A configured SendGrid adapter.
184
+ * @throws {IgniterMailError} Does not throw on creation; errors surface on send.
185
+ * @example
186
+ * ```ts
187
+ * const adapter = SendGridMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })
188
+ * ```
189
+ */
190
+ static create(credentials) {
191
+ return new _SendGridMailAdapter(credentials);
192
+ }
193
+ /**
194
+ * Sends an email using SendGrid (HTTP API).
195
+ *
196
+ * @param params - Email payload to send.
197
+ * @returns Resolves when the email is accepted by SendGrid.
198
+ * @throws {IgniterMailError} When credentials are missing or the API fails.
199
+ *
200
+ * @example
201
+ * ```ts
202
+ * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })
203
+ * ```
204
+ */
205
+ async send(params) {
206
+ if (!this.credentials.secret) {
207
+ throw new IgniterMailError({
208
+ code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
209
+ message: "SendGrid adapter secret is required"
210
+ });
211
+ }
212
+ if (!this.credentials.from) {
213
+ throw new IgniterMailError({
214
+ code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
215
+ message: "SendGrid adapter from is required"
216
+ });
217
+ }
218
+ const apiKey = this.credentials.secret;
219
+ const from = this.credentials.from;
220
+ const response = await fetch("https://api.sendgrid.com/v3/mail/send", {
221
+ method: "POST",
222
+ headers: {
223
+ Authorization: `Bearer ${apiKey}`,
224
+ "Content-Type": "application/json"
225
+ },
226
+ body: JSON.stringify({
227
+ personalizations: [
228
+ {
229
+ to: [{ email: params.to }]
230
+ }
231
+ ],
232
+ from: { email: from },
233
+ subject: params.subject,
234
+ content: [
235
+ { type: "text/plain", value: params.text },
236
+ { type: "text/html", value: params.html }
237
+ ]
238
+ })
239
+ });
240
+ if (!response.ok) {
241
+ const body = await response.text().catch(() => "");
242
+ throw new IgniterMailError({
243
+ code: "MAIL_PROVIDER_SEND_FAILED",
244
+ message: "SendGrid send failed",
245
+ metadata: {
246
+ status: response.status,
247
+ body
248
+ }
249
+ });
250
+ }
251
+ }
252
+ };
253
+ var SmtpMailAdapter = class _SmtpMailAdapter {
254
+ /**
255
+ * Creates an adapter with credentials.
256
+ *
257
+ * @param credentials - Adapter credentials including SMTP URL and default from.
258
+ * @throws {IgniterMailError} Does not throw on creation; errors surface on send.
259
+ */
260
+ constructor(credentials = {}) {
261
+ this.credentials = credentials;
262
+ }
263
+ /**
264
+ * Creates a new adapter instance.
265
+ *
266
+ * @param credentials - Adapter credentials including SMTP URL and default from.
267
+ * @returns A configured SMTP adapter.
268
+ * @throws {IgniterMailError} Does not throw on creation; errors surface on send.
269
+ * @example
270
+ * ```ts
271
+ * const adapter = SmtpMailAdapter.create({ secret: 'smtps://user:pass@host:465', from: 'no-reply@acme.com' })
272
+ * ```
273
+ */
274
+ static create(credentials) {
275
+ return new _SmtpMailAdapter(credentials);
276
+ }
277
+ /**
278
+ * Sends an email using Nodemailer over SMTP.
279
+ *
280
+ * @param params - Email payload to send.
281
+ * @returns Resolves when the email is sent.
282
+ * @throws {IgniterMailError} When credentials are missing or the SMTP send fails.
283
+ *
284
+ * @example
285
+ * ```ts
286
+ * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })
287
+ * ```
288
+ */
289
+ async send(params) {
290
+ if (!this.credentials.secret) {
291
+ throw new IgniterMailError({
292
+ code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
293
+ message: "SMTP adapter secret is required"
294
+ });
295
+ }
296
+ if (!this.credentials.from) {
297
+ throw new IgniterMailError({
298
+ code: "MAIL_ADAPTER_CONFIGURATION_INVALID",
299
+ message: "SMTP adapter from is required"
300
+ });
301
+ }
302
+ const smtpUrl = this.credentials.secret;
303
+ const from = this.credentials.from;
304
+ const transport = nodemailer.createTransport(smtpUrl, {
305
+ connectionTimeout: 1e4,
306
+ greetingTimeout: 5e3,
307
+ socketTimeout: 1e4,
308
+ tls: {
309
+ rejectUnauthorized: false
310
+ }
311
+ });
312
+ try {
313
+ await transport.sendMail({
314
+ from,
315
+ to: params.to,
316
+ subject: params.subject,
317
+ html: params.html,
318
+ text: params.text
319
+ });
320
+ } catch (error) {
321
+ throw new IgniterMailError({
322
+ code: "MAIL_PROVIDER_SEND_FAILED",
323
+ message: "SMTP send failed",
324
+ metadata: {
325
+ originalError: error
326
+ }
327
+ });
328
+ } finally {
329
+ transport.close();
330
+ }
331
+ }
332
+ };
333
+
334
+ // src/adapters/mock.adapter.ts
335
+ var MockMailAdapter = class _MockMailAdapter {
336
+ constructor() {
337
+ /** Tracks all send calls. */
338
+ this.sent = [];
339
+ /** Tracks method call counts. */
340
+ this.calls = {
341
+ send: 0
342
+ };
343
+ }
344
+ /** Creates a new mock adapter instance. */
345
+ static create() {
346
+ return new _MockMailAdapter();
347
+ }
348
+ async send(params) {
349
+ this.calls.send += 1;
350
+ this.sent.push(params);
351
+ }
352
+ /** Clears all tracked state. */
353
+ clear() {
354
+ this.sent.length = 0;
355
+ this.calls.send = 0;
356
+ }
357
+ };
358
+
359
+ export { MockMailAdapter, PostmarkMailAdapter, ResendMailAdapter, SendGridMailAdapter, SmtpMailAdapter };
360
+ //# sourceMappingURL=index.mjs.map
361
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/errors/mail.error.ts","../../src/adapters/postmark.adapter.ts","../../src/adapters/resend.adapter.ts","../../src/adapters/sendgrid.adapter.ts","../../src/adapters/smtp.adapter.ts","../../src/adapters/mock.adapter.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import { IgniterError, type IgniterLogger } from '@igniter-js/core'\n\n/**\n * Known error codes thrown by `@igniter-js/mail` runtime.\n */\nexport type IgniterMailErrorCode =\n | 'MAIL_PROVIDER_FROM_REQUIRED'\n | 'MAIL_PROVIDER_ADAPTER_REQUIRED'\n | 'MAIL_PROVIDER_ADAPTER_SECRET_REQUIRED'\n | 'MAIL_PROVIDER_ADAPTER_NOT_FOUND'\n | 'MAIL_PROVIDER_TEMPLATES_REQUIRED'\n | 'MAIL_PROVIDER_TEMPLATE_NOT_FOUND'\n | 'MAIL_PROVIDER_TEMPLATE_DATA_INVALID'\n | 'MAIL_PROVIDER_SCHEDULE_DATE_INVALID'\n | 'MAIL_PROVIDER_SEND_FAILED'\n | 'MAIL_PROVIDER_SCHEDULE_FAILED'\n | 'MAIL_ADAPTER_CONFIGURATION_INVALID'\n | 'MAIL_TEMPLATE_CONFIGURATION_INVALID'\n | 'MAIL_PROVIDER_SCHEDULE_QUEUE_NOT_CONFIGURED'\n\n/**\n * Payload used to create an {@link IgniterMailError}.\n */\nexport type IgniterMailErrorPayload = {\n /** Machine-readable error code. */\n code: IgniterMailErrorCode\n /** Human-readable message. */\n message: string\n /** Optional HTTP status code when surfacing errors through HTTP boundaries. */\n statusCode?: number\n /** Optional original cause. */\n cause?: unknown\n /** Extra diagnostic details (e.g. schema issues). */\n details?: unknown\n /** Arbitrary metadata for debugging. */\n metadata?: Record<string, unknown>\n /** Optional logger used by IgniterError. */\n logger?: IgniterLogger\n}\n\n/**\n * Typed error used by the `IgniterMail` runtime.\n *\n * This is designed to be stable for extraction into `@igniter-js/mail`.\n */\nexport class IgniterMailError extends IgniterError {\n declare readonly code: IgniterMailErrorCode\n\n constructor(payload: IgniterMailErrorPayload) {\n super({\n code: payload.code,\n message: payload.message,\n statusCode: payload.statusCode,\n causer: '@igniter-js/mail',\n details: payload.details,\n metadata: payload.metadata,\n logger: payload.logger,\n })\n\n this.name = 'IgniterMailError'\n }\n\n /**\n * Type guard utility.\n */\n static is(error: unknown): error is IgniterMailError {\n return error instanceof IgniterMailError\n }\n}\n\n","import { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * Postmark adapter.\n *\n * Notes:\n * - This implementation uses `fetch` (no SDK dependency).\n * - Designed to be extracted to `@igniter-js/mail/adapters/postmark`.\n *\n * @example\n * ```ts\n * const adapter = PostmarkMailAdapter.create({\n * secret: process.env.POSTMARK_SERVER_TOKEN,\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class PostmarkMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Provider credentials (secret/from).\n * @returns A Postmark adapter instance.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = PostmarkMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new PostmarkMailAdapter(credentials)\n }\n\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Provider credentials (secret/from).\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using Postmark (HTTP API).\n *\n * @param params - Normalized email parameters.\n * @returns A promise that resolves when the email is accepted.\n * @throws {IgniterMailError} When configuration is invalid or Postmark rejects the request.\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams) {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Postmark adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Postmark adapter from is required',\n })\n }\n\n const token = this.credentials.secret\n const from = this.credentials.from\n\n const response = await fetch('https://api.postmarkapp.com/email', {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n 'X-Postmark-Server-Token': token,\n },\n body: JSON.stringify({\n From: from,\n To: params.to,\n Subject: params.subject,\n HtmlBody: params.html,\n TextBody: params.text,\n }),\n })\n\n if (!response.ok) {\n const body = await response.text().catch(() => '')\n\n throw new IgniterMailError({\n code: 'MAIL_PROVIDER_SEND_FAILED',\n message: 'Postmark send failed',\n metadata: {\n status: response.status,\n body,\n },\n })\n }\n }\n}","import { Resend } from 'resend'\n\nimport { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * Resend adapter implementation.\n *\n * Notes:\n * - Uses the Resend SDK.\n * - Designed to be extracted to `@igniter-js/mail/adapters/resend`.\n *\n * @example\n * ```ts\n * const adapter = ResendMailAdapter.create({\n * secret: process.env.RESEND_API_KEY,\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class ResendMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @returns A configured Resend adapter.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = ResendMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new ResendMailAdapter(credentials)\n }\n\n /**\n * Creates an adapter with credentials.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using Resend.\n *\n * @param params - Email payload to send.\n * @returns Resolves when the email is accepted by Resend.\n * @throws {IgniterMailError} When credentials are missing or Resend rejects the request.\n *\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Welcome', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Resend adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Resend adapter from is required',\n })\n }\n\n const resend = new Resend(this.credentials.secret)\n const from = this.credentials.from\n\n await resend.emails.create({\n to: params.to,\n from,\n subject: params.subject,\n html: params.html,\n text: params.text,\n scheduledAt: params.scheduledAt?.toISOString(),\n })\n }\n}\n","import { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * SendGrid adapter implementation.\n *\n * Notes:\n * - This implementation uses `fetch` (no SDK dependency).\n * - Designed to be extracted to `@igniter-js/mail/adapters/sendgrid`.\n *\n * @example\n * ```ts\n * const adapter = SendGridMailAdapter.create({\n * secret: process.env.SENDGRID_API_KEY,\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class SendGridMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @returns A configured SendGrid adapter.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = SendGridMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new SendGridMailAdapter(credentials)\n }\n\n /**\n * Creates an adapter with credentials.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using SendGrid (HTTP API).\n *\n * @param params - Email payload to send.\n * @returns Resolves when the email is accepted by SendGrid.\n * @throws {IgniterMailError} When credentials are missing or the API fails.\n *\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SendGrid adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SendGrid adapter from is required',\n })\n }\n\n const apiKey = this.credentials.secret\n const from = this.credentials.from\n\n const response = await fetch('https://api.sendgrid.com/v3/mail/send', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n personalizations: [\n {\n to: [{ email: params.to }],\n },\n ],\n from: { email: from },\n subject: params.subject,\n content: [\n { type: 'text/plain', value: params.text },\n { type: 'text/html', value: params.html },\n ],\n }),\n })\n\n if (!response.ok) {\n const body = await response.text().catch(() => '')\n\n throw new IgniterMailError({\n code: 'MAIL_PROVIDER_SEND_FAILED',\n message: 'SendGrid send failed',\n metadata: {\n status: response.status,\n body,\n },\n })\n }\n }\n}\n","import nodemailer from 'nodemailer'\n\nimport { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * SMTP adapter implementation.\n *\n * Notes:\n * - Uses Nodemailer.\n * - `credentials.secret` must be an SMTP connection URL.\n *\n * @example\n * ```ts\n * const adapter = SmtpMailAdapter.create({\n * secret: 'smtps://user:pass@host:465',\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class SmtpMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Adapter credentials including SMTP URL and default from.\n * @returns A configured SMTP adapter.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = SmtpMailAdapter.create({ secret: 'smtps://user:pass@host:465', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new SmtpMailAdapter(credentials)\n }\n\n /**\n * Creates an adapter with credentials.\n *\n * @param credentials - Adapter credentials including SMTP URL and default from.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using Nodemailer over SMTP.\n *\n * @param params - Email payload to send.\n * @returns Resolves when the email is sent.\n * @throws {IgniterMailError} When credentials are missing or the SMTP send fails.\n *\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SMTP adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SMTP adapter from is required',\n })\n }\n\n const smtpUrl = this.credentials.secret\n const from = this.credentials.from\n\n const transport = nodemailer.createTransport(smtpUrl, {\n connectionTimeout: 10000,\n greetingTimeout: 5000,\n socketTimeout: 10000,\n tls: {\n rejectUnauthorized: false,\n },\n })\n\n try {\n await transport.sendMail({\n from,\n to: params.to,\n subject: params.subject,\n html: params.html,\n text: params.text,\n })\n } catch(error) {\n throw new IgniterMailError({\n code: 'MAIL_PROVIDER_SEND_FAILED',\n message: 'SMTP send failed',\n metadata: {\n originalError: error,\n },\n })\n } finally {\n transport.close()\n }\n }\n}\n","import type {\n IgniterMailAdapter,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * In-memory mock adapter for `@igniter-js/mail`.\n *\n * Use this in tests to avoid real provider calls.\n */\nexport class MockMailAdapter implements IgniterMailAdapter {\n /** Creates a new mock adapter instance. */\n static create(): MockMailAdapter {\n return new MockMailAdapter()\n }\n\n /** Tracks all send calls. */\n public readonly sent: IgniterMailAdapterSendParams[] = []\n\n /** Tracks method call counts. */\n public readonly calls = {\n send: 0,\n }\n\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n this.calls.send += 1\n this.sent.push(params)\n }\n\n /** Clears all tracked state. */\n clear(): void {\n this.sent.length = 0\n this.calls.send = 0\n }\n}\n"]}