@flusys/nestjs-email 7.1.0 → 8.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/config/index.d.ts +3 -3
  2. package/controllers/email-config.controller.d.ts +2 -2
  3. package/controllers/email-send.controller.d.ts +3 -3
  4. package/controllers/email-template.controller.d.ts +4 -4
  5. package/controllers/index.d.ts +3 -3
  6. package/docs/email-swagger.config.d.ts +2 -2
  7. package/docs/index.d.ts +1 -1
  8. package/dtos/email-config.dto.d.ts +1 -1
  9. package/dtos/email-send.dto.d.ts +1 -1
  10. package/dtos/index.d.ts +3 -3
  11. package/entities/email-config-with-company.entity.d.ts +1 -1
  12. package/entities/email-template-with-company.entity.d.ts +1 -1
  13. package/entities/index.d.ts +10 -10
  14. package/enums/index.d.ts +1 -1
  15. package/fesm/261.js +799 -0
  16. package/fesm/493.js +1723 -0
  17. package/fesm/chunks/99.js +12 -0
  18. package/fesm/config/index.js +104 -3
  19. package/fesm/controllers/index.js +396 -3
  20. package/fesm/docs/index.js +134 -1
  21. package/fesm/dtos/index.js +22 -3
  22. package/fesm/entities/index.js +409 -13
  23. package/fesm/enums/index.js +37 -1
  24. package/fesm/index.js +762 -10
  25. package/fesm/interfaces/index.js +18 -4
  26. package/fesm/modules/index.js +541 -1
  27. package/fesm/providers/index.js +523 -5
  28. package/fesm/runtime.js +134 -0
  29. package/fesm/services/index.js +17 -5
  30. package/index.d.ts +10 -10
  31. package/interfaces/email-config.interface.d.ts +2 -2
  32. package/interfaces/email-module-options.interface.d.ts +3 -3
  33. package/interfaces/email-template.interface.d.ts +1 -1
  34. package/interfaces/index.d.ts +4 -4
  35. package/modules/email.module.d.ts +2 -2
  36. package/modules/index.d.ts +1 -1
  37. package/package.json +27 -38
  38. package/providers/email-factory.service.d.ts +2 -2
  39. package/providers/email-provider.registry.d.ts +2 -2
  40. package/providers/index.d.ts +5 -5
  41. package/providers/mailgun-provider.d.ts +1 -1
  42. package/providers/sendgrid-provider.d.ts +1 -1
  43. package/providers/smtp-provider.d.ts +1 -1
  44. package/services/email-config.service.d.ts +3 -3
  45. package/services/email-datasource.provider.d.ts +3 -3
  46. package/services/email-provider-config.service.d.ts +7 -7
  47. package/services/email-send.service.d.ts +7 -7
  48. package/services/email-template.service.d.ts +6 -6
  49. package/services/index.d.ts +5 -5
  50. package/utils/index.d.ts +1 -1
  51. package/cjs/config/email.constants.js +0 -22
  52. package/cjs/config/event-actions.js +0 -34
  53. package/cjs/config/index.js +0 -20
  54. package/cjs/config/message-keys.js +0 -39
  55. package/cjs/controllers/email-config.controller.js +0 -111
  56. package/cjs/controllers/email-config.controller.spec.js +0 -85
  57. package/cjs/controllers/email-send.controller.js +0 -128
  58. package/cjs/controllers/email-send.controller.spec.js +0 -139
  59. package/cjs/controllers/email-template.controller.js +0 -142
  60. package/cjs/controllers/email-template.controller.spec.js +0 -89
  61. package/cjs/controllers/index.js +0 -20
  62. package/cjs/docs/email-swagger.config.js +0 -116
  63. package/cjs/docs/email-swagger.config.spec.js +0 -62
  64. package/cjs/docs/index.js +0 -11
  65. package/cjs/dtos/email-config.dto.js +0 -212
  66. package/cjs/dtos/email-send.dto.js +0 -354
  67. package/cjs/dtos/email-template.dto.js +0 -252
  68. package/cjs/dtos/index.js +0 -20
  69. package/cjs/entities/email-config-with-company.entity.js +0 -63
  70. package/cjs/entities/email-config.entity.js +0 -114
  71. package/cjs/entities/email-template-with-company.entity.js +0 -65
  72. package/cjs/entities/email-template.entity.js +0 -136
  73. package/cjs/entities/index.js +0 -56
  74. package/cjs/entities/index.spec.js +0 -18
  75. package/cjs/enums/email-provider-type.enum.js +0 -18
  76. package/cjs/enums/index.js +0 -18
  77. package/cjs/index.js +0 -28
  78. package/cjs/interfaces/email-config.interface.js +0 -4
  79. package/cjs/interfaces/email-module-options.interface.js +0 -4
  80. package/cjs/interfaces/email-provider.interface.js +0 -4
  81. package/cjs/interfaces/email-template.interface.js +0 -4
  82. package/cjs/interfaces/index.js +0 -21
  83. package/cjs/modules/email.module.js +0 -122
  84. package/cjs/modules/email.module.spec.js +0 -142
  85. package/cjs/modules/index.js +0 -18
  86. package/cjs/providers/email-factory.service.js +0 -113
  87. package/cjs/providers/email-factory.service.spec.js +0 -242
  88. package/cjs/providers/email-provider.registry.js +0 -41
  89. package/cjs/providers/email-provider.registry.spec.js +0 -58
  90. package/cjs/providers/index.js +0 -22
  91. package/cjs/providers/mailgun-provider.js +0 -119
  92. package/cjs/providers/mailgun-provider.spec.js +0 -244
  93. package/cjs/providers/sendgrid-provider.js +0 -129
  94. package/cjs/providers/sendgrid-provider.spec.js +0 -346
  95. package/cjs/providers/smtp-provider.js +0 -164
  96. package/cjs/providers/smtp-provider.spec.js +0 -327
  97. package/cjs/services/email-config.service.js +0 -71
  98. package/cjs/services/email-config.service.spec.js +0 -69
  99. package/cjs/services/email-datasource.provider.js +0 -84
  100. package/cjs/services/email-datasource.provider.spec.js +0 -135
  101. package/cjs/services/email-provider-config.service.js +0 -159
  102. package/cjs/services/email-provider-config.service.spec.js +0 -269
  103. package/cjs/services/email-send.service.js +0 -315
  104. package/cjs/services/email-send.service.spec.js +0 -614
  105. package/cjs/services/email-template.service.js +0 -144
  106. package/cjs/services/email-template.service.spec.js +0 -261
  107. package/cjs/services/index.js +0 -22
  108. package/cjs/utils/email-templates.util.js +0 -64
  109. package/cjs/utils/email-templates.util.spec.js +0 -61
  110. package/cjs/utils/index.js +0 -18
  111. package/fesm/config/email.constants.js +0 -4
  112. package/fesm/config/event-actions.js +0 -13
  113. package/fesm/config/message-keys.js +0 -18
  114. package/fesm/controllers/email-config.controller.js +0 -101
  115. package/fesm/controllers/email-config.controller.spec.js +0 -81
  116. package/fesm/controllers/email-send.controller.js +0 -118
  117. package/fesm/controllers/email-send.controller.spec.js +0 -135
  118. package/fesm/controllers/email-template.controller.js +0 -132
  119. package/fesm/controllers/email-template.controller.spec.js +0 -85
  120. package/fesm/docs/email-swagger.config.js +0 -109
  121. package/fesm/docs/email-swagger.config.spec.js +0 -58
  122. package/fesm/dtos/email-config.dto.js +0 -191
  123. package/fesm/dtos/email-send.dto.js +0 -324
  124. package/fesm/dtos/email-template.dto.js +0 -231
  125. package/fesm/entities/email-config-with-company.entity.js +0 -53
  126. package/fesm/entities/email-config.entity.js +0 -104
  127. package/fesm/entities/email-template-with-company.entity.js +0 -55
  128. package/fesm/entities/email-template.entity.js +0 -126
  129. package/fesm/entities/index.spec.js +0 -14
  130. package/fesm/enums/email-provider-type.enum.js +0 -8
  131. package/fesm/interfaces/email-config.interface.js +0 -1
  132. package/fesm/interfaces/email-module-options.interface.js +0 -1
  133. package/fesm/interfaces/email-provider.interface.js +0 -1
  134. package/fesm/interfaces/email-template.interface.js +0 -1
  135. package/fesm/modules/email.module.js +0 -112
  136. package/fesm/modules/email.module.spec.js +0 -138
  137. package/fesm/providers/email-factory.service.js +0 -103
  138. package/fesm/providers/email-factory.service.spec.js +0 -238
  139. package/fesm/providers/email-provider.registry.js +0 -31
  140. package/fesm/providers/email-provider.registry.spec.js +0 -54
  141. package/fesm/providers/mailgun-provider.js +0 -109
  142. package/fesm/providers/mailgun-provider.spec.js +0 -240
  143. package/fesm/providers/sendgrid-provider.js +0 -119
  144. package/fesm/providers/sendgrid-provider.spec.js +0 -342
  145. package/fesm/providers/smtp-provider.js +0 -113
  146. package/fesm/providers/smtp-provider.spec.js +0 -282
  147. package/fesm/services/email-config.service.js +0 -61
  148. package/fesm/services/email-config.service.spec.js +0 -65
  149. package/fesm/services/email-datasource.provider.js +0 -74
  150. package/fesm/services/email-datasource.provider.spec.js +0 -131
  151. package/fesm/services/email-provider-config.service.js +0 -149
  152. package/fesm/services/email-provider-config.service.spec.js +0 -265
  153. package/fesm/services/email-send.service.js +0 -305
  154. package/fesm/services/email-send.service.spec.js +0 -610
  155. package/fesm/services/email-template.service.js +0 -134
  156. package/fesm/services/email-template.service.spec.js +0 -257
  157. package/fesm/utils/email-templates.util.js +0 -47
  158. package/fesm/utils/email-templates.util.spec.js +0 -57
  159. package/fesm/utils/index.js +0 -1
