@5minds/node-red-contrib-processcube-tools 1.2.0-develop-2eb127-mg68t7xt → 1.2.0-develop-59ef22-mg9d9ja5

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 (55) hide show
  1. package/.mocharc.json +5 -0
  2. package/package.json +26 -10
  3. package/src/custom-node-template/custom-node-template.html.template +45 -0
  4. package/src/custom-node-template/custom-node-template.ts.template +69 -0
  5. package/src/email-receiver/email-receiver.ts +439 -0
  6. package/src/email-sender/email-sender.ts +210 -0
  7. package/{processcube-html-to-text/processcube-html-to-text.html → src/html-to-text/html-to-text.html} +3 -3
  8. package/src/html-to-text/html-to-text.ts +53 -0
  9. package/src/index.ts +12 -0
  10. package/src/interfaces/EmailReceiverMessage.ts +22 -0
  11. package/src/interfaces/EmailSenderNodeProperties.ts +37 -0
  12. package/src/interfaces/FetchState.ts +9 -0
  13. package/src/interfaces/ImapConnectionConfig.ts +14 -0
  14. package/src/test/framework/advanced-test-patterns.ts +224 -0
  15. package/src/test/framework/generic-node-test-suite.ts +58 -0
  16. package/src/test/framework/index.ts +17 -0
  17. package/src/test/framework/integration-assertions.ts +67 -0
  18. package/src/test/framework/integration-scenario-builder.ts +77 -0
  19. package/src/test/framework/integration-test-runner.ts +101 -0
  20. package/src/test/framework/node-assertions.ts +63 -0
  21. package/src/test/framework/node-test-runner.ts +260 -0
  22. package/src/test/framework/test-scenario-builder.ts +74 -0
  23. package/src/test/framework/types.ts +61 -0
  24. package/src/test/helpers/email-receiver-test-configs.ts +67 -0
  25. package/src/test/helpers/email-receiver-test-flows.ts +16 -0
  26. package/src/test/helpers/email-sender-test-configs.ts +123 -0
  27. package/src/test/helpers/email-sender-test-flows.ts +16 -0
  28. package/src/test/integration/email-receiver.integration.test.ts +41 -0
  29. package/src/test/integration/email-sender.integration.test.ts +129 -0
  30. package/src/test/interfaces/email-data.ts +10 -0
  31. package/src/test/interfaces/email-receiver-config.ts +12 -0
  32. package/src/test/interfaces/email-sender-config.ts +26 -0
  33. package/src/test/interfaces/imap-config.ts +9 -0
  34. package/src/test/interfaces/imap-mailbox.ts +5 -0
  35. package/src/test/interfaces/mail-options.ts +20 -0
  36. package/src/test/interfaces/parsed-email.ts +11 -0
  37. package/src/test/interfaces/send-mail-result.ts +7 -0
  38. package/src/test/mocks/imap-mock.ts +147 -0
  39. package/src/test/mocks/mailparser-mock.ts +82 -0
  40. package/src/test/mocks/nodemailer-mock.ts +118 -0
  41. package/src/test/unit/email-receiver.unit.test.ts +471 -0
  42. package/src/test/unit/email-sender.unit.test.ts +550 -0
  43. package/tsconfig.json +23 -0
  44. package/email-receiver/email-receiver.js +0 -304
  45. package/email-sender/email-sender.js +0 -178
  46. package/examples/.gitkeep +0 -0
  47. package/processcube-html-to-text/processcube-html-to-text.js +0 -22
  48. package/test/helpers/email-receiver.mocks.js +0 -447
  49. package/test/helpers/email-sender.mocks.js +0 -368
  50. package/test/integration/email-receiver.integration.test.js +0 -515
  51. package/test/integration/email-sender.integration.test.js +0 -239
  52. package/test/unit/email-receiver.unit.test.js +0 -304
  53. package/test/unit/email-sender.unit.test.js +0 -570
  54. /package/{email-receiver → src/email-receiver}/email-receiver.html +0 -0
  55. /package/{email-sender → src/email-sender}/email-sender.html +0 -0
