@wisemen/nestjs-mail 1.0.0 → 1.1.0

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 (47) hide show
  1. package/README.md +91 -5
  2. package/dist/clients/mail-client.factory.d.ts +2 -1
  3. package/dist/clients/mail-client.factory.js +12 -1
  4. package/dist/clients/mail-client.factory.js.map +1 -1
  5. package/dist/clients/mail-delivery-hooks.client.d.ts +11 -0
  6. package/dist/clients/mail-delivery-hooks.client.js +47 -0
  7. package/dist/clients/mail-delivery-hooks.client.js.map +1 -0
  8. package/dist/clients/mail-delivery-hooks.test.d.ts +1 -0
  9. package/dist/clients/mail-delivery-hooks.test.js +104 -0
  10. package/dist/clients/mail-delivery-hooks.test.js.map +1 -0
  11. package/dist/clients/mail-providers.test.d.ts +1 -0
  12. package/dist/clients/mail-providers.test.js +275 -0
  13. package/dist/clients/mail-providers.test.js.map +1 -0
  14. package/dist/clients/mail.client.d.ts +26 -1
  15. package/dist/clients/mail.client.js +9 -0
  16. package/dist/clients/mail.client.js.map +1 -1
  17. package/dist/clients/mailgun-mail.client.d.ts +12 -0
  18. package/dist/clients/mailgun-mail.client.js +81 -0
  19. package/dist/clients/mailgun-mail.client.js.map +1 -0
  20. package/dist/clients/mailpit-mail.client.js +2 -1
  21. package/dist/clients/mailpit-mail.client.js.map +1 -1
  22. package/dist/clients/scaleway-mail.client.js +1 -1
  23. package/dist/clients/scaleway-mail.client.js.map +1 -1
  24. package/dist/clients/sendgrid-mail.client.js +1 -3
  25. package/dist/clients/sendgrid-mail.client.js.map +1 -1
  26. package/dist/enums/mail-provider.enum.d.ts +2 -1
  27. package/dist/enums/mail-provider.enum.js +1 -0
  28. package/dist/enums/mail-provider.enum.js.map +1 -1
  29. package/dist/index.d.ts +3 -2
  30. package/dist/index.js +1 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/modules/mail.module-options.d.ts +22 -4
  33. package/dist/modules/mail.module.js +1 -1
  34. package/dist/modules/mail.module.js.map +1 -1
  35. package/dist/queue/mail-job-handlers.test.d.ts +1 -0
  36. package/dist/queue/mail-job-handlers.test.js +66 -0
  37. package/dist/queue/mail-job-handlers.test.js.map +1 -0
  38. package/dist/queue/send-html-mail.job-handler.js +2 -1
  39. package/dist/queue/send-html-mail.job-handler.js.map +1 -1
  40. package/dist/queue/send-html-mail.job.d.ts +3 -1
  41. package/dist/queue/send-html-mail.job.js.map +1 -1
  42. package/dist/queue/send-template-mail.job-handler.js +2 -1
  43. package/dist/queue/send-template-mail.job-handler.js.map +1 -1
  44. package/dist/queue/send-template-mail.job.d.ts +3 -1
  45. package/dist/queue/send-template-mail.job.js.map +1 -1
  46. package/package.json +6 -3
  47. package/skills/getting-started/SKILL.md +51 -5
package/README.md CHANGED
@@ -6,7 +6,10 @@ Reusable NestJS mail infrastructure for Wisemen services.
6
6
 
7
7
  - `MailModule.forRoot()` and `MailModule.forRootAsync()` for DI-friendly mail client wiring
8
8
  - `HandlebarsRenderer` integration through `@wisemen/nestjs-handlebars`
9
- - Mail providers for MailPit, Scaleway, and SendGrid
9
+ - Mail providers for MailPit, Scaleway, SendGrid, and Mailgun
10
+ - Named sender support (`from` as a string or `{ email, name? }`)
11
+ - Application-defined delivery metadata
12
+ - Delivery success and failure hooks without replacing packaged clients or queue handlers
10
13
  - Shared mail errors, enums, and message types
11
14
 
12
15
  ## Usage