@@ -1,346 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- const _common = require("@nestjs/common");
6
- const _sendgridprovider = require("./sendgrid-provider");
7
- // @sendgrid/mail is not an installed dependency in this workspace, so the real dynamic
8
- // import() inside `initialize()` fails — exercised directly, no mocking needed.
9
- // For "happy path" behavior we bypass initialize() and inject a fake sgMail client,
10
- // matching the private `sgMail`/`apiKey` fields the provider mutates internally.
11
- function injectSgMail(provider, sgMail, apiKey = 'SG.fake-key') {
12
- provider.sgMail = sgMail;
13
- provider.apiKey = apiKey;
14
- }
15
- describe('SendGridProvider', ()=>{
16
- let provider;
17
- beforeEach(()=>{
18
- provider = new _sendgridprovider.SendGridProvider();
19
- });
20
- describe('initialize', ()=>{
21
- it('throws InternalServerErrorException with SDK_NOT_INSTALLED when @sendgrid/mail is unavailable', async ()=>{
22
- await expect(provider.initialize({
23
- apiKey: 'SG.key'
24
- })).rejects.toThrow(_common.InternalServerErrorException);
25
- });
26
- it('surfaces the sdk name in the exception messageVariables', async ()=>{
27
- try {
28
- await provider.initialize({
29
- apiKey: 'SG.key'
30
- });
31
- fail('expected initialize to throw');
32
- } catch (error) {
33
- expect(error.getResponse()).toEqual(expect.objectContaining({
34
- messageKey: 'system.sdk.not.installed',
35
- messageVariables: {
36
- sdk: 'sendgrid'
37
- }
38
- }));
39
- }
40
- });
41
- });
42
- describe('sendEmail', ()=>{
43
- it('returns a failure result when the provider was never initialized', async ()=>{
44
- const result = await provider.sendEmail({
45
- to: 'a@example.com',
46
- subject: 'Hi'
47
- });
48
- expect(result).toEqual({
49
- success: false,
50
- error: 'SendGrid provider not initialized'
51
- });
52
- });
53
- it('sends via sgMail.send and extracts the message id from response headers', async ()=>{
54
- const send = jest.fn().mockResolvedValue([
55
- {
56
- headers: {
57
- 'x-message-id': 'sg-msg-1'
58
- }
59
- }
60
- ]);
61
- injectSgMail(provider, {
62
- send
63
- });
64
- const result = await provider.sendEmail({
65
- to: [
66
- 'a@example.com',
67
- 'b@example.com'
68
- ],
69
- subject: 'Hello',
70
- html: '<p>hi</p>',
71
- from: 'sender@example.com',
72
- fromName: 'Sender Name',
73
- cc: 'c@example.com',
74
- bcc: [
75
- 'd@example.com'
76
- ],
77
- replyTo: 'reply@example.com'
78
- });
79
- expect(result).toEqual({
80
- success: true,
81
- messageId: 'sg-msg-1'
82
- });
83
- expect(send).toHaveBeenCalledWith(expect.objectContaining({
84
- to: [
85
- 'a@example.com',
86
- 'b@example.com'
87
- ],
88
- from: {
89
- email: 'sender@example.com',
90
- name: 'Sender Name'
91
- },
92
- subject: 'Hello',
93
- html: '<p>hi</p>',
94
- cc: [
95
- 'c@example.com'
96
- ],
97
- bcc: [
98
- 'd@example.com'
99
- ],
100
- replyTo: 'reply@example.com'
101
- }));
102
- });
103
- it('falls back to the capitalized X-Message-Id header when lowercase is absent', async ()=>{
104
- const send = jest.fn().mockResolvedValue([
105
- {
106
- headers: {
107
- 'X-Message-Id': 'sg-msg-2'
108
- }
109
- }
110
- ]);
111
- injectSgMail(provider, {
112
- send
113
- });
114
- const result = await provider.sendEmail({
115
- to: 'a@example.com',
116
- subject: 'Hi',
117
- from: 'x@example.com'
118
- });
119
- expect(result).toEqual({
120
- success: true,
121
- messageId: 'sg-msg-2'
122
- });
123
- });
124
- it('base64-encodes buffer attachment content for the SendGrid payload', async ()=>{
125
- const send = jest.fn().mockResolvedValue([
126
- {
127
- headers: {}
128
- }
129
- ]);
130
- injectSgMail(provider, {
131
- send
132
- });
133
- await provider.sendEmail({
134
- to: 'a@example.com',
135
- subject: 'Hi',
136
- from: 'x@example.com',
137
- attachments: [
138
- {
139
- filename: 'doc.txt',
140
- content: Buffer.from('hello'),
141
- contentType: 'text/plain'
142
- }
143
- ]
144
- });
145
- const [msg] = send.mock.calls[0];
146
- expect(msg.attachments[0]).toEqual({
147
- filename: 'doc.txt',
148
- content: Buffer.from('hello').toString('base64'),
149
- type: 'text/plain',
150
- disposition: 'attachment'
151
- });
152
- });
153
- it('returns the SendGrid API error message on failure', async ()=>{
154
- injectSgMail(provider, {
155
- send: jest.fn().mockRejectedValue({
156
- response: {
157
- body: {
158
- errors: [
159
- {
160
- message: 'Invalid recipient'
161
- }
162
- ]
163
- }
164
- }
165
- })
166
- });
167
- const result = await provider.sendEmail({
168
- to: 'a@example.com',
169
- subject: 'Hi'
170
- });
171
- expect(result).toEqual({
172
- success: false,
173
- error: 'Invalid recipient'
174
- });
175
- });
176
- it('falls back to the generic error message when no SendGrid error body is present', async ()=>{
177
- injectSgMail(provider, {
178
- send: jest.fn().mockRejectedValue(new Error('network down'))
179
- });
180
- const result = await provider.sendEmail({
181
- to: 'a@example.com',
182
- subject: 'Hi'
183
- });
184
- expect(result).toEqual({
185
- success: false,
186
- error: 'network down'
187
- });
188
- });
189
- it('falls back to "Unknown error" when no message is available at all', async ()=>{
190
- injectSgMail(provider, {
191
- send: jest.fn().mockRejectedValue({})
192
- });
193
- const result = await provider.sendEmail({
194
- to: 'a@example.com',
195
- subject: 'Hi'
196
- });
197
- expect(result).toEqual({
198
- success: false,
199
- error: 'Unknown error'
200
- });
201
- });
202
- });
203
- describe('sendBulkEmails', ()=>{
204
- it('returns a not-initialized failure per message when not initialized', async ()=>{
205
- const results = await provider.sendBulkEmails([
206
- {
207
- to: 'a@example.com',
208
- subject: 'A'
209
- },
210
- {
211
- to: 'b@example.com',
212
- subject: 'B'
213
- }
214
- ]);
215
- expect(results).toEqual([
216
- {
217
- success: false,
218
- error: 'SendGrid provider not initialized'
219
- },
220
- {
221
- success: false,
222
- error: 'SendGrid provider not initialized'
223
- }
224
- ]);
225
- });
226
- it('sends all messages in a single sgMail.send call and reports success for all', async ()=>{
227
- const send = jest.fn().mockResolvedValue(undefined);
228
- injectSgMail(provider, {
229
- send
230
- });
231
- const results = await provider.sendBulkEmails([
232
- {
233
- to: 'a@example.com',
234
- subject: 'A',
235
- from: 'x@example.com'
236
- },
237
- {
238
- to: 'b@example.com',
239
- subject: 'B',
240
- from: 'x@example.com'
241
- }
242
- ]);
243
- expect(send).toHaveBeenCalledTimes(1);
244
- expect(send).toHaveBeenCalledWith([
245
- expect.objectContaining({
246
- to: [
247
- 'a@example.com'
248
- ],
249
- subject: 'A'
250
- }),
251
- expect.objectContaining({
252
- to: [
253
- 'b@example.com'
254
- ],
255
- subject: 'B'
256
- })
257
- ]);
258
- expect(results).toEqual([
259
- {
260
- success: true
261
- },
262
- {
263
- success: true
264
- }
265
- ]);
266
- });
267
- it('excludes cc/bcc/replyTo/attachments from bulk messages (includeExtras=false)', async ()=>{
268
- const send = jest.fn().mockResolvedValue(undefined);
269
- injectSgMail(provider, {
270
- send
271
- });
272
- await provider.sendBulkEmails([
273
- {
274
- to: 'a@example.com',
275
- subject: 'A',
276
- from: 'x@example.com',
277
- cc: 'c@example.com',
278
- replyTo: 'r@example.com'
279
- }
280
- ]);
281
- const [messages] = send.mock.calls[0];
282
- expect(messages[0]).not.toHaveProperty('cc');
283
- expect(messages[0]).not.toHaveProperty('replyTo');
284
- });
285
- it('reports the same failure for every message when the bulk send call rejects', async ()=>{
286
- injectSgMail(provider, {
287
- send: jest.fn().mockRejectedValue(new Error('rate limited'))
288
- });
289
- const results = await provider.sendBulkEmails([
290
- {
291
- to: 'a@example.com',
292
- subject: 'A',
293
- from: 'x@example.com'
294
- },
295
- {
296
- to: 'b@example.com',
297
- subject: 'B',
298
- from: 'x@example.com'
299
- }
300
- ]);
301
- expect(results).toEqual([
302
- {
303
- success: false,
304
- error: 'rate limited'
305
- },
306
- {
307
- success: false,
308
- error: 'rate limited'
309
- }
310
- ]);
311
- });
312
- });
313
- describe('healthCheck', ()=>{
314
- it('returns false when not initialized', async ()=>{
315
- await expect(provider.healthCheck()).resolves.toBe(false);
316
- });
317
- it('returns true when sgMail is set and the api key has the SG. prefix', async ()=>{
318
- injectSgMail(provider, {
319
- send: jest.fn()
320
- }, 'SG.validkey');
321
- await expect(provider.healthCheck()).resolves.toBe(true);
322
- });
323
- it('returns false when the api key does not have the SG. prefix', async ()=>{
324
- injectSgMail(provider, {
325
- send: jest.fn()
326
- }, 'invalid-key');
327
- await expect(provider.healthCheck()).resolves.toBe(false);
328
- });
329
- });
330
- describe('close', ()=>{
331
- it('clears the sgMail client reference', async ()=>{
332
- injectSgMail(provider, {
333
- send: jest.fn()
334
- });
335
- await provider.close();
336
- const result = await provider.sendEmail({
337
- to: 'a@example.com',
338
- subject: 'Hi'
339
- });
340
- expect(result).toEqual({
341
- success: false,
342
- error: 'SendGrid provider not initialized'
343
- });
344
- });
345
- });
346
- });
@@ -1,164 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "SmtpProvider", {
6
- enumerable: true,
7
- get: function() {
8
- return SmtpProvider;
9
- }
10
- });
11
- function _define_property(obj, key, value) {
12
- if (key in obj) {
13
- Object.defineProperty(obj, key, {
14
- value: value,
15
- enumerable: true,
16
- configurable: true,
17
- writable: true
18
- });
19
- } else {
20
- obj[key] = value;
21
- }
22
- return obj;
23
- }
24
- function _getRequireWildcardCache(nodeInterop) {
25
- if (typeof WeakMap !== "function") return null;
26
- var cacheBabelInterop = new WeakMap();
27
- var cacheNodeInterop = new WeakMap();
28
- return (_getRequireWildcardCache = function(nodeInterop) {
29
- return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
30
- })(nodeInterop);
31
- }
32
- function _interop_require_wildcard(obj, nodeInterop) {
33
- if (!nodeInterop && obj && obj.__esModule) {
34
- return obj;
35
- }
36
- if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
37
- return {
38
- default: obj
39
- };
40
- }
41
- var cache = _getRequireWildcardCache(nodeInterop);
42
- if (cache && cache.has(obj)) {
43
- return cache.get(obj);
44
- }
45
- var newObj = {
46
- __proto__: null
47
- };
48
- var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
49
- for(var key in obj){
50
- if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
51
- var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
52
- if (desc && (desc.get || desc.set)) {
53
- Object.defineProperty(newObj, key, desc);
54
- } else {
55
- newObj[key] = obj[key];
56
- }
57
- }
58
- }
59
- newObj.default = obj;
60
- if (cache) {
61
- cache.set(obj, newObj);
62
- }
63
- return newObj;
64
- }
65
- const VERIFY_TIMEOUT_MS = 10_000;
66
- const SEND_TIMEOUT_MS = 30_000;
67
- let SmtpProvider = class SmtpProvider {
68
- async initialize(config) {
69
- const nodemailer = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("nodemailer")));
70
- this.transporter = nodemailer.createTransport({
71
- host: config.host,
72
- port: config.port,
73
- secure: config.secure ?? config.port === 465,
74
- auth: config.auth,
75
- connectionTimeout: VERIFY_TIMEOUT_MS,
76
- greetingTimeout: VERIFY_TIMEOUT_MS,
77
- socketTimeout: SEND_TIMEOUT_MS,
78
- tls: {
79
- rejectUnauthorized: config.tls?.rejectUnauthorized ?? true,
80
- minVersion: config.tls?.minVersion ?? 'TLSv1.2'
81
- }
82
- });
83
- await this.withTimeout(this.transporter.verify(), VERIFY_TIMEOUT_MS, 'SMTP verification timeout');
84
- }
85
- async sendEmail(options) {
86
- if (!this.transporter) return {
87
- success: false,
88
- error: 'SMTP provider not initialized'
89
- };
90
- try {
91
- const mailOptions = {
92
- from: this.formatFrom(options.from, options.fromName),
93
- to: this.joinAddresses(options.to),
94
- cc: options.cc ? this.joinAddresses(options.cc) : undefined,
95
- bcc: options.bcc ? this.joinAddresses(options.bcc) : undefined,
96
- subject: options.subject,
97
- text: options.text,
98
- html: options.html,
99
- replyTo: options.replyTo,
100
- attachments: options.attachments?.map((a)=>({
101
- filename: a.filename,
102
- content: a.content,
103
- contentType: a.contentType,
104
- encoding: a.encoding
105
- }))
106
- };
107
- const info = await this.withTimeout(this.transporter.sendMail(mailOptions), SEND_TIMEOUT_MS, 'SMTP send timeout');
108
- return {
109
- success: true,
110
- messageId: info.messageId
111
- };
112
- } catch (error) {
113
- return {
114
- success: false,
115
- error: this.extractError(error)
116
- };
117
- }
118
- }
119
- async sendBulkEmails(options) {
120
- return Promise.all(options.map((opt)=>this.sendEmail(opt)));
121
- }
122
- async healthCheck() {
123
- if (!this.transporter) return false;
124
- try {
125
- await this.transporter.verify();
126
- return true;
127
- } catch {
128
- return false;
129
- }
130
- }
131
- async close() {
132
- if (this.transporter) {
133
- this.transporter.close();
134
- this.transporter = null;
135
- }
136
- }
137
- // ─── Private Helpers ────────────────────────────────────────────────────────
138
- formatFrom(from, fromName) {
139
- return fromName ? `"${fromName}" <${from}>` : from;
140
- }
141
- joinAddresses(addresses) {
142
- return Array.isArray(addresses) ? addresses.join(', ') : addresses;
143
- }
144
- async withTimeout(promise, ms, message) {
145
- let timer;
146
- const timeout = new Promise((_, reject)=>{
147
- timer = setTimeout(()=>reject(new Error(message)), ms);
148
- });
149
- try {
150
- return await Promise.race([
151
- promise,
152
- timeout
153
- ]);
154
- } finally{
155
- clearTimeout(timer);
156
- }
157
- }
158
- extractError(error) {
159
- return error instanceof Error ? error.message : 'Unknown error';
160
- }
161
- constructor(){
162
- _define_property(this, "transporter", null);
163
- }
164
- };