@5minds/node-red-contrib-processcube-tools 1.2.0-feature-37541f-mg92jkdw → 1.2.0-feature-8f3d72-mg9cplxi

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 (61) hide show
  1. package/.env.template +7 -1
  2. package/email-receiver/email-receiver.js +304 -0
  3. package/email-sender/email-sender.js +178 -0
  4. package/examples/.gitkeep +0 -0
  5. package/file-storage/file-storage.html +203 -0
  6. package/file-storage/file-storage.js +148 -0
  7. package/package.json +17 -26
  8. package/{src/html-to-text/html-to-text.html → processcube-html-to-text/processcube-html-to-text.html} +3 -3
  9. package/processcube-html-to-text/processcube-html-to-text.js +22 -0
  10. package/storage/providers/fs.js +117 -0
  11. package/storage/providers/postgres.js +160 -0
  12. package/storage/storage-core.js +77 -0
  13. package/test/helpers/email-receiver.mocks.js +447 -0
  14. package/test/helpers/email-sender.mocks.js +368 -0
  15. package/test/integration/email-receiver.integration.test.js +515 -0
  16. package/test/integration/email-sender.integration.test.js +239 -0
  17. package/test/unit/email-receiver.unit.test.js +304 -0
  18. package/test/unit/email-sender.unit.test.js +570 -0
  19. package/.mocharc.json +0 -5
  20. package/src/custom-node-template/custom-node-template.html.template +0 -45
  21. package/src/custom-node-template/custom-node-template.ts.template +0 -69
  22. package/src/email-receiver/email-receiver.ts +0 -439
  23. package/src/email-sender/email-sender.ts +0 -210
  24. package/src/html-to-text/html-to-text.ts +0 -53
  25. package/src/index.ts +0 -12
  26. package/src/interfaces/EmailReceiverMessage.ts +0 -22
  27. package/src/interfaces/EmailSenderNodeProperties.ts +0 -37
  28. package/src/interfaces/FetchState.ts +0 -9
  29. package/src/interfaces/ImapConnectionConfig.ts +0 -14
  30. package/src/test/framework/advanced-test-patterns.ts +0 -224
  31. package/src/test/framework/generic-node-test-suite.ts +0 -58
  32. package/src/test/framework/index.ts +0 -17
  33. package/src/test/framework/integration-assertions.ts +0 -67
  34. package/src/test/framework/integration-scenario-builder.ts +0 -77
  35. package/src/test/framework/integration-test-runner.ts +0 -101
  36. package/src/test/framework/node-assertions.ts +0 -63
  37. package/src/test/framework/node-test-runner.ts +0 -260
  38. package/src/test/framework/test-scenario-builder.ts +0 -74
  39. package/src/test/framework/types.ts +0 -61
  40. package/src/test/helpers/email-receiver-test-configs.ts +0 -67
  41. package/src/test/helpers/email-receiver-test-flows.ts +0 -16
  42. package/src/test/helpers/email-sender-test-configs.ts +0 -123
  43. package/src/test/helpers/email-sender-test-flows.ts +0 -16
  44. package/src/test/integration/email-receiver.integration.test.ts +0 -41
  45. package/src/test/integration/email-sender.integration.test.ts +0 -129
  46. package/src/test/interfaces/email-data.ts +0 -10
  47. package/src/test/interfaces/email-receiver-config.ts +0 -12
  48. package/src/test/interfaces/email-sender-config.ts +0 -26
  49. package/src/test/interfaces/imap-config.ts +0 -9
  50. package/src/test/interfaces/imap-mailbox.ts +0 -5
  51. package/src/test/interfaces/mail-options.ts +0 -20
  52. package/src/test/interfaces/parsed-email.ts +0 -11
  53. package/src/test/interfaces/send-mail-result.ts +0 -7
  54. package/src/test/mocks/imap-mock.ts +0 -147
  55. package/src/test/mocks/mailparser-mock.ts +0 -82
  56. package/src/test/mocks/nodemailer-mock.ts +0 -118
  57. package/src/test/unit/email-receiver.unit.test.ts +0 -471
  58. package/src/test/unit/email-sender.unit.test.ts +0 -550
  59. package/tsconfig.json +0 -23
  60. /package/{src/email-receiver → email-receiver}/email-receiver.html +0 -0
  61. /package/{src/email-sender → email-sender}/email-sender.html +0 -0