@@ -0,0 +1,61 @@
1
+ import type { Node, NodeMessageInFlow } from 'node-red';
2
+
3
+ export interface TestScenario {
4
+ name: string;
5
+ config: any;
6
+ input?: any;
7
+ expectedOutput?: any;
8
+ expectedError?: string | RegExp;
9
+ expectedStatus?: { fill: string; shape?: string; text?: string };
10
+ timeout?: number;
11
+ mockOptions?: MockNodeREDOptions;
12
+ }
13
+
14
+ export interface TestContext {
15
+ mockRED: any;
16
+ nodeInstance: any;
17
+ messages: any[];
18
+ errors: any[];
19
+ statuses: any[];
20
+ logs?: any[];
21
+ warnings?: any[];
22
+ debugs?: any[];
23
+ }
24
+
25
+ export interface MockNodeREDOptions {
26
+ dependencies?: Record<string, any>;
27
+ onHandler?: (this: Node, event: string, callback: Function) => void;
28
+ sendHandler?: (msg: any) => void;
29
+ errorHandler?: (err: any) => void;
30
+ statusHandler?: (status: any) => void;
31
+ logHandler?: (msg: any) => void;
32
+ warnHandler?: (msg: any) => void;
33
+ debugHandler?: (msg: any) => void;
34
+ traceHandler?: (msg: any) => void;
35
+ }
36
+
37
+ // Integration Test Types
38
+ export interface IntegrationTestScenario {
39
+ name: string;
40
+ flow: any[];
41
+ nodeId: string;
42
+ input?: any;
43
+ expectedMessages?: Array<{
44
+ nodeId: string;
45
+ expectedMsg: any;
46
+ timeout?: number;
47
+ }>;
48
+ timeout?: number;
49
+ setup?: (nodes: Record<string, Node>) => void;
50
+ cleanup?: () => void;
51
+ }
52
+
53
+ export interface IntegrationTestContext {
54
+ nodes: Record<string, Node>;
55
+ messages: Array<{
56
+ nodeId: string;
57
+ message: NodeMessageInFlow;
58
+ timestamp: number;
59
+ }>;
60
+ errors: any[];
61
+ }
@@ -0,0 +1,67 @@
1
+ import { EmailReceiverConfig } from '../interfaces/email-receiver-config';
2
+
3
+ export const EmailReceiverTestConfigs = {
4
+ valid: {
5
+ id: 'test-node-1',
6
+ type: 'email-receiver',
7
+ name: 'Test Email Receiver',
8
+ host: 'imap.test.com',
9
+ port: 993,
10
+ user: 'test@test.com',
11
+ password: 'testpass',
12
+ folder: ['INBOX'],
13
+ tls: true,
14
+ markseen: true,
15
+ } as EmailReceiverConfig,
16
+
17
+ minimal: {
18
+ id: 'test-node-minimal',
19
+ type: 'email-receiver',
20
+ host: 'imap.minimal.com',
21
+ port: 993,
22
+ user: 'minimal@test.com',
23
+ password: 'minimalpass',
24
+ folder: 'INBOX',
25
+ } as EmailReceiverConfig,
26
+
27
+ arrayFolders: {
28
+ id: 'test-node-array',
29
+ type: 'email-receiver',
30
+ name: 'Array Folders Test',
31
+ host: 'imap.test.com',
32
+ port: 993,
33
+ user: 'test@test.com',
34
+ password: 'testpass',
35
+ folder: ['INBOX', 'SENT', 'DRAFTS'],
36
+ } as EmailReceiverConfig,
37
+
38
+ invalidFolderType: {
39
+ id: 'test-node-invalid-folder',
40
+ type: 'email-receiver',
41
+ host: 'imap.test.com',
42
+ port: 993,
43
+ user: 'test@test.com',
44
+ password: 'testpass',
45
+ folder: 123 as any, // Invalid type
46
+ } as EmailReceiverConfig,
47
+
48
+ invalidConfig: {
49
+ id: 'test-node-invalid',
50
+ type: 'email-receiver',
51
+ host: '', // Missing host
52
+ port: 993,
53
+ user: 'test@test.com',
54
+ password: '', // Missing password
55
+ folder: ['INBOX'],
56
+ } as EmailReceiverConfig,
57
+
58
+ networkError: {
59
+ id: 'test-node-network-error',
60
+ type: 'email-receiver',
61
+ host: 'unreachable.invalid.host.com',
62
+ port: 993,
63
+ user: 'test@test.com',
64
+ password: 'testpass',
65
+ folder: ['INBOX'],
66
+ } as EmailReceiverConfig,
67
+ };
@@ -0,0 +1,16 @@
1
+ import { EmailReceiverTestConfigs } from './email-receiver-test-configs';
2
+
3
+ export const testFlows = {
4
+ single: [EmailReceiverTestConfigs.valid],
5
+
6
+ connected: [
7
+ { ...EmailReceiverTestConfigs.valid, wires: [['h1']] },
8
+ { id: 'h1', type: 'helper' },
9
+ ],
10
+
11
+ multiOutput: [
12
+ { ...EmailReceiverTestConfigs.valid, wires: [['h1', 'h2']] },
13
+ { id: 'h1', type: 'helper' },
14
+ { id: 'h2', type: 'helper' },
15
+ ],
16
+ };
@@ -0,0 +1,123 @@
1
+ import { EmailSenderConfig } from '../interfaces/email-sender-config';
2
+
3
+ const EmailSenderTestConfigs = {
4
+ valid: {
5
+ id: 'test-sender-1',
6
+ type: 'email-sender',
7
+ name: 'Test Email Sender',
8
+ sender: 'Test Sender',
9
+ address: 'test.sender@example.com',
10
+ to: 'recipient@example.com',
11
+ subject: 'Test Subject',
12
+ htmlContent: '<b>Test Content</b>',
13
+ attachments: '',
14
+ attachmentsType: 'str',
15
+ host: 'smtp.example.com',
16
+ port: 587,
17
+ user: 'testuser',
18
+ password: 'testpass',
19
+ secure: false,
20
+ rejectUnauthorized: true,
21
+ } as EmailSenderConfig,
22
+
23
+ minimal: {
24
+ id: 'test-sender-minimal',
25
+ type: 'email-sender',
26
+ sender: 'Test Sender',
27
+ address: 'test.sender@example.com',
28
+ to: 'recipient@example.com',
29
+ subject: 'Minimal Subject',
30
+ htmlContent: 'Minimal content',
31
+ host: 'smtp.minimal.com',
32
+ port: 587,
33
+ user: 'minimal-user',
34
+ password: 'minimal-pass',
35
+ secure: false,
36
+ rejectUnauthorized: true,
37
+ } as EmailSenderConfig,
38
+
39
+ invalid: {
40
+ id: 'test-sender-invalid',
41
+ type: 'email-sender',
42
+ name: 'Invalid Sender',
43
+ sender: '', // Missing sender
44
+ to: 'test@example.com',
45
+ subject: 'Invalid Test',
46
+ host: '', // Missing host
47
+ port: 587,
48
+ user: 'user',
49
+ password: '', // Missing password
50
+ } as EmailSenderConfig,
51
+
52
+ minimalDataDriven: {
53
+ id: 'test-sender-minimal',
54
+ type: 'email-sender',
55
+ sender: 'Test Sender',
56
+ address: 'test.sender@example.com',
57
+ subject: 'Minimal Subject',
58
+ htmlContent: 'Minimal content',
59
+ host: 'smtp.minimal.com',
60
+ port: 587,
61
+ user: 'minimal-user',
62
+ password: 'minimal-pass',
63
+ secure: false,
64
+ rejectUnauthorized: true,
65
+ } as EmailSenderConfig,
66
+
67
+ withAttachments: {
68
+ id: 'test-sender-attachments',
69
+ type: 'email-sender',
70
+ name: 'Sender With Attachments',
71
+ to: 'recipient@example.com',
72
+ subject: 'Attachment Test',
73
+ htmlContent: 'Email with attachments',
74
+ attachments: JSON.stringify([{ filename: 'test.txt', content: 'Test attachment' }]),
75
+ attachmentsType: 'json',
76
+ host: 'smtp.example.com',
77
+ port: 587,
78
+ user: 'testuser',
79
+ password: 'testpass',
80
+ } as EmailSenderConfig,
81
+
82
+ errorScenarios: {
83
+ networkError: {
84
+ id: 'test-sender-network-error',
85
+ type: 'email-sender',
86
+ name: 'Network Error Scenario',
87
+ host: 'unreachable.invalid.host.com',
88
+ shouldFail: true,
89
+ } as EmailSenderConfig,
90
+
91
+ rejectedEmail: {
92
+ id: 'test-sender-rejected',
93
+ type: 'email-sender',
94
+ name: 'Rejected Email Scenario',
95
+ rejectedEmails: ['recipient@example.com'],
96
+ } as EmailSenderConfig,
97
+
98
+ pendingEmail: {
99
+ id: 'test-sender-pending',
100
+ type: 'email-sender',
101
+ name: 'Pending Email Scenario',
102
+ pendingEmails: ['recipient@example.com'],
103
+ } as EmailSenderConfig,
104
+ },
105
+ };
106
+
107
+ const baseConfig = EmailSenderTestConfigs.valid;
108
+ EmailSenderTestConfigs.errorScenarios.networkError = {
109
+ ...baseConfig,
110
+ ...EmailSenderTestConfigs.errorScenarios.networkError,
111
+ };
112
+
113
+ EmailSenderTestConfigs.errorScenarios.rejectedEmail = {
114
+ ...baseConfig,
115
+ ...EmailSenderTestConfigs.errorScenarios.rejectedEmail,
116
+ };
117
+
118
+ EmailSenderTestConfigs.errorScenarios.pendingEmail = {
119
+ ...baseConfig,
120
+ ...EmailSenderTestConfigs.errorScenarios.pendingEmail,
121
+ };
122
+
123
+ export { EmailSenderTestConfigs };
@@ -0,0 +1,16 @@
1
+ import { EmailSenderTestConfigs } from './email-sender-test-configs';
2
+
3
+ export const testFlows = {
4
+ single: [EmailSenderTestConfigs.valid],
5
+
6
+ connected: [
7
+ { ...EmailSenderTestConfigs.valid, wires: [['h1']] },
8
+ { id: 'h1', type: 'helper' },
9
+ ],
10
+
11
+ multiOutput: [
12
+ { ...EmailSenderTestConfigs.valid, wires: [['h1', 'h2']] },
13
+ { id: 'h1', type: 'helper' },
14
+ { id: 'h2', type: 'helper' },
15
+ ],
16
+ };
@@ -0,0 +1,41 @@
1
+ import { expect } from 'chai';
2
+ import emailReceiverNode from '../../email-receiver/email-receiver';
3
+ import { EmailReceiverTestConfigs } from '../helpers/email-receiver-test-configs';
4
+
5
+ // Import our comprehensive test framework
6
+ import { NodeTestRunner, NodeAssertions, type TestScenario, MockNodeREDOptions } from '../framework';
7
+
8
+ import { MockImap } from '../mocks/imap-mock';
9
+ import { createMockMailparser } from '../mocks/mailparser-mock';
10
+
11
+ describe('E-Mail Receiver Node - Integration Tests', function () {
12
+ // ========================================================================
13
+ // INTEGRATION WITH EXISTING MOCK SYSTEM
14
+ // ========================================================================
15
+
16
+ describe('Mock System Integration', function () {
17
+ it('should work with all mock configurations', async function () {
18
+ const mockConfigs = [
19
+ EmailReceiverTestConfigs.valid,
20
+ EmailReceiverTestConfigs.minimal,
21
+ EmailReceiverTestConfigs.arrayFolders,
22
+ ];
23
+
24
+ for (const config of mockConfigs) {
25
+ const scenario: TestScenario = {
26
+ name: `mock integration - ${config.name || 'unnamed'}`,
27
+ config,
28
+ input: { payload: 'test' },
29
+ timeout: 2000,
30
+ };
31
+
32
+ const context = await NodeTestRunner.runScenario(emailReceiverNode, scenario);
33
+ expect(context.nodeInstance).to.exist;
34
+
35
+ if (config.name) {
36
+ NodeAssertions.expectNodeProperty(context, 'name', config.name);
37
+ }
38
+ }
39
+ });
40
+ });
41
+ });
@@ -0,0 +1,129 @@
1
+ import { describe, it } from 'mocha';
2
+ import { expect } from 'chai';
3
+ import {
4
+ IntegrationTestRunner,
5
+ IntegrationAssertions,
6
+ IntegrationScenarioBuilder,
7
+ type IntegrationTestScenario,
8
+ } from '../framework';
9
+ import { EmailSenderTestConfigs } from '../helpers/email-sender-test-configs';
10
+ import { testFlows } from '../helpers/email-sender-test-flows';
11
+
12
+ const emailSenderNode = require('../../email-sender/email-sender');
13
+
14
+ describe('E-Mail Sender Node - Integration Tests (Framework)', function () {
15
+ // ========================================================================
16
+ // NODE LOADING TESTS USING FRAMEWORK
17
+ // ========================================================================
18
+
19
+ describe('Node Loading (Enhanced)', function () {
20
+ const loadingTests = new IntegrationScenarioBuilder()
21
+ .addLoadingScenario('valid configuration', [EmailSenderTestConfigs.valid], EmailSenderTestConfigs.valid.id)
22
+ .addLoadingScenario(
23
+ 'minimal configuration',
24
+ [EmailSenderTestConfigs.minimal],
25
+ EmailSenderTestConfigs.minimal.id,
26
+ );
27
+
28
+ loadingTests.getScenarios().forEach((scenario) => {
29
+ it(`should load with ${scenario.name}`, async function () {
30
+ const context = await IntegrationTestRunner.runIntegrationScenario(emailSenderNode, scenario);
31
+
32
+ IntegrationAssertions.expectNodeExists(context, scenario.nodeId);
33
+ IntegrationAssertions.expectNodeProperty(context, scenario.nodeId, 'type', 'email-sender');
34
+
35
+ const nodeConfig = scenario.flow.find((n) => n.id === scenario.nodeId);
36
+ if (nodeConfig.name) {
37
+ IntegrationAssertions.expectNodeProperty(context, scenario.nodeId, 'name', nodeConfig.name);
38
+ }
39
+ });
40
+ });
41
+ });
42
+
43
+ // ========================================================================
44
+ // CONNECTION TESTS USING FRAMEWORK
45
+ // ========================================================================
46
+
47
+ describe('Node Connections', function () {
48
+ it('should load node in a flow configuration', async function () {
49
+ const scenario: IntegrationTestScenario = {
50
+ name: 'node in flow',
51
+ flow: testFlows.connected,
52
+ nodeId: EmailSenderTestConfigs.valid.id,
53
+ timeout: 2000,
54
+ };
55
+
56
+ const context = await IntegrationTestRunner.runIntegrationScenario(emailSenderNode, scenario);
57
+
58
+ // Only verify the main node exists - helper nodes aren't actually created
59
+ IntegrationAssertions.expectNodeExists(context, EmailSenderTestConfigs.valid.id);
60
+ IntegrationAssertions.expectNodeProperty(
61
+ context,
62
+ EmailSenderTestConfigs.valid.id,
63
+ 'name',
64
+ EmailSenderTestConfigs.valid.name,
65
+ );
66
+ });
67
+ });
68
+
69
+ // ========================================================================
70
+ // CONFIGURATION VALIDATION INTEGRATION
71
+ // ========================================================================
72
+
73
+ describe('Configuration Validation Integration', function () {
74
+ const validationTests = new IntegrationScenarioBuilder()
75
+ .addScenario({
76
+ name: 'minimal configuration handling',
77
+ flow: [EmailSenderTestConfigs.minimal],
78
+ nodeId: EmailSenderTestConfigs.minimal.id,
79
+ input: { payload: 'test', topic: 'test' },
80
+ timeout: 2000,
81
+ })
82
+ .addScenario({
83
+ name: 'complex configuration handling',
84
+ flow: [
85
+ {
86
+ ...EmailSenderTestConfigs.valid,
87
+ attachments: JSON.stringify([{ filename: 'config-test.txt', content: 'Configuration test' }]),
88
+ priority: 'high',
89
+ },
90
+ ],
91
+ nodeId: EmailSenderTestConfigs.valid.id,
92
+ input: { payload: 'complex test', topic: 'complex' },
93
+ timeout: 3000,
94
+ });
95
+
96
+ validationTests.getScenarios().forEach((scenario) => {
97
+ it(`should handle ${scenario.name}`, async function () {
98
+ const context = await IntegrationTestRunner.runIntegrationScenario(emailSenderNode, scenario);
99
+ IntegrationAssertions.expectNodeExists(context, scenario.nodeId);
100
+ // Node should handle different configurations gracefully
101
+ });
102
+ });
103
+ });
104
+
105
+ // ========================================================================
106
+ // LIFECYCLE TESTS USING FRAMEWORK
107
+ // ========================================================================
108
+
109
+ describe('Node Lifecycle', function () {
110
+ it('should handle multiple load/unload cycles', async function () {
111
+ const cycles = 3;
112
+
113
+ for (let i = 0; i < cycles; i++) {
114
+ const scenario: IntegrationTestScenario = {
115
+ name: `lifecycle cycle ${i}`,
116
+ flow: testFlows.single,
117
+ nodeId: EmailSenderTestConfigs.valid.id,
118
+ input: { payload: `test cycle ${i}`, topic: `cycle ${i}` },
119
+ timeout: 1000,
120
+ };
121
+
122
+ const context = await IntegrationTestRunner.runIntegrationScenario(emailSenderNode, scenario);
123
+ IntegrationAssertions.expectNodeExists(context, EmailSenderTestConfigs.valid.id);
124
+
125
+ await new Promise((resolve) => setTimeout(resolve, 50));
126
+ }
127
+ });
128
+ });
129
+ });
@@ -0,0 +1,10 @@
1
+ export interface EmailData {
2
+ subject?: string;
3
+ from?: string;
4
+ to?: string;
5
+ text?: string;
6
+ html?: string;
7
+ date?: Date;
8
+ messageId?: string;
9
+ attachments?: any[];
10
+ }
@@ -0,0 +1,12 @@
1
+ export interface EmailReceiverConfig {
2
+ id: string;
3
+ type: string;
4
+ name?: string;
5
+ host: string;
6
+ port: number;
7
+ user: string;
8
+ password: string;
9
+ folder: string | string[];
10
+ tls?: boolean;
11
+ markseen?: boolean;
12
+ }
@@ -0,0 +1,26 @@
1
+ export interface EmailSenderConfig {
2
+ id: string;
3
+ type: string;
4
+ name?: string;
5
+ sender?: string;
6
+ address?: string;
7
+ to?: string;
8
+ cc?: string;
9
+ bcc?: string;
10
+ subject?: string;
11
+ htmlContent?: string;
12
+ attachments?: string;
13
+ attachmentsType?: string;
14
+ host?: string;
15
+ port?: number;
16
+ user?: string;
17
+ password?: string;
18
+ secure?: boolean;
19
+ rejectUnauthorized?: boolean;
20
+ wires?: string[][];
21
+ // Test-specific properties
22
+ shouldFail?: boolean;
23
+ rejectedEmails?: string[];
24
+ pendingEmails?: string[];
25
+ acceptedEmails?: string[];
26
+ }
@@ -0,0 +1,9 @@
1
+ export interface ImapConfig {
2
+ host: string;
3
+ port: number;
4
+ secure?: boolean;
5
+ user: string;
6
+ password: string;
7
+ authTimeout?: number;
8
+ connTimeout?: number;
9
+ }
@@ -0,0 +1,5 @@
1
+ export interface ImapMailbox {
2
+ messages: { total: number };
3
+ name: string;
4
+ readOnly: boolean;
5
+ }
@@ -0,0 +1,20 @@
1
+ export interface MailOptions {
2
+ to: string | string[];
3
+ from?: string;
4
+ subject?: string;
5
+ html?: string;
6
+ text?: string;
7
+ attachments?: any[];
8
+ [key: string]: any;
9
+ }
10
+
11
+ export interface MockNodemailerOptions {
12
+ onSendMail?: (mailOptions: MailOptions) => void;
13
+ shouldFail?: boolean;
14
+ shouldFailVerify?: boolean;
15
+ failureMessage?: string;
16
+ failureCode?: string;
17
+ rejectedEmails?: string[];
18
+ pendingEmails?: string[];
19
+ acceptedEmails?: string[];
20
+ }
@@ -0,0 +1,11 @@
1
+ export interface ParsedEmail {
2
+ subject?: string;
3
+ text?: string;
4
+ html?: string;
5
+ from?: { text: string; value: Array<{ address: string; name?: string }> };
6
+ to?: { text: string; value: Array<{ address: string; name?: string }> };
7
+ date?: Date;
8
+ messageId?: string;
9
+ headers: Map<string, string>;
10
+ attachments?: any[];
11
+ }
@@ -0,0 +1,7 @@
1
+ export interface SendMailResult {
2
+ messageId: string;
3
+ response: string;
4
+ accepted: string[];
5
+ rejected: string[];
6
+ pending: string[];
7
+ }