@happyvertical/email 0.74.8

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.
package/dist/index.js ADDED
@@ -0,0 +1,2165 @@
1
+ import { google } from "googleapis";
2
+ import { simpleParser } from "mailparser";
3
+ import { createLogger } from "@happyvertical/logger";
4
+ import { ImapFlow } from "imapflow";
5
+ import Pop3Command from "node-pop3";
6
+ import nodemailer from "nodemailer";
7
+ class EmailError extends Error {
8
+ code;
9
+ provider;
10
+ cause;
11
+ constructor(message, code, provider, cause) {
12
+ super(message);
13
+ this.name = "EmailError";
14
+ this.code = code;
15
+ this.provider = provider;
16
+ this.cause = cause;
17
+ }
18
+ }
19
+ class ConnectionError extends EmailError {
20
+ constructor(message, provider, cause) {
21
+ super(message, "CONNECTION_ERROR", provider, cause);
22
+ this.name = "ConnectionError";
23
+ }
24
+ }
25
+ class TimeoutError extends EmailError {
26
+ constructor(message, provider, cause) {
27
+ super(message, "TIMEOUT_ERROR", provider, cause);
28
+ this.name = "TimeoutError";
29
+ }
30
+ }
31
+ class AuthenticationError extends EmailError {
32
+ constructor(message, provider, cause) {
33
+ super(message, "AUTHENTICATION_ERROR", provider, cause);
34
+ this.name = "AuthenticationError";
35
+ }
36
+ }
37
+ class AuthorizationError extends EmailError {
38
+ constructor(message, provider, cause) {
39
+ super(message, "AUTHORIZATION_ERROR", provider, cause);
40
+ this.name = "AuthorizationError";
41
+ }
42
+ }
43
+ class MessageNotFoundError extends EmailError {
44
+ messageId;
45
+ constructor(messageId, provider) {
46
+ super(`Message not found: ${messageId}`, "MESSAGE_NOT_FOUND", provider);
47
+ this.name = "MessageNotFoundError";
48
+ this.messageId = messageId;
49
+ }
50
+ }
51
+ class InvalidMessageError extends EmailError {
52
+ constructor(message, provider, cause) {
53
+ super(message, "INVALID_MESSAGE", provider, cause);
54
+ this.name = "InvalidMessageError";
55
+ }
56
+ }
57
+ class FolderNotFoundError extends EmailError {
58
+ folder;
59
+ constructor(folder, provider) {
60
+ super(`Folder not found: ${folder}`, "FOLDER_NOT_FOUND", provider);
61
+ this.name = "FolderNotFoundError";
62
+ this.folder = folder;
63
+ }
64
+ }
65
+ class FolderExistsError extends EmailError {
66
+ folder;
67
+ constructor(folder, provider) {
68
+ super(`Folder already exists: ${folder}`, "FOLDER_EXISTS", provider);
69
+ this.name = "FolderExistsError";
70
+ this.folder = folder;
71
+ }
72
+ }
73
+ class SendError extends EmailError {
74
+ accepted;
75
+ rejected;
76
+ constructor(message, accepted, rejected, provider, cause) {
77
+ super(message, "SEND_ERROR", provider, cause);
78
+ this.name = "SendError";
79
+ this.accepted = accepted;
80
+ this.rejected = rejected;
81
+ }
82
+ }
83
+ class AttachmentError extends EmailError {
84
+ filename;
85
+ constructor(message, filename, provider, cause) {
86
+ super(message, "ATTACHMENT_ERROR", provider, cause);
87
+ this.name = "AttachmentError";
88
+ this.filename = filename;
89
+ }
90
+ }
91
+ class BaseEmailClient {
92
+ config;
93
+ logger;
94
+ connected = false;
95
+ constructor(config) {
96
+ this.config = this.validateConfig(config);
97
+ this.logger = config.logger || createLogger({ level: "info" });
98
+ }
99
+ // ========================================================================
100
+ // Connection management
101
+ // ========================================================================
102
+ isConnected() {
103
+ return this.connected;
104
+ }
105
+ // ========================================================================
106
+ // Validation helpers
107
+ // ========================================================================
108
+ validateConfig(config) {
109
+ if (!config.type) {
110
+ throw new EmailError("Email client type is required", "INVALID_CONFIG");
111
+ }
112
+ return config;
113
+ }
114
+ validateEmail(email) {
115
+ if (!email.address) {
116
+ throw new InvalidMessageError(
117
+ "Email address is required",
118
+ this.getAdapter()
119
+ );
120
+ }
121
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
122
+ if (!emailRegex.test(email.address)) {
123
+ throw new InvalidMessageError(
124
+ `Invalid email address: ${email.address}`,
125
+ this.getAdapter()
126
+ );
127
+ }
128
+ }
129
+ validateMessage(message) {
130
+ if (!message.from) {
131
+ throw new InvalidMessageError(
132
+ "Message must have a sender (from)",
133
+ this.getAdapter()
134
+ );
135
+ }
136
+ this.validateEmail(message.from);
137
+ if (!message.to || message.to.length === 0) {
138
+ throw new InvalidMessageError(
139
+ "Message must have at least one recipient (to)",
140
+ this.getAdapter()
141
+ );
142
+ }
143
+ for (const recipient of message.to) {
144
+ this.validateEmail(recipient);
145
+ }
146
+ if (message.cc) {
147
+ for (const recipient of message.cc) {
148
+ this.validateEmail(recipient);
149
+ }
150
+ }
151
+ if (message.bcc) {
152
+ for (const recipient of message.bcc) {
153
+ this.validateEmail(recipient);
154
+ }
155
+ }
156
+ if (!message.subject) {
157
+ throw new InvalidMessageError(
158
+ "Message must have a subject",
159
+ this.getAdapter()
160
+ );
161
+ }
162
+ if (!message.text && !message.html) {
163
+ throw new InvalidMessageError(
164
+ "Message must have either text or HTML content",
165
+ this.getAdapter()
166
+ );
167
+ }
168
+ }
169
+ // ========================================================================
170
+ // Error mapping
171
+ // ========================================================================
172
+ mapError(error) {
173
+ if (error instanceof EmailError) {
174
+ return error;
175
+ }
176
+ if (error instanceof Error) {
177
+ return new EmailError(
178
+ error.message,
179
+ "UNKNOWN_ERROR",
180
+ this.getAdapter(),
181
+ error
182
+ );
183
+ }
184
+ return new EmailError(String(error), "UNKNOWN_ERROR", this.getAdapter());
185
+ }
186
+ // ========================================================================
187
+ // Utility methods
188
+ // ========================================================================
189
+ normalizeMessageIds(messageId) {
190
+ return Array.isArray(messageId) ? messageId : [messageId];
191
+ }
192
+ debug(message, data) {
193
+ if (this.config.debug) {
194
+ this.logger.debug(message, data);
195
+ }
196
+ }
197
+ }
198
+ class GmailAdapter extends BaseEmailClient {
199
+ gmail = null;
200
+ options;
201
+ auth = null;
202
+ constructor(options) {
203
+ super({
204
+ type: "gmail",
205
+ debug: options.debug
206
+ });
207
+ this.options = options;
208
+ }
209
+ // ========================================================================
210
+ // Connection management
211
+ // ========================================================================
212
+ async connect() {
213
+ try {
214
+ const auth = new google.auth.OAuth2(
215
+ this.options.auth.clientId,
216
+ this.options.auth.clientSecret
217
+ );
218
+ auth.setCredentials({
219
+ refresh_token: this.options.auth.refreshToken,
220
+ access_token: this.options.auth.accessToken
221
+ });
222
+ this.auth = auth;
223
+ this.gmail = google.gmail({ version: "v1", auth });
224
+ await this.gmail.users.getProfile({ userId: this.getUserId() });
225
+ this.connected = true;
226
+ this.debug("Connected to Gmail API");
227
+ } catch (error) {
228
+ throw this.mapGmailError(error);
229
+ }
230
+ }
231
+ async disconnect() {
232
+ this.gmail = null;
233
+ this.auth = null;
234
+ this.connected = false;
235
+ this.debug("Disconnected from Gmail API");
236
+ }
237
+ // ========================================================================
238
+ // Send operations
239
+ // ========================================================================
240
+ async send(message, options) {
241
+ this.ensureConnected();
242
+ this.validateMessage(message);
243
+ try {
244
+ const email = this.buildRFC2822Message(message, options);
245
+ const encodedEmail = Buffer.from(email).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
246
+ const response = await this.gmail?.users.messages.send({
247
+ userId: this.getUserId(),
248
+ requestBody: {
249
+ raw: encodedEmail
250
+ }
251
+ });
252
+ if (!response) {
253
+ throw new SendError("Failed to send message", [], [], "gmail");
254
+ }
255
+ return {
256
+ messageId: response.data.id || "",
257
+ accepted: message.to.map((addr) => addr.address),
258
+ rejected: [],
259
+ response: `Message sent: ${response.data.id}`
260
+ };
261
+ } catch (error) {
262
+ throw this.mapGmailError(error);
263
+ }
264
+ }
265
+ // ========================================================================
266
+ // Receive operations
267
+ // ========================================================================
268
+ async fetch(options) {
269
+ this.ensureConnected();
270
+ try {
271
+ const query = this.buildGmailQuery(options);
272
+ let labelIds = options?.labelIds;
273
+ if (options?.folder && !labelIds) {
274
+ labelIds = [options.folder];
275
+ }
276
+ let listResponse;
277
+ try {
278
+ listResponse = await this.gmail?.users.messages.list({
279
+ userId: this.getUserId(),
280
+ q: query,
281
+ labelIds,
282
+ maxResults: options?.maxResults || options?.limit || 100
283
+ });
284
+ } catch (error) {
285
+ if (error instanceof Error && error.message.includes("Invalid label")) {
286
+ return [];
287
+ }
288
+ throw error;
289
+ }
290
+ if (!listResponse) {
291
+ return [];
292
+ }
293
+ const messageIds = listResponse.data.messages || [];
294
+ if (messageIds.length === 0) {
295
+ return [];
296
+ }
297
+ const messages = [];
298
+ for (const msgId of messageIds) {
299
+ if (!msgId.id) continue;
300
+ try {
301
+ const message = await this.getMessage(msgId.id);
302
+ messages.push(message);
303
+ if (options?.limit && messages.length >= options.limit) {
304
+ break;
305
+ }
306
+ } catch (error) {
307
+ this.logger.error(`Failed to fetch message ${msgId.id}:`, {
308
+ error: error instanceof Error ? error.message : String(error)
309
+ });
310
+ }
311
+ }
312
+ return messages;
313
+ } catch (error) {
314
+ throw this.mapGmailError(error);
315
+ }
316
+ }
317
+ async getMessage(messageId) {
318
+ this.ensureConnected();
319
+ try {
320
+ const response = await this.gmail?.users.messages.get({
321
+ userId: this.getUserId(),
322
+ id: messageId,
323
+ format: "raw"
324
+ });
325
+ if (!response || !response.data.raw) {
326
+ throw new MessageNotFoundError(messageId, "gmail");
327
+ }
328
+ const rawMessage = Buffer.from(response.data.raw, "base64").toString(
329
+ "utf-8"
330
+ );
331
+ const parsed = await simpleParser(rawMessage);
332
+ const labels = response.data.labelIds || [];
333
+ const extractAddresses = (addressObj) => {
334
+ if (!addressObj) return [];
335
+ const addressArray = Array.isArray(addressObj) ? addressObj : [addressObj];
336
+ const addresses = addressArray.flatMap(
337
+ (obj) => Array.isArray(obj.value) ? obj.value : [obj.value]
338
+ );
339
+ return addresses.map((addr) => ({
340
+ address: addr.address || "",
341
+ name: addr.name
342
+ }));
343
+ };
344
+ const fromAddresses = extractAddresses(parsed.from);
345
+ const toAddresses = extractAddresses(parsed.to);
346
+ const ccAddresses = extractAddresses(parsed.cc);
347
+ const bccAddresses = extractAddresses(parsed.bcc);
348
+ const replyToAddresses = extractAddresses(parsed.replyTo);
349
+ return {
350
+ id: response.data.id || void 0,
351
+ messageId: parsed.messageId || response.data.id || void 0,
352
+ threadId: response.data.threadId || void 0,
353
+ inReplyTo: parsed.inReplyTo,
354
+ references: parsed.references ? Array.isArray(parsed.references) ? parsed.references : [parsed.references] : void 0,
355
+ from: fromAddresses[0] || { address: "", name: void 0 },
356
+ to: toAddresses,
357
+ cc: ccAddresses.length > 0 ? ccAddresses : void 0,
358
+ bcc: bccAddresses.length > 0 ? bccAddresses : void 0,
359
+ replyTo: replyToAddresses[0],
360
+ subject: parsed.subject || "",
361
+ date: parsed.date,
362
+ text: parsed.text,
363
+ html: parsed.html ? parsed.html.toString() : void 0,
364
+ attachments: parsed.attachments?.map((att) => ({
365
+ filename: att.filename,
366
+ contentType: att.contentType,
367
+ size: att.size,
368
+ content: att.content,
369
+ contentId: att.contentId,
370
+ contentDisposition: att.contentDisposition
371
+ })),
372
+ labels,
373
+ headers: parsed.headers,
374
+ size: Number(response.data.sizeEstimate),
375
+ raw: rawMessage
376
+ };
377
+ } catch (error) {
378
+ if (error instanceof MessageNotFoundError) {
379
+ throw error;
380
+ }
381
+ throw this.mapGmailError(error);
382
+ }
383
+ }
384
+ // ========================================================================
385
+ // Label operations (Gmail uses labels instead of folders)
386
+ // ========================================================================
387
+ async listFolders() {
388
+ this.ensureConnected();
389
+ try {
390
+ const response = await this.gmail?.users.labels.list({
391
+ userId: this.getUserId()
392
+ });
393
+ if (!response) {
394
+ return [];
395
+ }
396
+ const labels = response.data.labels || [];
397
+ return labels.map((label) => ({
398
+ name: label.name || "",
399
+ path: label.id || "",
400
+ delimiter: "/",
401
+ specialUse: this.mapGmailLabelToSpecialUse(label.type),
402
+ messageCount: label.messagesTotal || void 0,
403
+ unreadCount: label.messagesUnread || void 0
404
+ }));
405
+ } catch (error) {
406
+ throw this.mapGmailError(error);
407
+ }
408
+ }
409
+ async selectFolder(name) {
410
+ this.ensureConnected();
411
+ try {
412
+ const labels = await this.listFolders();
413
+ const label = labels.find((l) => l.name === name || l.path === name);
414
+ if (!label) {
415
+ throw new FolderNotFoundError(name, "gmail");
416
+ }
417
+ return {
418
+ name: label.name,
419
+ exists: label.messageCount || 0,
420
+ recent: 0,
421
+ unseen: label.unreadCount || 0,
422
+ uidValidity: 0,
423
+ uidNext: 0,
424
+ flags: [],
425
+ permanentFlags: []
426
+ };
427
+ } catch (error) {
428
+ if (error instanceof FolderNotFoundError) {
429
+ throw error;
430
+ }
431
+ throw this.mapGmailError(error);
432
+ }
433
+ }
434
+ async createFolder(name) {
435
+ this.ensureConnected();
436
+ try {
437
+ const labels = await this.listFolders();
438
+ if (labels.some((l) => l.name === name)) {
439
+ throw new FolderExistsError(name, "gmail");
440
+ }
441
+ await this.gmail?.users.labels.create({
442
+ userId: this.getUserId(),
443
+ requestBody: {
444
+ name,
445
+ labelListVisibility: "labelShow",
446
+ messageListVisibility: "show"
447
+ }
448
+ });
449
+ } catch (error) {
450
+ if (error instanceof FolderExistsError) {
451
+ throw error;
452
+ }
453
+ throw this.mapGmailError(error);
454
+ }
455
+ }
456
+ async deleteFolder(name) {
457
+ this.ensureConnected();
458
+ try {
459
+ const labels = await this.listFolders();
460
+ const label = labels.find((l) => l.name === name);
461
+ if (!label) {
462
+ throw new FolderNotFoundError(name, "gmail");
463
+ }
464
+ if (label.specialUse) {
465
+ throw new EmailError(
466
+ `Cannot delete system label: ${name}`,
467
+ "INVALID_OPERATION",
468
+ "gmail"
469
+ );
470
+ }
471
+ await this.gmail?.users.labels.delete({
472
+ userId: this.getUserId(),
473
+ id: label.path
474
+ });
475
+ } catch (error) {
476
+ if (error instanceof FolderNotFoundError || error instanceof EmailError) {
477
+ throw error;
478
+ }
479
+ throw this.mapGmailError(error);
480
+ }
481
+ }
482
+ // ========================================================================
483
+ // Message operations
484
+ // ========================================================================
485
+ async markRead(messageId) {
486
+ this.ensureConnected();
487
+ const ids = this.normalizeMessageIds(messageId);
488
+ try {
489
+ for (const id of ids) {
490
+ await this.gmail?.users.messages.modify({
491
+ userId: this.getUserId(),
492
+ id,
493
+ requestBody: {
494
+ removeLabelIds: ["UNREAD"]
495
+ }
496
+ });
497
+ }
498
+ } catch (error) {
499
+ throw this.mapGmailError(error);
500
+ }
501
+ }
502
+ async markUnread(messageId) {
503
+ this.ensureConnected();
504
+ const ids = this.normalizeMessageIds(messageId);
505
+ try {
506
+ for (const id of ids) {
507
+ await this.gmail?.users.messages.modify({
508
+ userId: this.getUserId(),
509
+ id,
510
+ requestBody: {
511
+ addLabelIds: ["UNREAD"]
512
+ }
513
+ });
514
+ }
515
+ } catch (error) {
516
+ throw this.mapGmailError(error);
517
+ }
518
+ }
519
+ async move(messageId, folder) {
520
+ this.ensureConnected();
521
+ const ids = this.normalizeMessageIds(messageId);
522
+ try {
523
+ const labels = await this.listFolders();
524
+ const label = labels.find((l) => l.name === folder || l.path === folder);
525
+ if (!label) {
526
+ throw new FolderNotFoundError(folder, "gmail");
527
+ }
528
+ for (const id of ids) {
529
+ await this.gmail?.users.messages.modify({
530
+ userId: this.getUserId(),
531
+ id,
532
+ requestBody: {
533
+ addLabelIds: [label.path]
534
+ }
535
+ });
536
+ }
537
+ } catch (error) {
538
+ if (error instanceof FolderNotFoundError) {
539
+ throw error;
540
+ }
541
+ throw this.mapGmailError(error);
542
+ }
543
+ }
544
+ async copy(messageId, folder) {
545
+ await this.move(messageId, folder);
546
+ }
547
+ async delete(messageId) {
548
+ this.ensureConnected();
549
+ const ids = this.normalizeMessageIds(messageId);
550
+ try {
551
+ for (const id of ids) {
552
+ await this.gmail?.users.messages.trash({
553
+ userId: this.getUserId(),
554
+ id
555
+ });
556
+ }
557
+ } catch (error) {
558
+ throw this.mapGmailError(error);
559
+ }
560
+ }
561
+ // ========================================================================
562
+ // Search
563
+ // ========================================================================
564
+ async search(criteria) {
565
+ this.ensureConnected();
566
+ try {
567
+ if (criteria.q) {
568
+ return await this.fetch({ q: criteria.q });
569
+ }
570
+ const queryParts = [];
571
+ if (criteria.from) queryParts.push(`from:${criteria.from}`);
572
+ if (criteria.to) queryParts.push(`to:${criteria.to}`);
573
+ if (criteria.subject) queryParts.push(`subject:${criteria.subject}`);
574
+ if (criteria.body) queryParts.push(criteria.body);
575
+ if (criteria.since) {
576
+ queryParts.push(`after:${this.formatGmailDate(criteria.since)}`);
577
+ }
578
+ if (criteria.before) {
579
+ queryParts.push(`before:${this.formatGmailDate(criteria.before)}`);
580
+ }
581
+ if (criteria.unread === true) queryParts.push("is:unread");
582
+ if (criteria.unread === false) queryParts.push("is:read");
583
+ if (criteria.flagged) queryParts.push("is:starred");
584
+ if (criteria.larger) queryParts.push(`larger:${criteria.larger}`);
585
+ if (criteria.smaller) queryParts.push(`smaller:${criteria.smaller}`);
586
+ const query = queryParts.join(" ");
587
+ return await this.fetch({ q: query });
588
+ } catch (error) {
589
+ throw this.mapGmailError(error);
590
+ }
591
+ }
592
+ // ========================================================================
593
+ // Adapter info
594
+ // ========================================================================
595
+ async getCapabilities() {
596
+ return {
597
+ send: true,
598
+ receive: true,
599
+ folders: true,
600
+ search: true,
601
+ markRead: true,
602
+ move: true,
603
+ delete: true,
604
+ threads: true,
605
+ oauth: true
606
+ };
607
+ }
608
+ getAdapter() {
609
+ return "gmail";
610
+ }
611
+ // ========================================================================
612
+ // Private helper methods
613
+ // ========================================================================
614
+ ensureConnected() {
615
+ if (!this.connected || !this.gmail) {
616
+ throw new ConnectionError(
617
+ "Not connected to Gmail. Call connect() first.",
618
+ "gmail"
619
+ );
620
+ }
621
+ }
622
+ getUserId() {
623
+ return this.options.userId || "me";
624
+ }
625
+ buildRFC2822Message(message, options) {
626
+ const lines = [];
627
+ const hasAttachments = message.attachments && message.attachments.length > 0;
628
+ lines.push(`From: ${this.formatEmailAddress(message.from)}`);
629
+ lines.push(
630
+ `To: ${message.to.map((a) => this.formatEmailAddress(a)).join(", ")}`
631
+ );
632
+ if (message.cc && message.cc.length > 0) {
633
+ lines.push(
634
+ `Cc: ${message.cc.map((a) => this.formatEmailAddress(a)).join(", ")}`
635
+ );
636
+ }
637
+ if (message.replyTo) {
638
+ lines.push(`Reply-To: ${this.formatEmailAddress(message.replyTo)}`);
639
+ }
640
+ lines.push(`Subject: ${message.subject}`);
641
+ lines.push(
642
+ `Date: ${options?.date?.toUTCString() || (/* @__PURE__ */ new Date()).toUTCString()}`
643
+ );
644
+ if (options?.messageId) {
645
+ lines.push(`Message-ID: <${options.messageId}>`);
646
+ }
647
+ lines.push("MIME-Version: 1.0");
648
+ if (hasAttachments) {
649
+ lines.push('Content-Type: multipart/mixed; boundary="mixed-boundary"');
650
+ lines.push("");
651
+ lines.push("--mixed-boundary");
652
+ if (message.html) {
653
+ lines.push(
654
+ 'Content-Type: multipart/alternative; boundary="alt-boundary"'
655
+ );
656
+ lines.push("");
657
+ lines.push("--alt-boundary");
658
+ lines.push('Content-Type: text/plain; charset="UTF-8"');
659
+ lines.push("Content-Transfer-Encoding: 7bit");
660
+ lines.push("");
661
+ lines.push(message.text || "");
662
+ lines.push("");
663
+ lines.push("--alt-boundary");
664
+ lines.push('Content-Type: text/html; charset="UTF-8"');
665
+ lines.push("Content-Transfer-Encoding: 7bit");
666
+ lines.push("");
667
+ lines.push(message.html);
668
+ lines.push("");
669
+ lines.push("--alt-boundary--");
670
+ } else {
671
+ lines.push('Content-Type: text/plain; charset="UTF-8"');
672
+ lines.push("Content-Transfer-Encoding: 7bit");
673
+ lines.push("");
674
+ lines.push(message.text || "");
675
+ }
676
+ if (message.attachments) {
677
+ for (const attachment of message.attachments) {
678
+ lines.push("");
679
+ lines.push("--mixed-boundary");
680
+ lines.push(
681
+ `Content-Type: ${attachment.contentType}; name="${attachment.filename}"`
682
+ );
683
+ lines.push("Content-Transfer-Encoding: base64");
684
+ lines.push(
685
+ `Content-Disposition: ${attachment.contentDisposition || "attachment"}; filename="${attachment.filename}"`
686
+ );
687
+ if (attachment.contentId) {
688
+ lines.push(`Content-ID: <${attachment.contentId}>`);
689
+ }
690
+ lines.push("");
691
+ const content = attachment.content || Buffer.from("");
692
+ const base64Content = content.toString("base64");
693
+ const base64Lines = base64Content.match(/.{1,76}/g) || [];
694
+ lines.push(...base64Lines);
695
+ }
696
+ }
697
+ lines.push("");
698
+ lines.push("--mixed-boundary--");
699
+ } else {
700
+ if (message.html) {
701
+ lines.push(
702
+ 'Content-Type: multipart/alternative; boundary="alt-boundary"'
703
+ );
704
+ lines.push("");
705
+ lines.push("--alt-boundary");
706
+ lines.push('Content-Type: text/plain; charset="UTF-8"');
707
+ lines.push("Content-Transfer-Encoding: 7bit");
708
+ lines.push("");
709
+ lines.push(message.text || "");
710
+ lines.push("");
711
+ lines.push("--alt-boundary");
712
+ lines.push('Content-Type: text/html; charset="UTF-8"');
713
+ lines.push("Content-Transfer-Encoding: 7bit");
714
+ lines.push("");
715
+ lines.push(message.html);
716
+ lines.push("");
717
+ lines.push("--alt-boundary--");
718
+ } else {
719
+ lines.push('Content-Type: text/plain; charset="UTF-8"');
720
+ lines.push("Content-Transfer-Encoding: 7bit");
721
+ lines.push("");
722
+ lines.push(message.text || "");
723
+ }
724
+ }
725
+ return lines.join("\r\n");
726
+ }
727
+ formatEmailAddress(addr) {
728
+ if (addr.name) {
729
+ return `${addr.name} <${addr.address}>`;
730
+ }
731
+ return addr.address;
732
+ }
733
+ buildGmailQuery(options) {
734
+ const queryParts = [];
735
+ if (options?.q) {
736
+ return options.q;
737
+ }
738
+ if (options?.since) {
739
+ queryParts.push(`after:${this.formatGmailDate(options.since)}`);
740
+ }
741
+ if (options?.before) {
742
+ queryParts.push(`before:${this.formatGmailDate(options.before)}`);
743
+ }
744
+ if (options?.unreadOnly) {
745
+ queryParts.push("is:unread");
746
+ }
747
+ return queryParts.join(" ");
748
+ }
749
+ formatGmailDate(date) {
750
+ const year = date.getFullYear();
751
+ const month = String(date.getMonth() + 1).padStart(2, "0");
752
+ const day = String(date.getDate()).padStart(2, "0");
753
+ return `${year}/${month}/${day}`;
754
+ }
755
+ mapGmailLabelToSpecialUse(type) {
756
+ switch (type) {
757
+ case "system":
758
+ return "\\Inbox";
759
+ default:
760
+ return void 0;
761
+ }
762
+ }
763
+ mapGmailError(error) {
764
+ if (error instanceof EmailError) {
765
+ return error;
766
+ }
767
+ if (error instanceof Error) {
768
+ const message = error.message.toLowerCase();
769
+ const errorObj = error;
770
+ if (message.includes("timeout") || errorObj.code === "ETIMEDOUT" || errorObj.code === "ESOCKETTIMEDOUT") {
771
+ return new TimeoutError(error.message, "gmail", error);
772
+ }
773
+ if (message.includes("network") || message.includes("connect") || errorObj.code === "ECONNREFUSED" || errorObj.code === "ENOTFOUND" || errorObj.code === "ECONNRESET") {
774
+ return new ConnectionError(error.message, "gmail", error);
775
+ }
776
+ if (message.includes("auth") || message.includes("invalid_grant") || message.includes("unauthorized") || errorObj.code === 401) {
777
+ return new AuthenticationError(error.message, "gmail", error);
778
+ }
779
+ if (message.includes("recipient") || message.includes("invalid email")) {
780
+ return new SendError(error.message, [], [], "gmail", error);
781
+ }
782
+ return new EmailError(error.message, "GMAIL_ERROR", "gmail", error);
783
+ }
784
+ return new EmailError(String(error), "GMAIL_ERROR", "gmail");
785
+ }
786
+ }
787
+ const gmail = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
788
+ __proto__: null,
789
+ GmailAdapter
790
+ }, Symbol.toStringTag, { value: "Module" }));
791
+ class IMAPAdapter extends BaseEmailClient {
792
+ client = null;
793
+ options;
794
+ currentFolder = null;
795
+ constructor(options) {
796
+ super({
797
+ type: "imap",
798
+ debug: options.debug
799
+ });
800
+ this.options = options;
801
+ }
802
+ /**
803
+ * Create ImapFlow client
804
+ */
805
+ createClient() {
806
+ const config = {
807
+ host: this.options.host,
808
+ port: this.options.port,
809
+ secure: this.options.secure ?? true,
810
+ auth: this.options.auth,
811
+ tls: this.options.tls,
812
+ logger: this.options.debug ? {
813
+ debug: (msg) => this.debug(msg),
814
+ info: (msg) => this.debug(msg),
815
+ warn: (msg) => this.debug(`WARN: ${msg}`),
816
+ error: (msg) => this.debug(`ERROR: ${msg}`)
817
+ } : false
818
+ };
819
+ return new ImapFlow(config);
820
+ }
821
+ getAdapter() {
822
+ return "imap";
823
+ }
824
+ /**
825
+ * Helper to convert search results to array
826
+ */
827
+ searchResultToArray(result) {
828
+ if (!result || !Array.isArray(result)) {
829
+ return [];
830
+ }
831
+ return result;
832
+ }
833
+ /**
834
+ * Connect to IMAP server
835
+ */
836
+ async connect() {
837
+ try {
838
+ this.debug("Connecting to IMAP server", { host: this.options.host });
839
+ this.client = this.createClient();
840
+ await this.client.connect();
841
+ this.connected = true;
842
+ this.debug("IMAP connection established");
843
+ } catch (error) {
844
+ this.connected = false;
845
+ throw this.mapIMAPError(error);
846
+ }
847
+ }
848
+ /**
849
+ * Disconnect from IMAP server
850
+ */
851
+ async disconnect() {
852
+ if (this.client) {
853
+ await this.client.logout();
854
+ this.client = null;
855
+ this.connected = false;
856
+ this.currentFolder = null;
857
+ this.debug("IMAP connection closed");
858
+ }
859
+ }
860
+ /**
861
+ * Fetch messages with filters
862
+ */
863
+ async fetch(options) {
864
+ this.ensureConnected();
865
+ const folder = options?.folder || "INBOX";
866
+ await this.selectFolder(folder);
867
+ try {
868
+ const searchQuery = this.buildSearchQuery(options);
869
+ this.debug("Fetching messages", { folder, query: searchQuery });
870
+ const searchResult = await this.client.search(searchQuery, {
871
+ uid: true
872
+ });
873
+ const uids = this.searchResultToArray(searchResult);
874
+ let targetUids = uids;
875
+ if (options?.offset || options?.limit) {
876
+ const start = options.offset || 0;
877
+ const end = options.limit ? start + options.limit : void 0;
878
+ targetUids = uids.slice(start, end);
879
+ }
880
+ if (targetUids.length === 0) {
881
+ return [];
882
+ }
883
+ const messages = [];
884
+ for await (const msg of this.client.fetch(targetUids, {
885
+ source: true,
886
+ flags: true,
887
+ uid: true,
888
+ envelope: true,
889
+ bodyStructure: true
890
+ })) {
891
+ try {
892
+ if (!msg.source) {
893
+ this.debug("Message has no source", { uid: msg.uid });
894
+ continue;
895
+ }
896
+ const parsed = await simpleParser(msg.source);
897
+ const email = this.parseMessage(parsed, msg);
898
+ messages.push(email);
899
+ if (options?.markSeen) {
900
+ await this.client.messageFlagsAdd(msg.uid, ["\\Seen"], {
901
+ uid: true
902
+ });
903
+ }
904
+ } catch (parseError) {
905
+ this.debug("Failed to parse message", { uid: msg.uid, parseError });
906
+ }
907
+ }
908
+ return messages;
909
+ } catch (error) {
910
+ throw this.mapIMAPError(error);
911
+ }
912
+ }
913
+ /**
914
+ * Get a specific message by ID
915
+ */
916
+ async getMessage(messageId) {
917
+ this.ensureConnected();
918
+ try {
919
+ const searchResult = await this.client.search(
920
+ { header: ["message-id", messageId] },
921
+ { uid: true }
922
+ );
923
+ const uids = this.searchResultToArray(searchResult);
924
+ if (uids.length === 0) {
925
+ throw new MessageNotFoundError(messageId, "imap");
926
+ }
927
+ const messages = [];
928
+ for await (const msg of this.client.fetch(uids[0], {
929
+ source: true,
930
+ flags: true,
931
+ uid: true
932
+ })) {
933
+ if (!msg.source) {
934
+ continue;
935
+ }
936
+ const parsed = await simpleParser(msg.source);
937
+ messages.push(this.parseMessage(parsed, msg));
938
+ }
939
+ if (messages.length === 0) {
940
+ throw new MessageNotFoundError(messageId, "imap");
941
+ }
942
+ return messages[0];
943
+ } catch (error) {
944
+ if (error instanceof EmailError) {
945
+ throw error;
946
+ }
947
+ throw this.mapIMAPError(error);
948
+ }
949
+ }
950
+ /**
951
+ * List all folders
952
+ */
953
+ async listFolders() {
954
+ this.ensureConnected();
955
+ try {
956
+ const list = await this.client?.list();
957
+ if (!list) {
958
+ return [];
959
+ }
960
+ return list.map((folder) => ({
961
+ name: folder.name,
962
+ path: folder.path,
963
+ delimiter: folder.delimiter,
964
+ specialUse: folder.specialUse,
965
+ subscribed: folder.subscribed
966
+ }));
967
+ } catch (error) {
968
+ throw this.mapIMAPError(error);
969
+ }
970
+ }
971
+ /**
972
+ * Select a folder
973
+ */
974
+ async selectFolder(name) {
975
+ this.ensureConnected();
976
+ try {
977
+ const mailbox = await this.client?.mailboxOpen(name);
978
+ this.currentFolder = name;
979
+ if (!mailbox) {
980
+ throw new FolderNotFoundError(name, "imap");
981
+ }
982
+ return {
983
+ name,
984
+ exists: Number(mailbox.exists),
985
+ recent: 0,
986
+ // ImapFlow doesn't provide this
987
+ unseen: 0,
988
+ // Would need separate STATUS command
989
+ uidValidity: Number(mailbox.uidValidity),
990
+ uidNext: Number(mailbox.uidNext),
991
+ flags: Array.from(mailbox.flags || []),
992
+ permanentFlags: Array.from(mailbox.permanentFlags || [])
993
+ };
994
+ } catch (error) {
995
+ if (error instanceof Error && error.message.toLowerCase().includes("does not exist")) {
996
+ throw new FolderNotFoundError(name, "imap");
997
+ }
998
+ throw this.mapIMAPError(error);
999
+ }
1000
+ }
1001
+ /**
1002
+ * Create a new folder
1003
+ */
1004
+ async createFolder(name) {
1005
+ this.ensureConnected();
1006
+ try {
1007
+ await this.client?.mailboxCreate(name);
1008
+ this.debug("Folder created", { name });
1009
+ } catch (error) {
1010
+ if (error instanceof Error && error.message.toLowerCase().includes("already exists")) {
1011
+ throw new FolderExistsError(name, "imap");
1012
+ }
1013
+ throw this.mapIMAPError(error);
1014
+ }
1015
+ }
1016
+ /**
1017
+ * Delete a folder
1018
+ */
1019
+ async deleteFolder(name) {
1020
+ this.ensureConnected();
1021
+ try {
1022
+ await this.client?.mailboxDelete(name);
1023
+ this.debug("Folder deleted", { name });
1024
+ } catch (error) {
1025
+ if (error instanceof Error && error.message.toLowerCase().includes("does not exist")) {
1026
+ throw new FolderNotFoundError(name, "imap");
1027
+ }
1028
+ throw this.mapIMAPError(error);
1029
+ }
1030
+ }
1031
+ /**
1032
+ * Mark message(s) as read
1033
+ */
1034
+ async markRead(messageId) {
1035
+ this.ensureConnected();
1036
+ const ids = Array.isArray(messageId) ? messageId : [messageId];
1037
+ try {
1038
+ for (const id of ids) {
1039
+ const searchResult = await this.client?.search(
1040
+ { header: ["message-id", id] },
1041
+ { uid: true }
1042
+ );
1043
+ const uids = this.searchResultToArray(searchResult);
1044
+ if (uids.length > 0) {
1045
+ await this.client?.messageFlagsAdd(uids[0], ["\\Seen"], {
1046
+ uid: true
1047
+ });
1048
+ }
1049
+ }
1050
+ } catch (error) {
1051
+ throw this.mapIMAPError(error);
1052
+ }
1053
+ }
1054
+ /**
1055
+ * Mark message(s) as unread
1056
+ */
1057
+ async markUnread(messageId) {
1058
+ this.ensureConnected();
1059
+ const ids = Array.isArray(messageId) ? messageId : [messageId];
1060
+ try {
1061
+ for (const id of ids) {
1062
+ const searchResult = await this.client?.search(
1063
+ { header: ["message-id", id] },
1064
+ { uid: true }
1065
+ );
1066
+ const uids = this.searchResultToArray(searchResult);
1067
+ if (uids.length > 0) {
1068
+ await this.client?.messageFlagsRemove(uids[0], ["\\Seen"], {
1069
+ uid: true
1070
+ });
1071
+ }
1072
+ }
1073
+ } catch (error) {
1074
+ throw this.mapIMAPError(error);
1075
+ }
1076
+ }
1077
+ /**
1078
+ * Move message(s) to another folder
1079
+ */
1080
+ async move(messageId, folder) {
1081
+ this.ensureConnected();
1082
+ const ids = Array.isArray(messageId) ? messageId : [messageId];
1083
+ try {
1084
+ for (const id of ids) {
1085
+ const searchResult = await this.client?.search(
1086
+ { header: ["message-id", id] },
1087
+ { uid: true }
1088
+ );
1089
+ const uids = this.searchResultToArray(searchResult);
1090
+ if (uids.length > 0) {
1091
+ await this.client?.messageMove(uids[0], folder, { uid: true });
1092
+ }
1093
+ }
1094
+ } catch (error) {
1095
+ throw this.mapIMAPError(error);
1096
+ }
1097
+ }
1098
+ /**
1099
+ * Copy message(s) to another folder
1100
+ */
1101
+ async copy(messageId, folder) {
1102
+ this.ensureConnected();
1103
+ const ids = Array.isArray(messageId) ? messageId : [messageId];
1104
+ try {
1105
+ for (const id of ids) {
1106
+ const searchResult = await this.client?.search(
1107
+ { header: ["message-id", id] },
1108
+ { uid: true }
1109
+ );
1110
+ const uids = this.searchResultToArray(searchResult);
1111
+ if (uids.length > 0) {
1112
+ await this.client?.messageCopy(uids[0], folder, { uid: true });
1113
+ }
1114
+ }
1115
+ } catch (error) {
1116
+ throw this.mapIMAPError(error);
1117
+ }
1118
+ }
1119
+ /**
1120
+ * Delete message(s)
1121
+ */
1122
+ async delete(messageId) {
1123
+ this.ensureConnected();
1124
+ const ids = Array.isArray(messageId) ? messageId : [messageId];
1125
+ try {
1126
+ for (const id of ids) {
1127
+ const searchResult = await this.client?.search(
1128
+ { header: ["message-id", id] },
1129
+ { uid: true }
1130
+ );
1131
+ const uids = this.searchResultToArray(searchResult);
1132
+ if (uids.length > 0) {
1133
+ await this.client?.messageFlagsAdd(uids[0], ["\\Deleted"], {
1134
+ uid: true
1135
+ });
1136
+ }
1137
+ }
1138
+ } catch (error) {
1139
+ throw this.mapIMAPError(error);
1140
+ }
1141
+ }
1142
+ /**
1143
+ * Search messages
1144
+ */
1145
+ async search(criteria) {
1146
+ this.ensureConnected();
1147
+ try {
1148
+ const query = this.buildSearchCriteria(criteria);
1149
+ const searchResult = await this.client.search(query, { uid: true });
1150
+ const uids = this.searchResultToArray(searchResult);
1151
+ if (uids.length === 0) {
1152
+ return [];
1153
+ }
1154
+ const messages = [];
1155
+ for await (const msg of this.client.fetch(uids, {
1156
+ source: true,
1157
+ flags: true,
1158
+ uid: true
1159
+ })) {
1160
+ if (!msg.source) {
1161
+ continue;
1162
+ }
1163
+ const parsed = await simpleParser(msg.source);
1164
+ messages.push(this.parseMessage(parsed, msg));
1165
+ }
1166
+ return messages;
1167
+ } catch (error) {
1168
+ throw this.mapIMAPError(error);
1169
+ }
1170
+ }
1171
+ /**
1172
+ * Get adapter capabilities
1173
+ */
1174
+ async getCapabilities() {
1175
+ return {
1176
+ send: false,
1177
+ // IMAP is receive-only
1178
+ receive: true,
1179
+ folders: true,
1180
+ search: true,
1181
+ markRead: true,
1182
+ move: true,
1183
+ delete: true,
1184
+ threads: false,
1185
+ // Thread support not implemented yet
1186
+ oauth: this.isOAuth2()
1187
+ };
1188
+ }
1189
+ /**
1190
+ * Check if using OAuth2 authentication
1191
+ */
1192
+ isOAuth2() {
1193
+ return this.options.auth !== void 0 && "type" in this.options.auth && this.options.auth.type === "OAuth2";
1194
+ }
1195
+ // ========================================================================
1196
+ // Unsupported operations (IMAP is receive-only)
1197
+ // ========================================================================
1198
+ async send(_message, _options) {
1199
+ throw new EmailError(
1200
+ "IMAP does not support sending messages. Use SMTP adapter.",
1201
+ "UNSUPPORTED_OPERATION",
1202
+ "imap"
1203
+ );
1204
+ }
1205
+ // ========================================================================
1206
+ // Helper methods
1207
+ // ========================================================================
1208
+ /**
1209
+ * Ensure client is connected
1210
+ */
1211
+ ensureConnected() {
1212
+ if (!this.client || !this.connected) {
1213
+ throw new ConnectionError(
1214
+ "Not connected to IMAP server. Call connect() first.",
1215
+ "imap"
1216
+ );
1217
+ }
1218
+ }
1219
+ /**
1220
+ * Build IMAP search query from FetchOptions
1221
+ */
1222
+ buildSearchQuery(options) {
1223
+ const query = {};
1224
+ if (options?.unreadOnly) {
1225
+ query.unseen = true;
1226
+ }
1227
+ if (options?.since) {
1228
+ query.since = options.since;
1229
+ }
1230
+ if (options?.before) {
1231
+ query.before = options.before;
1232
+ }
1233
+ if (Object.keys(query).length === 0) {
1234
+ query.all = true;
1235
+ }
1236
+ return query;
1237
+ }
1238
+ /**
1239
+ * Build IMAP search criteria from SearchCriteria
1240
+ */
1241
+ buildSearchCriteria(criteria) {
1242
+ const query = {};
1243
+ if (criteria.from) {
1244
+ query.from = criteria.from;
1245
+ }
1246
+ if (criteria.to) {
1247
+ query.to = criteria.to;
1248
+ }
1249
+ if (criteria.subject) {
1250
+ query.subject = criteria.subject;
1251
+ }
1252
+ if (criteria.body) {
1253
+ query.body = criteria.body;
1254
+ }
1255
+ if (criteria.since) {
1256
+ query.since = criteria.since;
1257
+ }
1258
+ if (criteria.before) {
1259
+ query.before = criteria.before;
1260
+ }
1261
+ if (criteria.unread) {
1262
+ query.unseen = true;
1263
+ }
1264
+ if (criteria.flagged) {
1265
+ query.flagged = true;
1266
+ }
1267
+ if (criteria.answered) {
1268
+ query.answered = true;
1269
+ }
1270
+ if (criteria.draft) {
1271
+ query.draft = true;
1272
+ }
1273
+ if (criteria.deleted) {
1274
+ query.deleted = true;
1275
+ }
1276
+ if (criteria.larger) {
1277
+ query.larger = criteria.larger;
1278
+ }
1279
+ if (criteria.smaller) {
1280
+ query.smaller = criteria.smaller;
1281
+ }
1282
+ if (criteria.header) {
1283
+ query.header = Object.entries(criteria.header).map(([key, value]) => [
1284
+ key,
1285
+ value
1286
+ ]);
1287
+ }
1288
+ if (Object.keys(query).length === 0) {
1289
+ query.all = true;
1290
+ }
1291
+ return query;
1292
+ }
1293
+ /**
1294
+ * Parse mailparser output to EmailMessage
1295
+ */
1296
+ parseMessage(parsed, msg) {
1297
+ const extractAddresses = (addressObj) => {
1298
+ if (!addressObj) return [];
1299
+ const addressArray = Array.isArray(addressObj) ? addressObj : [addressObj];
1300
+ const addresses = addressArray.flatMap(
1301
+ (obj) => Array.isArray(obj.value) ? obj.value : [obj.value]
1302
+ );
1303
+ return addresses.map((addr) => ({
1304
+ address: addr.address || "",
1305
+ name: addr.name
1306
+ }));
1307
+ };
1308
+ const fromAddresses = extractAddresses(parsed.from);
1309
+ const toAddresses = extractAddresses(parsed.to);
1310
+ const ccAddresses = extractAddresses(parsed.cc);
1311
+ const bccAddresses = extractAddresses(parsed.bcc);
1312
+ const replyToAddresses = extractAddresses(parsed.replyTo);
1313
+ return {
1314
+ id: String(msg.uid),
1315
+ messageId: parsed.messageId || void 0,
1316
+ from: fromAddresses[0] || { address: "", name: void 0 },
1317
+ to: toAddresses,
1318
+ cc: ccAddresses.length > 0 ? ccAddresses : void 0,
1319
+ bcc: bccAddresses.length > 0 ? bccAddresses : void 0,
1320
+ replyTo: replyToAddresses[0],
1321
+ subject: parsed.subject || "",
1322
+ date: parsed.date,
1323
+ text: parsed.text,
1324
+ html: parsed.html ? String(parsed.html) : void 0,
1325
+ attachments: parsed.attachments?.map((att) => ({
1326
+ filename: att.filename,
1327
+ contentType: att.contentType,
1328
+ size: att.size,
1329
+ content: att.content,
1330
+ contentId: att.contentId,
1331
+ contentDisposition: att.contentDisposition
1332
+ })),
1333
+ flags: msg.flags ? Array.from(msg.flags) : void 0,
1334
+ inReplyTo: parsed.inReplyTo,
1335
+ references: Array.isArray(parsed.references) ? parsed.references : parsed.references ? [parsed.references] : void 0,
1336
+ headers: parsed.headers ? Object.fromEntries(
1337
+ Array.from(parsed.headers.entries()).map(([key, value]) => [
1338
+ key,
1339
+ Array.isArray(value) ? value : String(value)
1340
+ ])
1341
+ ) : void 0
1342
+ };
1343
+ }
1344
+ /**
1345
+ * Map ImapFlow errors to standard error types
1346
+ */
1347
+ mapIMAPError(error) {
1348
+ if (error instanceof EmailError) {
1349
+ return error;
1350
+ }
1351
+ if (error instanceof Error) {
1352
+ const message = error.message.toLowerCase();
1353
+ if (message.includes("authentication") || message.includes("auth") || message.includes("login") || message.includes("invalid credentials")) {
1354
+ return new AuthenticationError(error.message, "imap", error);
1355
+ }
1356
+ if (message.includes("timeout") || message.includes("etimedout")) {
1357
+ return new TimeoutError(error.message, "imap", error);
1358
+ }
1359
+ if (message.includes("econnrefused") || message.includes("enotfound") || message.includes("connection") || message.includes("ehostunreach") || message.includes("network")) {
1360
+ return new ConnectionError(error.message, "imap", error);
1361
+ }
1362
+ return new EmailError(error.message, "IMAP_ERROR", "imap", error);
1363
+ }
1364
+ return new EmailError(String(error), "UNKNOWN_ERROR", "imap");
1365
+ }
1366
+ }
1367
+ const imap = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1368
+ __proto__: null,
1369
+ IMAPAdapter
1370
+ }, Symbol.toStringTag, { value: "Module" }));
1371
+ class POP3Adapter extends BaseEmailClient {
1372
+ client = null;
1373
+ options;
1374
+ messageCache = /* @__PURE__ */ new Map();
1375
+ // msgNum -> UID
1376
+ constructor(options) {
1377
+ super({
1378
+ type: "pop3",
1379
+ debug: options.debug
1380
+ });
1381
+ this.options = options;
1382
+ }
1383
+ // ========================================================================
1384
+ // Connection management
1385
+ // ========================================================================
1386
+ async connect() {
1387
+ try {
1388
+ this.client = new Pop3Command({
1389
+ user: this.options.auth.user,
1390
+ password: this.options.auth.pass,
1391
+ host: this.options.host,
1392
+ port: this.options.port,
1393
+ tls: this.options.secure ?? this.options.port === 995,
1394
+ timeout: this.options.connectionTimeout,
1395
+ tlsOptions: this.options.tls
1396
+ });
1397
+ await this.client.connect();
1398
+ this.connected = true;
1399
+ this.debug("Connected to POP3 server");
1400
+ } catch (error) {
1401
+ throw this.mapPOP3Error(error);
1402
+ }
1403
+ }
1404
+ async disconnect() {
1405
+ if (!this.client) {
1406
+ return;
1407
+ }
1408
+ try {
1409
+ await this.client.QUIT();
1410
+ this.client = null;
1411
+ this.connected = false;
1412
+ this.messageCache.clear();
1413
+ this.debug("Disconnected from POP3 server");
1414
+ } catch (error) {
1415
+ this.client = null;
1416
+ this.connected = false;
1417
+ this.messageCache.clear();
1418
+ throw this.mapPOP3Error(error);
1419
+ }
1420
+ }
1421
+ // ========================================================================
1422
+ // Message operations
1423
+ // ========================================================================
1424
+ async fetch(options) {
1425
+ this.ensureConnected();
1426
+ try {
1427
+ const uidlResponse = await this.client?.UIDL();
1428
+ if (!uidlResponse || !uidlResponse[0]) {
1429
+ return [];
1430
+ }
1431
+ const uidList = this.parseUidlResponse(
1432
+ Array.isArray(uidlResponse[0]) ? uidlResponse[0].join("\n") : uidlResponse[0]
1433
+ );
1434
+ if (uidList.length === 0) {
1435
+ return [];
1436
+ }
1437
+ this.messageCache.clear();
1438
+ for (const { msgNum, uid } of uidList) {
1439
+ this.messageCache.set(msgNum, uid);
1440
+ }
1441
+ let messageNums = uidList.map((item) => item.msgNum);
1442
+ if (options?.offset) {
1443
+ messageNums = messageNums.slice(options.offset);
1444
+ }
1445
+ if (options?.limit) {
1446
+ messageNums = messageNums.slice(0, options.limit);
1447
+ }
1448
+ const messages = [];
1449
+ for (const msgNum of messageNums) {
1450
+ try {
1451
+ const message = await this.fetchMessage(msgNum);
1452
+ if (this.shouldIncludeMessage(message, options)) {
1453
+ messages.push(message);
1454
+ if (!this.options.leaveOnServer) {
1455
+ await this.deleteMessageByNum(msgNum);
1456
+ }
1457
+ }
1458
+ } catch (error) {
1459
+ this.logger.error(`Failed to fetch message ${msgNum}:`, {
1460
+ error: error instanceof Error ? error.message : String(error)
1461
+ });
1462
+ }
1463
+ }
1464
+ return messages;
1465
+ } catch (error) {
1466
+ throw this.mapPOP3Error(error);
1467
+ }
1468
+ }
1469
+ async getMessage(messageId) {
1470
+ this.ensureConnected();
1471
+ try {
1472
+ let msgNum = null;
1473
+ for (const [num, uid] of this.messageCache.entries()) {
1474
+ if (uid === messageId) {
1475
+ msgNum = num;
1476
+ break;
1477
+ }
1478
+ }
1479
+ if (!msgNum) {
1480
+ const uidlResponse = await this.client?.UIDL();
1481
+ if (!uidlResponse || !uidlResponse[0]) {
1482
+ throw new MessageNotFoundError(messageId, "pop3");
1483
+ }
1484
+ const uidList = this.parseUidlResponse(
1485
+ Array.isArray(uidlResponse[0]) ? uidlResponse[0].join("\n") : uidlResponse[0]
1486
+ );
1487
+ this.messageCache.clear();
1488
+ for (const { msgNum: num, uid } of uidList) {
1489
+ this.messageCache.set(num, uid);
1490
+ if (uid === messageId) {
1491
+ msgNum = num;
1492
+ }
1493
+ }
1494
+ }
1495
+ if (!msgNum) {
1496
+ throw new MessageNotFoundError(messageId, "pop3");
1497
+ }
1498
+ return await this.fetchMessage(msgNum);
1499
+ } catch (error) {
1500
+ if (error instanceof MessageNotFoundError) {
1501
+ throw error;
1502
+ }
1503
+ throw this.mapPOP3Error(error);
1504
+ }
1505
+ }
1506
+ async delete(messageId) {
1507
+ this.ensureConnected();
1508
+ const ids = this.normalizeMessageIds(messageId);
1509
+ for (const id of ids) {
1510
+ try {
1511
+ let msgNum = null;
1512
+ for (const [num, uid] of this.messageCache.entries()) {
1513
+ if (uid === id) {
1514
+ msgNum = num;
1515
+ break;
1516
+ }
1517
+ }
1518
+ if (!msgNum) {
1519
+ const uidlResponse = await this.client?.UIDL();
1520
+ if (!uidlResponse || !uidlResponse[0]) {
1521
+ throw new MessageNotFoundError(id, "pop3");
1522
+ }
1523
+ const uidList = this.parseUidlResponse(
1524
+ Array.isArray(uidlResponse[0]) ? uidlResponse[0].join("\n") : uidlResponse[0]
1525
+ );
1526
+ for (const { msgNum: num, uid } of uidList) {
1527
+ if (uid === id) {
1528
+ msgNum = num;
1529
+ break;
1530
+ }
1531
+ }
1532
+ }
1533
+ if (!msgNum) {
1534
+ throw new MessageNotFoundError(id, "pop3");
1535
+ }
1536
+ await this.deleteMessageByNum(msgNum);
1537
+ } catch (error) {
1538
+ throw this.mapPOP3Error(error);
1539
+ }
1540
+ }
1541
+ }
1542
+ // ========================================================================
1543
+ // Unsupported operations (POP3 limitations)
1544
+ // ========================================================================
1545
+ async send(_message, _options) {
1546
+ throw new EmailError(
1547
+ "POP3 does not support sending email. Use SMTP instead.",
1548
+ "UNSUPPORTED_OPERATION",
1549
+ "pop3"
1550
+ );
1551
+ }
1552
+ async listFolders() {
1553
+ throw new EmailError(
1554
+ "POP3 does not support folders",
1555
+ "UNSUPPORTED_OPERATION",
1556
+ "pop3"
1557
+ );
1558
+ }
1559
+ async selectFolder(_name) {
1560
+ throw new EmailError(
1561
+ "POP3 does not support folders",
1562
+ "UNSUPPORTED_OPERATION",
1563
+ "pop3"
1564
+ );
1565
+ }
1566
+ async createFolder(_name) {
1567
+ throw new EmailError(
1568
+ "POP3 does not support folders",
1569
+ "UNSUPPORTED_OPERATION",
1570
+ "pop3"
1571
+ );
1572
+ }
1573
+ async deleteFolder(_name) {
1574
+ throw new EmailError(
1575
+ "POP3 does not support folders",
1576
+ "UNSUPPORTED_OPERATION",
1577
+ "pop3"
1578
+ );
1579
+ }
1580
+ async markRead(_messageId) {
1581
+ throw new EmailError(
1582
+ "POP3 does not support marking messages as read",
1583
+ "UNSUPPORTED_OPERATION",
1584
+ "pop3"
1585
+ );
1586
+ }
1587
+ async markUnread(_messageId) {
1588
+ throw new EmailError(
1589
+ "POP3 does not support marking messages as unread",
1590
+ "UNSUPPORTED_OPERATION",
1591
+ "pop3"
1592
+ );
1593
+ }
1594
+ async move(_messageId, _folder) {
1595
+ throw new EmailError(
1596
+ "POP3 does not support moving messages",
1597
+ "UNSUPPORTED_OPERATION",
1598
+ "pop3"
1599
+ );
1600
+ }
1601
+ async copy(_messageId, _folder) {
1602
+ throw new EmailError(
1603
+ "POP3 does not support copying messages",
1604
+ "UNSUPPORTED_OPERATION",
1605
+ "pop3"
1606
+ );
1607
+ }
1608
+ async search(_criteria) {
1609
+ throw new EmailError(
1610
+ "POP3 does not support server-side search. Use fetch() with filters instead.",
1611
+ "UNSUPPORTED_OPERATION",
1612
+ "pop3"
1613
+ );
1614
+ }
1615
+ // ========================================================================
1616
+ // Adapter info
1617
+ // ========================================================================
1618
+ async getCapabilities() {
1619
+ return {
1620
+ send: false,
1621
+ receive: true,
1622
+ folders: false,
1623
+ search: false,
1624
+ markRead: false,
1625
+ move: false,
1626
+ delete: true,
1627
+ threads: false,
1628
+ oauth: false
1629
+ };
1630
+ }
1631
+ getAdapter() {
1632
+ return "pop3";
1633
+ }
1634
+ // ========================================================================
1635
+ // Private helper methods
1636
+ // ========================================================================
1637
+ ensureConnected() {
1638
+ if (!this.connected || !this.client) {
1639
+ throw new ConnectionError(
1640
+ "Not connected to POP3 server. Call connect() first.",
1641
+ "pop3"
1642
+ );
1643
+ }
1644
+ }
1645
+ async fetchMessage(msgNum) {
1646
+ try {
1647
+ const response = await this.client?.RETR(Number(msgNum));
1648
+ if (!response || !response[0]) {
1649
+ throw new Error(`Failed to retrieve message ${msgNum}`);
1650
+ }
1651
+ const messageData = response[0];
1652
+ const parsed = await simpleParser(messageData);
1653
+ const uid = this.messageCache.get(msgNum);
1654
+ const extractAddresses = (addressObj) => {
1655
+ if (!addressObj) return [];
1656
+ const addressArray = Array.isArray(addressObj) ? addressObj : [addressObj];
1657
+ const addresses = addressArray.flatMap(
1658
+ (obj) => Array.isArray(obj.value) ? obj.value : [obj.value]
1659
+ );
1660
+ return addresses.map((addr) => ({
1661
+ address: addr.address || "",
1662
+ name: addr.name
1663
+ }));
1664
+ };
1665
+ const fromAddresses = extractAddresses(parsed.from);
1666
+ const toAddresses = extractAddresses(parsed.to);
1667
+ const ccAddresses = extractAddresses(parsed.cc);
1668
+ const bccAddresses = extractAddresses(parsed.bcc);
1669
+ const replyToAddresses = extractAddresses(parsed.replyTo);
1670
+ return {
1671
+ id: uid || msgNum,
1672
+ messageId: parsed.messageId || uid || msgNum,
1673
+ from: fromAddresses[0] || { address: "", name: void 0 },
1674
+ to: toAddresses,
1675
+ cc: ccAddresses.length > 0 ? ccAddresses : void 0,
1676
+ bcc: bccAddresses.length > 0 ? bccAddresses : void 0,
1677
+ replyTo: replyToAddresses[0],
1678
+ subject: parsed.subject || "",
1679
+ date: parsed.date,
1680
+ text: parsed.text,
1681
+ html: parsed.html ? parsed.html.toString() : void 0,
1682
+ attachments: parsed.attachments?.map((att) => ({
1683
+ filename: att.filename,
1684
+ contentType: att.contentType,
1685
+ size: att.size,
1686
+ content: att.content,
1687
+ contentId: att.contentId,
1688
+ contentDisposition: att.contentDisposition
1689
+ })),
1690
+ headers: parsed.headers,
1691
+ size: messageData.length,
1692
+ raw: messageData
1693
+ };
1694
+ } catch (error) {
1695
+ throw this.mapPOP3Error(error);
1696
+ }
1697
+ }
1698
+ async deleteMessageByNum(msgNum) {
1699
+ try {
1700
+ await this.client?.command(`DELE ${msgNum}`);
1701
+ this.messageCache.delete(msgNum);
1702
+ this.debug(`Deleted message ${msgNum}`);
1703
+ } catch (error) {
1704
+ throw this.mapPOP3Error(error);
1705
+ }
1706
+ }
1707
+ parseUidlResponse(response) {
1708
+ const lines = response.split("\n");
1709
+ const result = [];
1710
+ for (const line of lines) {
1711
+ const trimmed = line.trim();
1712
+ if (!trimmed || trimmed === "." || trimmed.startsWith("+OK")) {
1713
+ continue;
1714
+ }
1715
+ const parts = trimmed.split(/\s+/);
1716
+ if (parts.length >= 2) {
1717
+ result.push({
1718
+ msgNum: parts[0],
1719
+ uid: parts[1]
1720
+ });
1721
+ }
1722
+ }
1723
+ return result;
1724
+ }
1725
+ shouldIncludeMessage(message, options) {
1726
+ if (options?.since && message.date) {
1727
+ if (message.date < options.since) {
1728
+ return false;
1729
+ }
1730
+ }
1731
+ if (options?.before && message.date) {
1732
+ if (message.date >= options.before) {
1733
+ return false;
1734
+ }
1735
+ }
1736
+ return true;
1737
+ }
1738
+ mapPOP3Error(error) {
1739
+ if (error instanceof EmailError) {
1740
+ return error;
1741
+ }
1742
+ if (error instanceof Error) {
1743
+ const message = error.message.toLowerCase();
1744
+ const errorObj = error;
1745
+ if (message.includes("timeout") || errorObj.eventName === "timeout" || errorObj.code === "ETIMEDOUT") {
1746
+ return new TimeoutError(error.message, "pop3", error);
1747
+ }
1748
+ if (message.includes("connect") || message.includes("econnrefused") || message.includes("enotfound") || message.includes("ehostunreach") || errorObj.eventName === "error" || errorObj.eventName === "close" || errorObj.code === "ECONNREFUSED" || errorObj.code === "ENOTFOUND") {
1749
+ return new ConnectionError(error.message, "pop3", error);
1750
+ }
1751
+ if (message.includes("auth") || message.includes("login") || message.includes("password") || message.includes("user") || message.includes("-err")) {
1752
+ return new AuthenticationError(error.message, "pop3", error);
1753
+ }
1754
+ return new EmailError(error.message, "POP3_ERROR", "pop3", error);
1755
+ }
1756
+ return new EmailError(String(error), "POP3_ERROR", "pop3");
1757
+ }
1758
+ }
1759
+ const pop3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1760
+ __proto__: null,
1761
+ POP3Adapter
1762
+ }, Symbol.toStringTag, { value: "Module" }));
1763
+ class SMTPAdapter extends BaseEmailClient {
1764
+ transporter;
1765
+ options;
1766
+ constructor(options) {
1767
+ super({
1768
+ type: "smtp",
1769
+ debug: options.debug
1770
+ });
1771
+ this.options = options;
1772
+ this.transporter = this.createTransporter();
1773
+ }
1774
+ /**
1775
+ * Create Nodemailer transporter with configuration
1776
+ */
1777
+ createTransporter() {
1778
+ const config = {
1779
+ host: this.options.host,
1780
+ port: this.options.port,
1781
+ secure: this.options.secure ?? false,
1782
+ auth: this.options.auth,
1783
+ tls: this.options.tls,
1784
+ connectionTimeout: this.options.connectionTimeout,
1785
+ greetingTimeout: this.options.greetingTimeout,
1786
+ socketTimeout: this.options.socketTimeout,
1787
+ pool: this.options.pool,
1788
+ maxConnections: this.options.maxConnections,
1789
+ maxMessages: this.options.maxMessages,
1790
+ debug: this.options.debug
1791
+ };
1792
+ return nodemailer.createTransport(config);
1793
+ }
1794
+ getAdapter() {
1795
+ return "smtp";
1796
+ }
1797
+ /**
1798
+ * Send an email message
1799
+ */
1800
+ async send(message, options) {
1801
+ this.validateMessage(message);
1802
+ try {
1803
+ const mailOptions = {
1804
+ from: this.formatEmailAddress(message.from),
1805
+ to: message.to.map((addr) => this.formatEmailAddress(addr)),
1806
+ cc: message.cc?.map((addr) => this.formatEmailAddress(addr)),
1807
+ bcc: message.bcc?.map((addr) => this.formatEmailAddress(addr)),
1808
+ subject: message.subject,
1809
+ text: message.text,
1810
+ html: message.html,
1811
+ replyTo: message.replyTo ? this.formatEmailAddress(message.replyTo) : void 0,
1812
+ inReplyTo: message.inReplyTo,
1813
+ references: message.references,
1814
+ attachments: message.attachments?.map((att) => ({
1815
+ filename: att.filename,
1816
+ content: att.content,
1817
+ path: att.path,
1818
+ contentType: att.contentType,
1819
+ cid: att.contentId,
1820
+ contentDisposition: att.contentDisposition
1821
+ })),
1822
+ headers: message.headers,
1823
+ messageId: message.messageId || options?.messageId,
1824
+ date: message.date || options?.date,
1825
+ encoding: options?.encoding,
1826
+ textEncoding: options?.textEncoding,
1827
+ envelope: options?.envelope,
1828
+ priority: options?.priority,
1829
+ dsn: options?.dsn
1830
+ };
1831
+ this.debug("Sending email", {
1832
+ to: mailOptions.to,
1833
+ subject: message.subject
1834
+ });
1835
+ const info = await this.transporter.sendMail(mailOptions);
1836
+ const sendInfo = info;
1837
+ return {
1838
+ messageId: sendInfo.messageId,
1839
+ accepted: sendInfo.accepted,
1840
+ rejected: sendInfo.rejected,
1841
+ response: sendInfo.response
1842
+ };
1843
+ } catch (error) {
1844
+ throw this.mapSMTPError(error);
1845
+ }
1846
+ }
1847
+ /**
1848
+ * Format email address for Nodemailer
1849
+ */
1850
+ formatEmailAddress(addr) {
1851
+ if (addr.name) {
1852
+ const name = addr.name.replace(/"/g, '\\"');
1853
+ return `"${name}" <${addr.address}>`;
1854
+ }
1855
+ return addr.address;
1856
+ }
1857
+ /**
1858
+ * Map Nodemailer/SMTP errors to standard error types
1859
+ */
1860
+ mapSMTPError(error) {
1861
+ if (error instanceof EmailError) {
1862
+ return error;
1863
+ }
1864
+ if (error instanceof Error) {
1865
+ const message = error.message.toLowerCase();
1866
+ if (message.includes("authentication") || message.includes("invalid login") || message.includes("535")) {
1867
+ return new AuthenticationError(error.message, "smtp", error);
1868
+ }
1869
+ if (message.includes("timeout") || message.includes("etimedout")) {
1870
+ return new TimeoutError(error.message, "smtp", error);
1871
+ }
1872
+ if (message.includes("econnrefused") || message.includes("enotfound") || message.includes("connection") || message.includes("ehostunreach")) {
1873
+ return new ConnectionError(error.message, "smtp", error);
1874
+ }
1875
+ if ("accepted" in error && "rejected" in error) {
1876
+ const errorWithRecipients = error;
1877
+ return new SendError(
1878
+ error.message,
1879
+ errorWithRecipients.accepted || [],
1880
+ errorWithRecipients.rejected || [],
1881
+ "smtp",
1882
+ error
1883
+ );
1884
+ }
1885
+ return new EmailError(error.message, "SMTP_ERROR", "smtp", error);
1886
+ }
1887
+ return new EmailError(String(error), "UNKNOWN_ERROR", "smtp");
1888
+ }
1889
+ /**
1890
+ * Connect to SMTP server (verify connection)
1891
+ */
1892
+ async connect() {
1893
+ try {
1894
+ this.debug("Verifying SMTP connection", { host: this.options.host });
1895
+ await this.transporter.verify();
1896
+ this.connected = true;
1897
+ this.debug("SMTP connection verified");
1898
+ } catch (error) {
1899
+ this.connected = false;
1900
+ throw this.mapSMTPError(error);
1901
+ }
1902
+ }
1903
+ /**
1904
+ * Disconnect from SMTP server
1905
+ */
1906
+ async disconnect() {
1907
+ this.transporter.close();
1908
+ this.connected = false;
1909
+ this.debug("SMTP connection closed");
1910
+ }
1911
+ /**
1912
+ * Get adapter capabilities
1913
+ */
1914
+ async getCapabilities() {
1915
+ return {
1916
+ send: true,
1917
+ receive: false,
1918
+ // SMTP is send-only
1919
+ folders: false,
1920
+ search: false,
1921
+ markRead: false,
1922
+ move: false,
1923
+ delete: false,
1924
+ threads: false,
1925
+ oauth: this.isOAuth2()
1926
+ };
1927
+ }
1928
+ /**
1929
+ * Check if using OAuth2 authentication
1930
+ */
1931
+ isOAuth2() {
1932
+ return this.options.auth !== void 0 && "type" in this.options.auth && this.options.auth.type === "OAuth2";
1933
+ }
1934
+ // ========================================================================
1935
+ // Unsupported operations (SMTP is send-only)
1936
+ // ========================================================================
1937
+ async fetch(_options) {
1938
+ throw new EmailError(
1939
+ "SMTP does not support receiving messages. Use IMAP or POP3 adapter.",
1940
+ "UNSUPPORTED_OPERATION",
1941
+ "smtp"
1942
+ );
1943
+ }
1944
+ async getMessage(_messageId) {
1945
+ throw new EmailError(
1946
+ "SMTP does not support receiving messages. Use IMAP or POP3 adapter.",
1947
+ "UNSUPPORTED_OPERATION",
1948
+ "smtp"
1949
+ );
1950
+ }
1951
+ async listFolders() {
1952
+ throw new EmailError(
1953
+ "SMTP does not support folder operations.",
1954
+ "UNSUPPORTED_OPERATION",
1955
+ "smtp"
1956
+ );
1957
+ }
1958
+ async selectFolder(_name) {
1959
+ throw new EmailError(
1960
+ "SMTP does not support folder operations.",
1961
+ "UNSUPPORTED_OPERATION",
1962
+ "smtp"
1963
+ );
1964
+ }
1965
+ async createFolder(_name) {
1966
+ throw new EmailError(
1967
+ "SMTP does not support folder operations.",
1968
+ "UNSUPPORTED_OPERATION",
1969
+ "smtp"
1970
+ );
1971
+ }
1972
+ async deleteFolder(_name) {
1973
+ throw new EmailError(
1974
+ "SMTP does not support folder operations.",
1975
+ "UNSUPPORTED_OPERATION",
1976
+ "smtp"
1977
+ );
1978
+ }
1979
+ async markRead(_messageId) {
1980
+ throw new EmailError(
1981
+ "SMTP does not support message operations.",
1982
+ "UNSUPPORTED_OPERATION",
1983
+ "smtp"
1984
+ );
1985
+ }
1986
+ async markUnread(_messageId) {
1987
+ throw new EmailError(
1988
+ "SMTP does not support message operations.",
1989
+ "UNSUPPORTED_OPERATION",
1990
+ "smtp"
1991
+ );
1992
+ }
1993
+ async move(_messageId, _folder) {
1994
+ throw new EmailError(
1995
+ "SMTP does not support message operations.",
1996
+ "UNSUPPORTED_OPERATION",
1997
+ "smtp"
1998
+ );
1999
+ }
2000
+ async copy(_messageId, _folder) {
2001
+ throw new EmailError(
2002
+ "SMTP does not support message operations.",
2003
+ "UNSUPPORTED_OPERATION",
2004
+ "smtp"
2005
+ );
2006
+ }
2007
+ async delete(_messageId) {
2008
+ throw new EmailError(
2009
+ "SMTP does not support message operations.",
2010
+ "UNSUPPORTED_OPERATION",
2011
+ "smtp"
2012
+ );
2013
+ }
2014
+ async search(_criteria) {
2015
+ throw new EmailError(
2016
+ "SMTP does not support search operations.",
2017
+ "UNSUPPORTED_OPERATION",
2018
+ "smtp"
2019
+ );
2020
+ }
2021
+ }
2022
+ const smtp = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2023
+ __proto__: null,
2024
+ SMTPAdapter
2025
+ }, Symbol.toStringTag, { value: "Module" }));
2026
+ function isSMTPOptions(opts) {
2027
+ return opts.type === "smtp";
2028
+ }
2029
+ function isIMAPOptions(opts) {
2030
+ return opts.type === "imap";
2031
+ }
2032
+ function isPOP3Options(opts) {
2033
+ return opts.type === "pop3";
2034
+ }
2035
+ function isGmailOptions(opts) {
2036
+ return opts.type === "gmail";
2037
+ }
2038
+ async function getEmailClient(options) {
2039
+ const opts = await loadEnvironmentConfig(options);
2040
+ if (isSMTPOptions(opts)) {
2041
+ const { SMTPAdapter: SMTPAdapter2 } = await Promise.resolve().then(() => smtp);
2042
+ return new SMTPAdapter2(opts);
2043
+ }
2044
+ if (isIMAPOptions(opts)) {
2045
+ const { IMAPAdapter: IMAPAdapter2 } = await Promise.resolve().then(() => imap);
2046
+ return new IMAPAdapter2(opts);
2047
+ }
2048
+ if (isPOP3Options(opts)) {
2049
+ const { POP3Adapter: POP3Adapter2 } = await Promise.resolve().then(() => pop3);
2050
+ return new POP3Adapter2(opts);
2051
+ }
2052
+ if (isGmailOptions(opts)) {
2053
+ const { GmailAdapter: GmailAdapter2 } = await Promise.resolve().then(() => gmail);
2054
+ return new GmailAdapter2(opts);
2055
+ }
2056
+ throw new Error(
2057
+ `Unknown email client type: ${opts.type}`
2058
+ );
2059
+ }
2060
+ async function loadEnvironmentConfig(options) {
2061
+ if (!options.type && process.env.HAVE_EMAIL_TYPE) {
2062
+ options = {
2063
+ ...options,
2064
+ type: process.env.HAVE_EMAIL_TYPE
2065
+ };
2066
+ }
2067
+ if (options.type === "smtp") {
2068
+ return loadSMTPEnvironmentConfig(options);
2069
+ }
2070
+ if (options.type === "imap") {
2071
+ return loadIMAPEnvironmentConfig(options);
2072
+ }
2073
+ if (options.type === "pop3") {
2074
+ return loadPOP3EnvironmentConfig(options);
2075
+ }
2076
+ if (options.type === "gmail") {
2077
+ return loadGmailEnvironmentConfig(options);
2078
+ }
2079
+ return options;
2080
+ }
2081
+ function loadSMTPEnvironmentConfig(options) {
2082
+ return {
2083
+ ...options,
2084
+ host: options.host || process.env.HAVE_EMAIL_SMTP_HOST || process.env.HAVE_EMAIL_HOST || "",
2085
+ port: options.port || Number.parseInt(
2086
+ process.env.HAVE_EMAIL_SMTP_PORT || process.env.HAVE_EMAIL_PORT || "587",
2087
+ 10
2088
+ ),
2089
+ secure: options.secure ?? (process.env.HAVE_EMAIL_SMTP_SECURE || process.env.HAVE_EMAIL_SECURE) === "true",
2090
+ auth: options.auth || {
2091
+ user: process.env.HAVE_EMAIL_USER || process.env.HAVE_EMAIL_SMTP_USER,
2092
+ pass: process.env.HAVE_EMAIL_PASSWORD || process.env.HAVE_EMAIL_SMTP_PASSWORD
2093
+ },
2094
+ debug: options.debug ?? process.env.HAVE_EMAIL_DEBUG === "true"
2095
+ };
2096
+ }
2097
+ function loadIMAPEnvironmentConfig(options) {
2098
+ return {
2099
+ ...options,
2100
+ host: options.host || process.env.HAVE_EMAIL_IMAP_HOST || process.env.HAVE_EMAIL_HOST || "",
2101
+ port: options.port || Number.parseInt(
2102
+ process.env.HAVE_EMAIL_IMAP_PORT || process.env.HAVE_EMAIL_PORT || "993",
2103
+ 10
2104
+ ),
2105
+ secure: options.secure ?? (process.env.HAVE_EMAIL_IMAP_SECURE || process.env.HAVE_EMAIL_SECURE) === "true",
2106
+ auth: options.auth || {
2107
+ user: process.env.HAVE_EMAIL_USER || process.env.HAVE_EMAIL_IMAP_USER,
2108
+ pass: process.env.HAVE_EMAIL_PASSWORD || process.env.HAVE_EMAIL_IMAP_PASSWORD
2109
+ },
2110
+ debug: options.debug ?? process.env.HAVE_EMAIL_DEBUG === "true"
2111
+ };
2112
+ }
2113
+ function loadPOP3EnvironmentConfig(options) {
2114
+ return {
2115
+ ...options,
2116
+ host: options.host || process.env.HAVE_EMAIL_POP3_HOST || process.env.HAVE_EMAIL_HOST || "",
2117
+ port: options.port || Number.parseInt(
2118
+ process.env.HAVE_EMAIL_POP3_PORT || process.env.HAVE_EMAIL_PORT || "995",
2119
+ 10
2120
+ ),
2121
+ secure: options.secure ?? (process.env.HAVE_EMAIL_POP3_SECURE || process.env.HAVE_EMAIL_SECURE) === "true",
2122
+ auth: options.auth || {
2123
+ user: process.env.HAVE_EMAIL_USER || process.env.HAVE_EMAIL_POP3_USER,
2124
+ pass: process.env.HAVE_EMAIL_PASSWORD || process.env.HAVE_EMAIL_POP3_PASSWORD
2125
+ },
2126
+ debug: options.debug ?? process.env.HAVE_EMAIL_DEBUG === "true"
2127
+ };
2128
+ }
2129
+ function loadGmailEnvironmentConfig(options) {
2130
+ return {
2131
+ ...options,
2132
+ auth: options.auth || {
2133
+ clientId: process.env.HAVE_EMAIL_GMAIL_CLIENT_ID || "",
2134
+ clientSecret: process.env.HAVE_EMAIL_GMAIL_CLIENT_SECRET || "",
2135
+ refreshToken: process.env.HAVE_EMAIL_GMAIL_REFRESH_TOKEN || "",
2136
+ accessToken: process.env.HAVE_EMAIL_GMAIL_ACCESS_TOKEN
2137
+ },
2138
+ userId: options.userId || process.env.HAVE_EMAIL_GMAIL_USER_ID || "me",
2139
+ debug: options.debug ?? process.env.HAVE_EMAIL_DEBUG === "true"
2140
+ };
2141
+ }
2142
+ export {
2143
+ AttachmentError,
2144
+ AuthenticationError,
2145
+ AuthorizationError,
2146
+ BaseEmailClient,
2147
+ ConnectionError,
2148
+ EmailError,
2149
+ FolderExistsError,
2150
+ FolderNotFoundError,
2151
+ GmailAdapter,
2152
+ IMAPAdapter,
2153
+ InvalidMessageError,
2154
+ MessageNotFoundError,
2155
+ POP3Adapter,
2156
+ SMTPAdapter,
2157
+ SendError,
2158
+ TimeoutError,
2159
+ getEmailClient,
2160
+ isGmailOptions,
2161
+ isIMAPOptions,
2162
+ isPOP3Options,
2163
+ isSMTPOptions
2164
+ };
2165
+ //# sourceMappingURL=index.js.map