@@ -1,123 +0,0 @@
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 };
@@ -1,16 +0,0 @@
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
- };
@@ -1,41 +0,0 @@
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
- });
@@ -1,129 +0,0 @@
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
- });
@@ -1,10 +0,0 @@
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
- }
@@ -1,12 +0,0 @@
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
- }
@@ -1,26 +0,0 @@
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
- }
@@ -1,9 +0,0 @@
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
- }
@@ -1,5 +0,0 @@
1
- export interface ImapMailbox {
2
- messages: { total: number };
3
- name: string;
4
- readOnly: boolean;
5
- }
@@ -1,20 +0,0 @@
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
- }
@@ -1,11 +0,0 @@
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
- }
@@ -1,7 +0,0 @@
1
- export interface SendMailResult {
2
- messageId: string;
3
- response: string;
4
- accepted: string[];
5
- rejected: string[];
6
- pending: string[];
7
- }
@@ -1,147 +0,0 @@
1
- import { EventEmitter } from 'events';
2
- import { ImapConfig } from '../interfaces/imap-config';
3
- import { ImapMailbox } from '../interfaces/imap-mailbox';
4
-
5
- export class MockImap extends EventEmitter {
6
- private config: ImapConfig;
7
- private isConnected = false;
8
- private currentBox: string | null = null;
9
- public state: string = 'disconnected';
10
-
11
- constructor(config: ImapConfig) {
12
- super();
13
- this.config = config;
14
- }
15
-
16
- connect(): void {
17
- setTimeout(() => {
18
- if (this.isConnectionInvalid()) {
19
- const error = new Error('Connection failed') as Error & { code: string };
20
- error.code = 'ENOTFOUND';
21
- this.emit('error', error);
22
- } else {
23
- this.isConnected = true;
24
- this.state = 'authenticated';
25
- this.emit('ready');
26
- }
27
- }, 10);
28
- }
29
-
30
- openBox(folder: string, readOnly: boolean, callback: (err: Error | null, box?: ImapMailbox) => void): void {
31
- if (!this.isConnected) {
32
- callback(new Error('Not connected'));
33
- return;
34
- }
35
-
36
- setTimeout(() => {
37
- this.currentBox = folder;
38
- callback(null, {
39
- messages: { total: this.getMessageCount(folder) },
40
- name: folder,
41
- readOnly,
42
- });
43
- }, 5);
44
- }
45
-
46
- search(criteria: any[], callback: (err: Error | null, results?: number[]) => void): void {
47
- setTimeout(() => {
48
- const messageIds = this.getFixedMessageIds();
49
- callback(null, messageIds);
50
- }, 10);
51
- }
52
-
53
- fetch(results: number[], options?: any) {
54
- const fetchEmitter = new EventEmitter();
55
-
56
- setTimeout(() => {
57
- results.forEach((id, index) => {
58
- setTimeout(() => {
59
- const mockMessage = this.createMockMessage(id);
60
- fetchEmitter.emit('message', mockMessage, id);
61
- }, index * 5);
62
- });
63
-
64
- // Emit end after all messages are processed
65
- setTimeout(() => fetchEmitter.emit('end'), results.length * 10 + 50);
66
- }, 10);
67
-
68
- return fetchEmitter;
69
- }
70
-
71
- end(): void {
72
- this.isConnected = false;
73
- this.state = 'disconnected';
74
- setTimeout(() => this.emit('end'), 5);
75
- }
76
-
77
- addFlags(source: number | number[], flags: string[], callback: (err: Error | null) => void): void {
78
- setTimeout(() => callback(null), 5);
79
- }
80
-
81
- // Private helper methods
82
- private isConnectionInvalid(): boolean {
83
- return (
84
- !this.config.host ||
85
- this.config.host.includes('invalid') ||
86
- this.config.host.includes('nonexistent') ||
87
- this.config.host.includes('unreachable') ||
88
- !this.config.user ||
89
- !this.config.password
90
- );
91
- }
92
-
93
- private getMessageCount(folder: string): number {
94
- const counts: Record<string, number> = {
95
- INBOX: 5,
96
- SENT: 2,
97
- DRAFTS: 1,
98
- JUNK: 0,
99
- };
100
- return counts[folder.toUpperCase()] || 3;
101
- }
102
-
103
- private getFixedMessageIds(): number[] {
104
- return this.currentBox === 'INBOX' ? [123, 456, 789, 1011, 1213] : [123, 456, 789];
105
- }
106
-
107
- private createMockMessage(id: number) {
108
- const message = new EventEmitter();
109
-
110
- setTimeout(() => {
111
- const emailContent = this.generateEmailContent(id);
112
- // Create a proper readable stream
113
- const { Readable } = require('stream');
114
- const mockStream = new Readable({
115
- read() {
116
- this.push(Buffer.from(emailContent));
117
- this.push(null); // End the stream
118
- },
119
- });
120
- message.emit('body', mockStream);
121
- }, 5);
122
-
123
- setTimeout(() => {
124
- message.emit('attributes', {
125
- uid: id,
126
- flags: Math.random() > 0.5 ? ['\\Seen'] : [],
127
- date: new Date(),
128
- size: Math.floor(Math.random() * 10000) + 500,
129
- });
130
- }, 10);
131
-
132
- return message;
133
- }
134
-
135
- private generateEmailContent(id: number): string {
136
- return [
137
- `Message-ID: <${id}@test.com>`,
138
- `From: sender${id}@test.com`,
139
- `To: recipient@test.com`,
140
- `Subject: Test Email ${id}`,
141
- `Date: ${new Date().toUTCString()}`,
142
- ``,
143
- `This is test email content for message ${id}.`,
144
- `Generated for testing purposes.`,
145
- ].join('\r\n');
146
- }
147
- }
@@ -1,82 +0,0 @@
1
- import { EmailData } from '../interfaces/email-data';
2
-
3
- export function createMockMailparser() {
4
- return function mockMailParser(stream: NodeJS.ReadableStream, callback: (err: Error | null, parsed?: any) => void) {
5
- // Read the stream data
6
- let emailData = '';
7
-
8
- stream.on('data', (chunk) => {
9
- emailData += chunk.toString();
10
- });
11
-
12
- stream.on('end', () => {
13
- // Parse the email content using your existing helper
14
- const parsedData = parseEmailContent(emailData);
15
- const parsedMail = {
16
- subject: parsedData.subject || 'Mock Email Subject',
17
- text: parsedData.text || 'Mock email content',
18
- html: parsedData.html || '<p>Mock email content</p>',
19
- from: {
20
- text: parsedData.from || 'sender@test.com',
21
- value: [{ address: parsedData.from || parsedData.from || 'sender@test.com' }],
22
- },
23
- replyTo: {
24
- text: parsedData.from || 'sender@test.com',
25
- value: [{ address: parsedData.to || parsedData.to || 'recipient@test.com' }],
26
- },
27
- date: parsedData.date || new Date(),
28
- messageId: parsedData.messageId || '<mock@test.com>',
29
- headers: new Map([
30
- ['message-id', parsedData.messageId || '<mock@test.com>'],
31
- ['subject', parsedData.subject || 'Mock Email Subject'],
32
- ['from', parsedData.from || 'sender@test.com'],
33
- ]),
34
- attachments: parsedData.attachments || [],
35
- };
36
-
37
- // Call the callback asynchronously to simulate real parsing
38
- setTimeout(() => {
39
- callback(null, parsedMail);
40
- }, 5);
41
- });
42
-
43
- stream.on('error', (err) => {
44
- callback(err);
45
- });
46
- };
47
- }
48
-
49
- // Helper to parse basic email content
50
- function parseEmailContent(content: string): Partial<EmailData> {
51
- const lines = content.split('\r\n');
52
- const result: Partial<EmailData> = {};
53
- let bodyStart = false;
54
- let bodyLines: string[] = [];
55
-
56
- for (const line of lines) {
57
- if (!bodyStart) {
58
- if (line === '') {
59
- bodyStart = true;
60
- continue;
61
- }
62
-
63
- if (line.startsWith('Subject: ')) {
64
- result.subject = line.substring(9);
65
- } else if (line.startsWith('From: ')) {
66
- result.from = line.substring(6);
67
- } else if (line.startsWith('To: ')) {
68
- result.to = line.substring(4);
69
- } else if (line.startsWith('Message-ID: ')) {
70
- result.messageId = line.substring(12);
71
- }
72
- } else {
73
- bodyLines.push(line);
74
- }
75
- }
76
-
77
- if (bodyLines.length > 0) {
78
- result.text = bodyLines.join('\n');
79
- }
80
-
81
- return result;
82
- }