@@ -21,10 +24,15 @@ import { MailModule } from '@wisemen/nestjs-mail'
21
24
  templateRootPath: process.cwd() + '/dist/src/modules',
22
25
  client: {
23
26
  type: 'mailpit',
24
- url: 'http://127.0.0.1:8025'
25
- }
26
- })
27
- ]
27
+ url: 'http://127.0.0.1:8025',
28
+ defaultFrom: {
29
+ email: 'hello@example.com',
30
+ name: 'Example Team',
31
+ },
32
+ },
33
+ queueName: 'mail',
34
+ }),
35
+ ],
28
36
  })
29
37
  export class AppModule {}
30
38
  ```
@@ -32,3 +40,81 @@ export class AppModule {}
32
40
  Project-specific policy belongs outside the package. In larger apps, use `forRootAsync()` and resolve the `client` options from environment variables, feature flags, secrets, or test doubles in your own factory.
33
41
 
34
42
  Install `@wisemen/nestjs-handlebars` alongside this package when you want to inject `HandlebarsRenderer` for template-based mail rendering.
43
+
44
+ ## Named senders and metadata
45
+
46
+ Both configured default senders and per-message `from` values accept an email
47
+ string or an object with an optional display name. `metadata` is opaque
48
+ application context exposed to delivery hooks; it is never sent to MailPit,
49
+ Scaleway, or SendGrid.
50
+
51
+ ```ts
52
+ await mailClient.sendMail({
53
+ from: {
54
+ email: 'tickets@example.com',
55
+ name: 'Example Tickets',
56
+ },
57
+ to: 'customer@example.com',
58
+ subject: 'Your tickets',
59
+ html: '<p>Your tickets are ready.</p>',
60
+ metadata: {
61
+ deliveryId: 'delivery-123',
62
+ kind: 'ticket-confirmation',
63
+ },
64
+ })
65
+ ```
66
+
67
+ Metadata placed in `SendHtmlMailJobData` or `SendTemplateMailJobData` must be
68
+ JSON-serializable because it passes through the queue.
69
+
70
+ ## Mailgun
71
+
72
+ ```ts
73
+ MailModule.forRoot({
74
+ client: {
75
+ type: 'mailgun',
76
+ domain: 'mail.example.com',
77
+ apiKey: 'key-...',
78
+ defaultFrom: {
79
+ email: 'hello@example.com',
80
+ name: 'Example Team',
81
+ },
82
+ // Use 'https://api.eu.mailgun.net' for EU-region domains. Defaults to 'https://api.mailgun.net'.
83
+ baseUrl: 'https://api.eu.mailgun.net',
84
+ },
85
+ queueName: 'mail',
86
+ })
87
+ ```
88
+
89
+ ## Delivery hooks
90
+
91
+ Configure one hooks object on the module. Hooks receive the configured client
92
+ type, the original send options (including metadata), and either the exact
93
+ `SentMail[]` result or the original provider error.
94
+
95
+ ```ts
96
+ MailModule.forRoot({
97
+ client: {
98
+ type: 'mailpit',
99
+ url: 'http://127.0.0.1:8025',
100
+ },
101
+ queueName: 'mail',
102
+ deliveryHooks: {
103
+ async onSuccess ({ clientType, options, sentMails }) {
104
+ await deliveries.markSent({
105
+ clientType,
106
+ metadata: options.metadata,
107
+ providerIds: sentMails.map(mail => mail.id),
108
+ })
109
+ },
110
+ async onFailure ({ clientType, options, error }) {
111
+ await deliveries.markFailed({ clientType, metadata: options.metadata, error })
112
+ },
113
+ },
114
+ })
115
+ ```
116
+
117
+ Hook failures log only the hook phase, client type, and error stack. Recipients,
118
+ bodies, subjects, and metadata are never added to the log. Hook failures never
119
+ turn a successful provider delivery into a failed send or replace a provider
120
+ error, so they cannot trigger a duplicate-email queue retry.
@@ -1,4 +1,5 @@
1
1
  import type { MailModuleOptions } from '../modules/mail.module-options.js';
2
2
  import type { MailClient } from './mail.client.js';
3
- export declare function createMailClient(options: MailModuleOptions['client']): MailClient;
3
+ import type { MailDeliveryHooks } from './mail.client.js';
4
+ export declare function createMailClient(options: MailModuleOptions['client'], deliveryHooks?: MailDeliveryHooks): MailClient;
4
5
  export declare const mailClientFactory: typeof createMailClient;
@@ -1,10 +1,19 @@
1
1
  import { MailProvider } from '../enums/mail-provider.enum.js';
2
2
  import { exhaustiveCheck } from '../utils/exhaustive-check.js';
3
+ import { MailDeliveryHooksClient } from './mail-delivery-hooks.client.js';
4
+ import { MailgunMailClient } from './mailgun-mail.client.js';
3
5
  import { MailPitMailClient } from './mailpit-mail.client.js';
4
6
  import { MockMailClient } from './mock-mail.client.js';
5
7
  import { ScalewayMailClient } from './scaleway-mail.client.js';
6
8
  import { SendGridMailClient } from './sendgrid-mail.client.js';
7
- export function createMailClient(options) {
9
+ export function createMailClient(options, deliveryHooks) {
10
+ const client = createConcreteMailClient(options);
11
+ if (deliveryHooks === undefined) {
12
+ return client;
13
+ }
14
+ return new MailDeliveryHooksClient(client, options.type, deliveryHooks);
15
+ }
16
+ function createConcreteMailClient(options) {
8
17
  switch (options.type) {
9
18
  case 'mailpit':
10
19
  return new MailPitMailClient(options);
@@ -14,6 +23,8 @@ export function createMailClient(options) {
14
23
  return new ScalewayMailClient(options);
15
24
  case MailProvider.SEND_GRID:
16
25
  return new SendGridMailClient(options);
26
+ case MailProvider.MAILGUN:
27
+ return new MailgunMailClient(options);
17
28
  default:
18
29
  exhaustiveCheck(options);
19
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mail-client.factory.js","sourceRoot":"","sources":["../../lib/clients/mail-client.factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAE7D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAE9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAE9D,MAAM,UAAU,gBAAgB,CAAE,OAAoC;IACpE,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,SAAS;YACZ,OAAO,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAAA;QACvC,KAAK,MAAM;YACT,OAAO,IAAI,cAAc,EAAE,CAAA;QAC7B,KAAK,YAAY,CAAC,QAAQ;YACxB,OAAO,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAA;QACxC,KAAK,YAAY,CAAC,SAAS;YACzB,OAAO,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAA;QACxC;YACE,eAAe,CAAC,OAAO,CAAC,CAAA;IAC5B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,gBAAgB,CAAA"}
1
+ {"version":3,"file":"mail-client.factory.js","sourceRoot":"","sources":["../../lib/clients/mail-client.factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAE7D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAG9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAA;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAE9D,MAAM,UAAU,gBAAgB,CAC9B,OAAoC,EACpC,aAAiC;IAEjC,MAAM,MAAM,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAA;IAEhD,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,MAAM,CAAA;IACf,CAAC;IAED,OAAO,IAAI,uBAAuB,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,aAAa,CAAC,CAAA;AACzE,CAAC;AAED,SAAS,wBAAwB,CAAE,OAAoC;IACrE,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,SAAS;YACZ,OAAO,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAAA;QACvC,KAAK,MAAM;YACT,OAAO,IAAI,cAAc,EAAE,CAAA;QAC7B,KAAK,YAAY,CAAC,QAAQ;YACxB,OAAO,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAA;QACxC,KAAK,YAAY,CAAC,SAAS;YACzB,OAAO,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAA;QACxC,KAAK,YAAY,CAAC,OAAO;YACvB,OAAO,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAAA;QACvC;YACE,eAAe,CAAC,OAAO,CAAC,CAAA;IAC5B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,gBAAgB,CAAA"}
@@ -0,0 +1,11 @@
1
+ import type { MailClientType, MailDeliveryHooks, SendMailOptions, SentMail } from './mail.client.js';
2
+ import { MailClient } from './mail.client.js';
3
+ export declare class MailDeliveryHooksClient extends MailClient {
4
+ private readonly client;
5
+ private readonly clientType;
6
+ private readonly hooks;
7
+ private readonly logger;
8
+ constructor(client: MailClient, clientType: MailClientType, hooks: MailDeliveryHooks);
9
+ sendMail(options: SendMailOptions): Promise<SentMail[]>;
10
+ private runHook;
11
+ }
@@ -0,0 +1,47 @@
1
+ import { Logger } from '@nestjs/common';
2
+ import { MailClient } from './mail.client.js';
3
+ export class MailDeliveryHooksClient extends MailClient {
4
+ client;
5
+ clientType;
6
+ hooks;
7
+ logger = new Logger(MailDeliveryHooksClient.name);
8
+ constructor(client, clientType, hooks) {
9
+ super();
10
+ this.client = client;
11
+ this.clientType = clientType;
12
+ this.hooks = hooks;
13
+ }
14
+ async sendMail(options) {
15
+ let sentMails;
16
+ try {
17
+ sentMails = await this.client.sendMail(options);
18
+ }
19
+ catch (error) {
20
+ await this.runHook('failure', () => this.hooks.onFailure?.({
21
+ clientType: this.clientType,
22
+ options,
23
+ error
24
+ }));
25
+ throw error;
26
+ }
27
+ await this.runHook('success', () => this.hooks.onSuccess?.({
28
+ clientType: this.clientType,
29
+ options,
30
+ sentMails
31
+ }));
32
+ return sentMails;
33
+ }
34
+ async runHook(phase, hook) {
35
+ try {
36
+ await hook();
37
+ }
38
+ catch (error) {
39
+ this.logger.error({
40
+ phase,
41
+ clientType: this.clientType,
42
+ stack: error instanceof Error ? error.stack : undefined
43
+ });
44
+ }
45
+ }
46
+ }
47
+ //# sourceMappingURL=mail-delivery-hooks.client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail-delivery-hooks.client.js","sourceRoot":"","sources":["../../lib/clients/mail-delivery-hooks.client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAEvC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAI7C,MAAM,OAAO,uBAAwB,SAAQ,UAAU;IAIlC;IACA;IACA;IALF,MAAM,GAAG,IAAI,MAAM,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAA;IAElE,YACmB,MAAkB,EAClB,UAA0B,EAC1B,KAAwB;QAEzC,KAAK,EAAE,CAAA;QAJU,WAAM,GAAN,MAAM,CAAY;QAClB,eAAU,GAAV,UAAU,CAAgB;QAC1B,UAAK,GAAL,KAAK,CAAmB;IAG3C,CAAC;IAED,KAAK,CAAC,QAAQ,CAAE,OAAwB;QACtC,IAAI,SAAqB,CAAA;QAEzB,IAAI,CAAC;YACH,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;QACjD,CAAC;QACD,OAAO,KAAK,EAAE,CAAC;YACb,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;gBACzD,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,OAAO;gBACP,KAAK;aACN,CAAC,CAAC,CAAA;YAEH,MAAM,KAAK,CAAA;QACb,CAAC;QAED,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;YACzD,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,OAAO;YACP,SAAS;SACV,CAAC,CAAC,CAAA;QAEH,OAAO,SAAS,CAAA;IAClB,CAAC;IAEO,KAAK,CAAC,OAAO,CACnB,KAA4B,EAC5B,IAAqC;QAErC,IAAI,CAAC;YACH,MAAM,IAAI,EAAE,CAAA;QACd,CAAC;QACD,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;gBAChB,KAAK;gBACL,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;aACxD,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,104 @@
1
+ import { Logger } from '@nestjs/common';
2
+ import { afterEach, describe, it, mock } from 'node:test';
3
+ import { expect } from 'expect';
4
+ import { createMailClient } from './mail-client.factory.js';
5
+ import { MockMailClient } from './mock-mail.client.js';
6
+ afterEach(() => {
7
+ mock.restoreAll();
8
+ });
9
+ const options = {
10
+ to: 'recipient@example.com',
11
+ subject: 'Sensitive subject',
12
+ text: 'Sensitive body',
13
+ metadata: { deliveryId: 'sensitive-metadata' }
14
+ };
15
+ describe('mail delivery hooks', () => {
16
+ it('awaits success hooks and preserves the exact result', async () => {
17
+ const sentMails = [{ id: 'provider-id', recipients: ['recipient@example.com'] }];
18
+ let delivery;
19
+ let hookFinished = false;
20
+ mock.method(MockMailClient.prototype, 'sendMail', () => Promise.resolve(sentMails));
21
+ const client = createMailClient({ type: 'mock' }, {
22
+ onSuccess: async (value) => {
23
+ await Promise.resolve();
24
+ delivery = value;
25
+ hookFinished = true;
26
+ }
27
+ });
28
+ const result = await client.sendMail(options);
29
+ expect(hookFinished).toBe(true);
30
+ expect(delivery?.clientType).toBe('mock');
31
+ expect(delivery?.options).toBe(options);
32
+ expect(delivery?.sentMails).toBe(sentMails);
33
+ expect(result).toBe(sentMails);
34
+ });
35
+ it('invokes failure hooks and rethrows the exact provider error', async () => {
36
+ const providerError = new Error('provider failed');
37
+ let delivery;
38
+ mock.method(MockMailClient.prototype, 'sendMail', () => Promise.reject(providerError));
39
+ const client = createMailClient({ type: 'mock' }, {
40
+ onFailure: async (value) => {
41
+ delivery = value;
42
+ await Promise.resolve();
43
+ }
44
+ });
45
+ await expect(client.sendMail(options)).rejects.toBe(providerError);
46
+ expect(delivery?.clientType).toBe('mock');
47
+ expect(delivery?.options).toBe(options);
48
+ expect(delivery?.error).toBe(providerError);
49
+ });
50
+ it('logs and swallows success-hook failures without invoking failure hooks', async () => {
51
+ const sentMails = [{ id: 'provider-id', recipients: ['recipient@example.com'] }];
52
+ const hookError = new Error('success hook failed');
53
+ const logs = [];
54
+ let failureHookCalled = false;
55
+ mock.method(MockMailClient.prototype, 'sendMail', () => Promise.resolve(sentMails));
56
+ mock.method(Logger.prototype, 'error', (...args) => {
57
+ logs.push(args);
58
+ });
59
+ const client = createMailClient({ type: 'mock' }, {
60
+ onSuccess: () => Promise.reject(hookError),
61
+ onFailure: () => {
62
+ failureHookCalled = true;
63
+ return Promise.resolve();
64
+ }
65
+ });
66
+ const result = await client.sendMail(options);
67
+ expect(result).toBe(sentMails);
68
+ expect(failureHookCalled).toBe(false);
69
+ expect(logs).toEqual([[{
70
+ phase: 'success',
71
+ clientType: 'mock',
72
+ stack: hookError.stack
73
+ }]]);
74
+ expect(JSON.stringify(logs)).not.toContain(options.subject);
75
+ expect(JSON.stringify(logs)).not.toContain(options.text);
76
+ expect(JSON.stringify(logs)).not.toContain('sensitive-metadata');
77
+ });
78
+ it('logs failure-hook failures and preserves the provider error', async () => {
79
+ const providerError = new Error('provider failed');
80
+ const hookError = new Error('failure hook failed');
81
+ const logs = [];
82
+ mock.method(MockMailClient.prototype, 'sendMail', () => Promise.reject(providerError));
83
+ mock.method(Logger.prototype, 'error', (...args) => {
84
+ logs.push(args);
85
+ });
86
+ const client = createMailClient({ type: 'mock' }, {
87
+ onFailure: () => Promise.reject(hookError)
88
+ });
89
+ await expect(client.sendMail(options)).rejects.toBe(providerError);
90
+ expect(logs).toEqual([[{
91
+ phase: 'failure',
92
+ clientType: 'mock',
93
+ stack: hookError.stack
94
+ }]]);
95
+ expect(JSON.stringify(logs)).not.toContain(options.subject);
96
+ expect(JSON.stringify(logs)).not.toContain(options.text);
97
+ expect(JSON.stringify(logs)).not.toContain('sensitive-metadata');
98
+ });
99
+ it('returns the existing concrete client unchanged without hooks', () => {
100
+ const client = createMailClient({ type: 'mock' });
101
+ expect(client).toBeInstanceOf(MockMailClient);
102
+ });
103
+ });
104
+ //# sourceMappingURL=mail-delivery-hooks.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail-delivery-hooks.test.js","sourceRoot":"","sources":["../../lib/clients/mail-delivery-hooks.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,275 @@
1
+ import { afterEach, describe, it, mock } from 'node:test';
2
+ import { expect } from 'expect';
3
+ import { MailProvider } from '../enums/mail-provider.enum.js';
4
+ import { MailgunMailClient } from './mailgun-mail.client.js';
5
+ import { MailPitMailClient } from './mailpit-mail.client.js';
6
+ import { ScalewayMailClient } from './scaleway-mail.client.js';
7
+ import { SendGridMailClient } from './sendgrid-mail.client.js';
8
+ afterEach(() => {
9
+ mock.restoreAll();
10
+ });
11
+ function captureFetchBody(response) {
12
+ let body;
13
+ mock.method(globalThis, 'fetch', (_input, init) => {
14
+ if (typeof init?.body !== 'string') {
15
+ return Promise.reject(new Error('Expected a JSON request body'));
16
+ }
17
+ body = init.body;
18
+ return Promise.resolve(response);
19
+ });
20
+ return () => JSON.parse(body ?? '');
21
+ }
22
+ function scalewayResponse() {
23
+ return new Response(JSON.stringify({
24
+ emails: [{ id: 'scaleway-id', mail_rcpt: 'recipient@example.com' }]
25
+ }), { status: 200 });
26
+ }
27
+ function sendGridResponse() {
28
+ return new Response(null, {
29
+ status: 202,
30
+ headers: { 'x-message-id': 'sendgrid-id' }
31
+ });
32
+ }
33
+ function captureFormDataBody(response) {
34
+ let body;
35
+ mock.method(globalThis, 'fetch', (_input, init) => {
36
+ if (!(init?.body instanceof FormData)) {
37
+ return Promise.reject(new Error('Expected a FormData request body'));
38
+ }
39
+ body = init.body;
40
+ return Promise.resolve(response);
41
+ });
42
+ return () => {
43
+ if (body === undefined) {
44
+ throw new Error('fetch was not called');
45
+ }
46
+ return body;
47
+ };
48
+ }
49
+ function mailgunResponse() {
50
+ return new Response(JSON.stringify({ id: 'mailgun-id', message: 'Queued. Thank you.' }), { status: 200 });
51
+ }
52
+ describe('ScalewayMailClient sender payload', () => {
53
+ it('normalizes a string sender and preserves SentMail', async () => {
54
+ const getBody = captureFetchBody(scalewayResponse());
55
+ const client = new ScalewayMailClient({
56
+ type: MailProvider.SCALEWAY,
57
+ projectId: 'project-id',
58
+ apiKey: 'api-key',
59
+ from: 'default@example.com'
60
+ });
61
+ const sentMails = await client.sendMail({
62
+ from: 'sender@example.com',
63
+ to: 'recipient@example.com',
64
+ subject: 'Subject',
65
+ text: 'Body'
66
+ });
67
+ expect(getBody()).toMatchObject({ from: { email: 'sender@example.com' } });
68
+ expect(sentMails).toEqual([{ id: 'scaleway-id', recipients: ['recipient@example.com'] }]);
69
+ });
70
+ it('maps a named sender and excludes metadata', async () => {
71
+ const getBody = captureFetchBody(scalewayResponse());
72
+ const client = new ScalewayMailClient({
73
+ type: MailProvider.SCALEWAY,
74
+ projectId: 'project-id',
75
+ apiKey: 'api-key',
76
+ from: 'default@example.com'
77
+ });
78
+ await client.sendMail({
79
+ from: { email: 'sender@example.com', name: 'Sender Name' },
80
+ to: 'recipient@example.com',
81
+ subject: 'Subject',
82
+ text: 'Body',
83
+ metadata: { deliveryId: 'secret-delivery-id' }
84
+ });
85
+ const body = getBody();
86
+ expect(body).toMatchObject({ from: { email: 'sender@example.com', name: 'Sender Name' } });
87
+ expect(body).not.toHaveProperty('metadata');
88
+ expect(JSON.stringify(body)).not.toContain('secret-delivery-id');
89
+ });
90
+ it('maps a named default sender', async () => {
91
+ const getBody = captureFetchBody(scalewayResponse());
92
+ const client = new ScalewayMailClient({
93
+ type: MailProvider.SCALEWAY,
94
+ projectId: 'project-id',
95
+ apiKey: 'api-key',
96
+ from: { email: 'default@example.com', name: 'Default Sender' }
97
+ });
98
+ await client.sendMail({
99
+ to: 'recipient@example.com',
100
+ subject: 'Subject',
101
+ text: 'Body'
102
+ });
103
+ expect(getBody()).toMatchObject({ from: { email: 'default@example.com', name: 'Default Sender' } });
104
+ });
105
+ });
106
+ describe('SendGridMailClient sender payload', () => {
107
+ it('normalizes a string sender and preserves SentMail', async () => {
108
+ const getBody = captureFetchBody(sendGridResponse());
109
+ const client = new SendGridMailClient({
110
+ type: MailProvider.SEND_GRID,
111
+ apiToken: 'api-token',
112
+ defaultFrom: 'default@example.com'
113
+ });
114
+ const sentMails = await client.sendMail({
115
+ from: 'sender@example.com',
116
+ to: 'recipient@example.com',
117
+ subject: 'Subject',
118
+ text: 'Body'
119
+ });
120
+ expect(getBody()).toMatchObject({ from: { email: 'sender@example.com' } });
121
+ expect(sentMails).toEqual([{ id: 'sendgrid-id', recipients: ['recipient@example.com'] }]);
122
+ });
123
+ it('maps a named sender and excludes metadata', async () => {
124
+ const getBody = captureFetchBody(sendGridResponse());
125
+ const client = new SendGridMailClient({
126
+ type: MailProvider.SEND_GRID,
127
+ apiToken: 'api-token',
128
+ defaultFrom: 'default@example.com'
129
+ });
130
+ await client.sendMail({
131
+ from: { email: 'sender@example.com', name: 'Sender Name' },
132
+ to: 'recipient@example.com',
133
+ subject: 'Subject',
134
+ text: 'Body',
135
+ metadata: { deliveryId: 'secret-delivery-id' }
136
+ });
137
+ const body = getBody();
138
+ expect(body).toMatchObject({ from: { email: 'sender@example.com', name: 'Sender Name' } });
139
+ expect(body).not.toHaveProperty('metadata');
140
+ expect(JSON.stringify(body)).not.toContain('secret-delivery-id');
141
+ });
142
+ it('maps a named default sender', async () => {
143
+ const getBody = captureFetchBody(sendGridResponse());
144
+ const client = new SendGridMailClient({
145
+ type: MailProvider.SEND_GRID,
146
+ apiToken: 'api-token',
147
+ defaultFrom: { email: 'default@example.com', name: 'Default Sender' }
148
+ });
149
+ await client.sendMail({
150
+ to: 'recipient@example.com',
151
+ subject: 'Subject',
152
+ text: 'Body'
153
+ });
154
+ expect(getBody()).toMatchObject({ from: { email: 'default@example.com', name: 'Default Sender' } });
155
+ });
156
+ });
157
+ describe('MailgunMailClient sender payload', () => {
158
+ it('normalizes a string sender and preserves SentMail', async () => {
159
+ const getForm = captureFormDataBody(mailgunResponse());
160
+ const client = new MailgunMailClient({
161
+ type: MailProvider.MAILGUN,
162
+ domain: 'mail.example.com',
163
+ apiKey: 'api-key',
164
+ defaultFrom: 'default@example.com'
165
+ });
166
+ const sentMails = await client.sendMail({
167
+ from: 'sender@example.com',
168
+ to: 'recipient@example.com',
169
+ subject: 'Subject',
170
+ text: 'Body'
171
+ });
172
+ expect(getForm().get('from')).toBe('sender@example.com');
173
+ expect(sentMails).toEqual([{ id: 'mailgun-id', recipients: ['recipient@example.com'] }]);
174
+ });
175
+ it('maps a named sender and excludes metadata', async () => {
176
+ const getForm = captureFormDataBody(mailgunResponse());
177
+ const client = new MailgunMailClient({
178
+ type: MailProvider.MAILGUN,
179
+ domain: 'mail.example.com',
180
+ apiKey: 'api-key',
181
+ defaultFrom: 'default@example.com'
182
+ });
183
+ await client.sendMail({
184
+ from: { email: 'sender@example.com', name: 'Sender Name' },
185
+ to: 'recipient@example.com',
186
+ subject: 'Subject',
187
+ text: 'Body',
188
+ metadata: { deliveryId: 'secret-delivery-id' }
189
+ });
190
+ const form = getForm();
191
+ expect(form.get('from')).toBe('Sender Name <sender@example.com>');
192
+ const values = [...form.values()]
193
+ .filter((value) => typeof value === 'string')
194
+ .join(' ');
195
+ expect(values).not.toContain('secret-delivery-id');
196
+ expect(values).not.toContain('metadata');
197
+ });
198
+ it('maps a named default sender', async () => {
199
+ const getForm = captureFormDataBody(mailgunResponse());
200
+ const client = new MailgunMailClient({
201
+ type: MailProvider.MAILGUN,
202
+ domain: 'mail.example.com',
203
+ apiKey: 'api-key',
204
+ defaultFrom: { email: 'default@example.com', name: 'Default Sender' }
205
+ });
206
+ await client.sendMail({
207
+ to: 'recipient@example.com',
208
+ subject: 'Subject',
209
+ text: 'Body'
210
+ });
211
+ expect(getForm().get('from')).toBe('Default Sender <default@example.com>');
212
+ });
213
+ });
214
+ function captureMailPitRequest(client) {
215
+ let request;
216
+ Object.defineProperty(client, 'client', {
217
+ value: {
218
+ sendMessage: (value) => {
219
+ request = value;
220
+ return Promise.resolve({ ID: 'mailpit-id' });
221
+ }
222
+ }
223
+ });
224
+ return () => request;
225
+ }
226
+ describe('MailPitMailClient sender payload', () => {
227
+ it('normalizes a string sender and preserves SentMail', async () => {
228
+ const client = new MailPitMailClient({
229
+ type: 'mailpit',
230
+ url: 'http://127.0.0.1:8025'
231
+ });
232
+ const getRequest = captureMailPitRequest(client);
233
+ const sentMails = await client.sendMail({
234
+ from: 'sender@example.com',
235
+ to: 'recipient@example.com',
236
+ subject: 'Subject',
237
+ text: 'Body'
238
+ });
239
+ expect(getRequest()).toMatchObject({ From: { Email: 'sender@example.com' } });
240
+ expect(sentMails).toEqual([{ id: 'mailpit-id', recipients: ['recipient@example.com'] }]);
241
+ });
242
+ it('maps a named sender and excludes metadata', async () => {
243
+ const client = new MailPitMailClient({
244
+ type: 'mailpit',
245
+ url: 'http://127.0.0.1:8025'
246
+ });
247
+ const getRequest = captureMailPitRequest(client);
248
+ await client.sendMail({
249
+ from: { email: 'sender@example.com', name: 'Sender Name' },
250
+ to: 'recipient@example.com',
251
+ subject: 'Subject',
252
+ text: 'Body',
253
+ metadata: { deliveryId: 'secret-delivery-id' }
254
+ });
255
+ const request = getRequest();
256
+ expect(request).toMatchObject({ From: { Email: 'sender@example.com', Name: 'Sender Name' } });
257
+ expect(request).not.toHaveProperty('metadata');
258
+ expect(JSON.stringify(request)).not.toContain('secret-delivery-id');
259
+ });
260
+ it('maps a named default sender', async () => {
261
+ const client = new MailPitMailClient({
262
+ type: 'mailpit',
263
+ url: 'http://127.0.0.1:8025',
264
+ defaultFrom: { email: 'default@example.com', name: 'Default Sender' }
265
+ });
266
+ const getRequest = captureMailPitRequest(client);
267
+ await client.sendMail({
268
+ to: 'recipient@example.com',
269
+ subject: 'Subject',
270
+ text: 'Body'
271
+ });
272
+ expect(getRequest()).toMatchObject({ From: { Email: 'default@example.com', Name: 'Default Sender' } });
273
+ });
274
+ });
275
+ //# sourceMappingURL=mail-providers.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail-providers.test.js","sourceRoot":"","sources":["../../lib/clients/mail-providers.test.ts"],"names":[],"mappings":"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"}