@bobfrankston/iflow 1.0.2

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.
@@ -0,0 +1,10 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(find:*)",
5
+ "Bash(git init:*)",
6
+ "WebFetch(domain:imapflow.com)"
7
+ ],
8
+ "deny": []
9
+ }
10
+ }
@@ -0,0 +1,35 @@
1
+ import imapflow from 'imapflow';
2
+ import { ImapClientConfig, FetchedMessage } from './types.js';
3
+ export interface iFetchOptions {
4
+ source: boolean;
5
+ }
6
+ export declare class ImapClient {
7
+ private client;
8
+ private config;
9
+ private isDestroying;
10
+ private connectionPromise;
11
+ constructor(cfg: ImapClientConfig);
12
+ private init;
13
+ private handleReconnection;
14
+ private cleanup;
15
+ private withConnection;
16
+ getMessagesCount(mailbox: string): Promise<number>;
17
+ fetchMessageByDate(mailbox: string, start: Date | number, endingx?: Date | number, options?: iFetchOptions): Promise<FetchedMessage[]>;
18
+ fetchMessages(mailbox: string, end: number, count: number, options?: iFetchOptions): Promise<FetchedMessage[]>;
19
+ moveMessage(msg: FetchedMessage, sourceMailbox: string, targetMailbox: string): Promise<void>;
20
+ getFolderList(): Promise<imapflow.ListResponse[]>;
21
+ getFolderTree(): Promise<imapflow.ListTreeResponse>;
22
+ getSpecialFolders(mailboxes: Awaited<ReturnType<ImapClient["getFolderList"]>>): {
23
+ inbox: string;
24
+ trash: string;
25
+ spam: string;
26
+ sent: string;
27
+ drafts: string;
28
+ archive: string;
29
+ junk: string;
30
+ };
31
+ createmailbox(folder: string[] | string): Promise<imapflow.MailboxCreateResponse>;
32
+ logout(): Promise<void>;
33
+ connect(): Promise<void>;
34
+ }
35
+ //# sourceMappingURL=ImapClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImapClient.d.ts","sourceRoot":"","sources":["ImapClient.ts"],"names":[],"mappings":"AAGA,OAAO,QAAoE,MAAM,UAAU,CAAC;AAC5F,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAc,MAAM,YAAY,CAAC;AAO1E,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,OAAO,CAAC;CACnB;AAMD,qBAAa,UAAU;IACnB,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,iBAAiB,CAA8B;gBAE3C,GAAG,EAAE,gBAAgB;IAKjC,OAAO,CAAC,IAAI;YA4BE,kBAAkB;IAgChC,OAAO,CAAC,OAAO;YAQD,cAAc;IAmCtB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOlD,kBAAkB,CACpB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,IAAI,GAAG,MAAM,EACpB,OAAO,CAAC,EAAE,IAAI,GAAG,MAAM,EACvB,OAAO,CAAC,EAAE,aAAa;IAmCrB,aAAa,CACf,OAAO,EAAE,MAAM,EAEf,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,aAAa;IAsCrB,WAAW,CAAC,GAAG,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;IAwB7E,aAAa;IAMb,aAAa;IAMnB,iBAAiB,CAAC,SAAS,EAAE,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC;eAIlD,MAAM;eACN,MAAM;cACP,MAAM;cACN,MAAM;gBACJ,MAAM;iBACL,MAAM;cACT,MAAM;;IAgD1B,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM;IAQvC,MAAM;IAWN,OAAO;CAwChB"}
@@ -0,0 +1,312 @@
1
+ // copilot: disable all suggestions related to testing frameworks like jest, mocha, etc.
2
+ // copilot: do not include the return type for functions unless necessary
3
+ import { ImapFlow } from 'imapflow';
4
+ import { FetchedMessage } from './types.js';
5
+ import { styleText } from 'util';
6
+ const defaultFetchOptions = {
7
+ source: true
8
+ };
9
+ export class ImapClient {
10
+ client;
11
+ config;
12
+ isDestroying = false;
13
+ connectionPromise = null;
14
+ constructor(cfg) {
15
+ this.config = cfg;
16
+ this.init();
17
+ }
18
+ init() {
19
+ this.client = new ImapFlow({
20
+ host: this.config.server,
21
+ port: this.config.port,
22
+ secure: true,
23
+ auth: {
24
+ user: this.config.username,
25
+ pass: this.config.password
26
+ },
27
+ logger: false, // Disable internal logging
28
+ });
29
+ // Set up error handlers
30
+ this.client.on('error', (err) => {
31
+ console.error(`ImapFlow socket error: ${err.message}`);
32
+ if (!this.isDestroying) {
33
+ this.handleReconnection();
34
+ }
35
+ });
36
+ this.client.on('close', () => {
37
+ console.error(`ImapFlow connection closed`);
38
+ if (!this.isDestroying) {
39
+ this.handleReconnection();
40
+ }
41
+ });
42
+ }
43
+ async handleReconnection() {
44
+ if (this.connectionPromise)
45
+ return; // Already reconnecting
46
+ this.connectionPromise = new Promise(async (resolve) => {
47
+ try {
48
+ // Clean up old client
49
+ this.cleanup();
50
+ // Wait before reconnecting
51
+ await new Promise(r => setTimeout(r, 1000));
52
+ // Reinitialize
53
+ this.init();
54
+ try {
55
+ await this.client.connect();
56
+ console.log('Reconnected successfully');
57
+ }
58
+ catch (err) {
59
+ console.error('Reconnection failed, will retry:', err);
60
+ // Will trigger another reconnection attempt via error handler
61
+ }
62
+ }
63
+ catch (err) {
64
+ console.error('Error during reconnection process:', err);
65
+ }
66
+ finally {
67
+ this.connectionPromise = null;
68
+ resolve();
69
+ }
70
+ });
71
+ return this.connectionPromise;
72
+ }
73
+ cleanup() {
74
+ if (this.client) {
75
+ this.client.removeAllListeners('error');
76
+ this.client.removeAllListeners('close');
77
+ }
78
+ }
79
+ // Wrapper to ensure all operations handle connection errors
80
+ async withConnection(operation) {
81
+ try {
82
+ await this.connect();
83
+ return await operation();
84
+ }
85
+ catch (err) {
86
+ console.error(`Operation failed: ${err.message}`);
87
+ // Check if it's a connection-related error
88
+ if (err.code === 'ECONNRESET' || err.code === 'ENOTFOUND' ||
89
+ err.code === 'ETIMEDOUT' || err.code === 'ECONNREFUSED' ||
90
+ err.message.includes('connection') || err.message.includes('socket')) {
91
+ if (!this.isDestroying) {
92
+ console.log('Connection error detected, attempting reconnection...');
93
+ await this.handleReconnection();
94
+ // Retry the operation once after reconnection
95
+ try {
96
+ await this.connect();
97
+ return await operation();
98
+ }
99
+ catch (retryErr) {
100
+ console.error(`Operation failed after reconnection: ${retryErr}`);
101
+ throw retryErr;
102
+ }
103
+ }
104
+ }
105
+ throw err;
106
+ }
107
+ }
108
+ // https://imapflow.com/module-imapflow-ImapFlow.html
109
+ // https://imapflow.com/module-imapflow-ImapFlow.html#search
110
+ async getMessagesCount(mailbox) {
111
+ return this.withConnection(async () => {
112
+ const status = await this.client.status(mailbox, { messages: true });
113
+ return status.messages;
114
+ });
115
+ }
116
+ async fetchMessageByDate(mailbox, start, endingx, options) {
117
+ try {
118
+ options = { ...defaultFetchOptions, ...options }; // Merge with default options
119
+ await this.connect(); // Ensure connected
120
+ const startTime = Date.now();
121
+ const peek = true; // always peek for now
122
+ await this.client.mailboxOpen(mailbox, { readOnly: peek });
123
+ if (typeof start === 'number')
124
+ start = new Date(start);
125
+ if (endingx && typeof endingx === 'number')
126
+ endingx = new Date(endingx);
127
+ const ending = endingx; // Hack for typescript typing
128
+ const searchCriteria = endingx ? { since: start, before: ending } : { on: start };
129
+ const fetchQuery = {
130
+ uid: true,
131
+ envelope: true,
132
+ flags: true,
133
+ headers: true,
134
+ source: options.source
135
+ };
136
+ const messageObjects = await this.client.fetchAll(searchCriteria, fetchQuery);
137
+ // const messageObjects = await this.client.fetchAll(uids, fetchQuery);
138
+ const messages = messageObjects.map(msg => new FetchedMessage(msg));
139
+ // if (this.config.verbose) console.log(`Fetched ${messages.length} messages in ${Date.now() - startTime}ms`);
140
+ return messages;
141
+ }
142
+ catch (error) {
143
+ console.error(`Error fetching messages by date`, error);
144
+ debugger;
145
+ throw error; // rethrow to handle it outside
146
+ }
147
+ finally {
148
+ await this.client.mailboxClose();
149
+ }
150
+ }
151
+ async fetchMessages(mailbox,
152
+ // start: number,
153
+ end, count, options) {
154
+ try {
155
+ options = { ...defaultFetchOptions, ...options }; // Merge with default options
156
+ await this.connect(); // Ensure connected
157
+ const peek = true; // always peek for now
158
+ await this.client.mailboxOpen(mailbox, { readOnly: peek });
159
+ let start = end - count + 1;
160
+ const range = `${start}:${end}`;
161
+ const startTime = Date.now();
162
+ const fetchQuery = {
163
+ uid: true,
164
+ envelope: true,
165
+ flags: true,
166
+ // bodyStructure: true,
167
+ headers: true,
168
+ source: options.source // Fetch the raw RFC822 message
169
+ };
170
+ const messageObjects = await this.client.fetchAll(range, fetchQuery);
171
+ // Testing
172
+ const messages = messageObjects.map(msg => new FetchedMessage(msg));
173
+ // console.log(`Fetched ${messages.length} messages in ${Date.now() - startTime}ms`);
174
+ return messages;
175
+ }
176
+ catch (error) {
177
+ console.error(`Error fetching messages: ${error.message}`);
178
+ debugger;
179
+ throw error; // rethrow to handle it outside
180
+ }
181
+ finally {
182
+ await this.client.mailboxClose();
183
+ // await this.client.logout();
184
+ }
185
+ }
186
+ // async moveMessage(uid: number, sourceMailbox: string, targetMailbox: string) {
187
+ async moveMessage(msg, sourceMailbox, targetMailbox) {
188
+ try {
189
+ await this.connect(); // Ensure connected
190
+ await this.client.mailboxOpen(sourceMailbox, { readOnly: false }); // Open the source mailbox in read-write mode
191
+ const result = await this.client.messageMove([msg.uid], targetMailbox, { uid: true });
192
+ if (result && !result.uidMap) {
193
+ console.log(styleText(['red', 'bgBlack'], `iflow/iMapClient: NO uidMap: Failed to move message UID ${msg.uid} from ${sourceMailbox} to ${targetMailbox}`));
194
+ return; // Treat as nonfatal
195
+ }
196
+ if (!result || !result.uidMap.has(msg.uid)) {
197
+ debugger;
198
+ throw new Error(`Message with UID ${msg.uid} was not moved.`);
199
+ }
200
+ }
201
+ catch (error) {
202
+ const ermsg = `iflow/iMapClient: Error moving message UID ${msg.uid}: ${error.message}`;
203
+ console.log(styleText(['red', 'bgBlack'], ermsg));
204
+ debugger;
205
+ throw error;
206
+ }
207
+ finally {
208
+ await this.client.mailboxClose();
209
+ }
210
+ }
211
+ async getFolderList() {
212
+ return this.withConnection(async () => {
213
+ return await this.client.list();
214
+ });
215
+ }
216
+ async getFolderTree() {
217
+ return this.withConnection(async () => {
218
+ return await this.client.listTree();
219
+ });
220
+ }
221
+ getSpecialFolders(mailboxes) {
222
+ try {
223
+ // await this.client.connect();
224
+ const specialFolders = {
225
+ inbox: null,
226
+ trash: null,
227
+ spam: null,
228
+ sent: null,
229
+ drafts: null,
230
+ archive: null,
231
+ junk: null,
232
+ };
233
+ for (const box of mailboxes) {
234
+ // normalize flags/specialUse
235
+ const rawFlags = Array.isArray(box.flags)
236
+ ? box.flags
237
+ : box.flags
238
+ ? [box.flags]
239
+ : [];
240
+ const rawSpecial = Array.isArray(box.specialUse)
241
+ ? box.specialUse
242
+ : box.specialUse
243
+ ? [box.specialUse]
244
+ : [];
245
+ const attrs = [...rawFlags, ...rawSpecial];
246
+ const name = box.path;
247
+ if (attrs.includes('\\Inbox') || name.toLowerCase() === 'inbox')
248
+ specialFolders.inbox = name;
249
+ if (attrs.includes('\\Trash'))
250
+ specialFolders.trash = name;
251
+ if (attrs.includes('\\Junk') || attrs.includes('\\Spam'))
252
+ specialFolders.spam = name;
253
+ if (attrs.includes('\\Sent'))
254
+ specialFolders.sent = name;
255
+ if (attrs.includes('\\Drafts'))
256
+ specialFolders.drafts = name;
257
+ if (attrs.includes('\\Archive'))
258
+ specialFolders.archive = name;
259
+ if (attrs.includes('\\Junk'))
260
+ specialFolders.junk = name;
261
+ // TODO: Figure out why trash isn't trash
262
+ // specialFolders.trash = "TRASH"; // for dovcoat
263
+ 7; // Place for breakpoints
264
+ }
265
+ return specialFolders;
266
+ }
267
+ catch (error) {
268
+ console.error(`Error getting special folders: ${error.message}`);
269
+ debugger;
270
+ throw error;
271
+ }
272
+ finally {
273
+ // await this.client.logout();
274
+ }
275
+ }
276
+ async createmailbox(folder) {
277
+ return this.withConnection(async () => {
278
+ let info = await this.client.mailboxCreate(folder);
279
+ console.log(`Created mailbox: ${info.path}`);
280
+ return info;
281
+ });
282
+ }
283
+ async logout() {
284
+ this.isDestroying = true;
285
+ this.cleanup();
286
+ try {
287
+ await this.client.logout();
288
+ }
289
+ catch (err) {
290
+ // Ignore logout errors - connection might already be closed
291
+ console.log(`Logout error (ignored): ${err.message}`);
292
+ }
293
+ }
294
+ async connect() {
295
+ try {
296
+ if (this.connectionPromise) {
297
+ await this.connectionPromise;
298
+ }
299
+ if (!this.client.usable) {
300
+ await this.client.connect();
301
+ }
302
+ }
303
+ catch (err) {
304
+ console.error(`Connection failed: ${err.message}`);
305
+ if (!this.isDestroying) {
306
+ await this.handleReconnection();
307
+ }
308
+ throw err;
309
+ }
310
+ }
311
+ }
312
+ //# sourceMappingURL=ImapClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImapClient.js","sourceRoot":"","sources":["ImapClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,376 @@
1
+ // copilot: disable all suggestions related to testing frameworks like jest, mocha, etc.
2
+ // copilot: do not include the return type for functions unless necessary
3
+
4
+ import imapflow, { FetchQueryObject, ImapFlow, SearchObject, StatusObject } from 'imapflow';
5
+ import { ImapClientConfig, FetchedMessage, FetchQuery } from './types.js';
6
+ import { styleText } from 'util';
7
+ // Removed dependencies on parent project - these should be passed in or handled differently
8
+
9
+ // TODO: Create our own range object and allow a nosource flag
10
+
11
+
12
+ export interface iFetchOptions {
13
+ source: boolean;
14
+ }
15
+
16
+ const defaultFetchOptions: iFetchOptions = {
17
+ source: true
18
+ }
19
+
20
+ export class ImapClient {
21
+ private client: ImapFlow;
22
+ private config: ImapClientConfig;
23
+ private isDestroying: boolean = false;
24
+ private connectionPromise: Promise<void> | null = null;
25
+
26
+ constructor(cfg: ImapClientConfig) {
27
+ this.config = cfg;
28
+ this.init();
29
+ }
30
+
31
+ private init() {
32
+ this.client = new ImapFlow({
33
+ host: this.config.server,
34
+ port: this.config.port,
35
+ secure: true,
36
+ auth: {
37
+ user: this.config.username,
38
+ pass: this.config.password
39
+ },
40
+ logger: false, // Disable internal logging
41
+ });
42
+
43
+ // Set up error handlers
44
+ this.client.on('error', (err: any) => {
45
+ console.error(`ImapFlow socket error: ${err.message}`);
46
+ if (!this.isDestroying) {
47
+ this.handleReconnection();
48
+ }
49
+ });
50
+
51
+ this.client.on('close', () => {
52
+ console.error(`ImapFlow connection closed`);
53
+ if (!this.isDestroying) {
54
+ this.handleReconnection();
55
+ }
56
+ });
57
+ }
58
+
59
+ private async handleReconnection() {
60
+ if (this.connectionPromise) return; // Already reconnecting
61
+
62
+ this.connectionPromise = new Promise(async (resolve) => {
63
+ try {
64
+ // Clean up old client
65
+ this.cleanup();
66
+
67
+ // Wait before reconnecting
68
+ await new Promise(r => setTimeout(r, 1000));
69
+
70
+ // Reinitialize
71
+ this.init();
72
+
73
+ try {
74
+ await this.client.connect();
75
+ console.log('Reconnected successfully');
76
+ } catch (err) {
77
+ console.error('Reconnection failed, will retry:', err);
78
+ // Will trigger another reconnection attempt via error handler
79
+ }
80
+ } catch (err) {
81
+ console.error('Error during reconnection process:', err);
82
+ } finally {
83
+ this.connectionPromise = null;
84
+ resolve();
85
+ }
86
+ });
87
+
88
+ return this.connectionPromise;
89
+ }
90
+
91
+ private cleanup() {
92
+ if (this.client) {
93
+ this.client.removeAllListeners('error');
94
+ this.client.removeAllListeners('close');
95
+ }
96
+ }
97
+
98
+ // Wrapper to ensure all operations handle connection errors
99
+ private async withConnection<T>(operation: () => Promise<T>): Promise<T> {
100
+ try {
101
+ await this.connect();
102
+ return await operation();
103
+ } catch (err: any) {
104
+ console.error(`Operation failed: ${err.message}`);
105
+
106
+ // Check if it's a connection-related error
107
+ if (err.code === 'ECONNRESET' || err.code === 'ENOTFOUND' ||
108
+ err.code === 'ETIMEDOUT' || err.code === 'ECONNREFUSED' ||
109
+ err.message.includes('connection') || err.message.includes('socket')) {
110
+
111
+ if (!this.isDestroying) {
112
+ console.log('Connection error detected, attempting reconnection...');
113
+ await this.handleReconnection();
114
+
115
+ // Retry the operation once after reconnection
116
+ try {
117
+ await this.connect();
118
+ return await operation();
119
+ } catch (retryErr) {
120
+ console.error(`Operation failed after reconnection: ${retryErr}`);
121
+ throw retryErr;
122
+ }
123
+ }
124
+ }
125
+
126
+ throw err;
127
+ }
128
+ }
129
+
130
+ // https://imapflow.com/module-imapflow-ImapFlow.html
131
+
132
+ // https://imapflow.com/module-imapflow-ImapFlow.html#search
133
+
134
+ async getMessagesCount(mailbox: string): Promise<number> {
135
+ return this.withConnection(async () => {
136
+ const status: StatusObject = await this.client.status(mailbox, { messages: true });
137
+ return status.messages;
138
+ });
139
+ }
140
+
141
+ async fetchMessageByDate(
142
+ mailbox: string,
143
+ start: Date | number,
144
+ endingx?: Date | number,
145
+ options?: iFetchOptions) {
146
+ try {
147
+ options = {...defaultFetchOptions, ...options}; // Merge with default options
148
+ await this.connect(); // Ensure connected
149
+ const startTime = Date.now();
150
+
151
+ const peek = true; // always peek for now
152
+ await this.client.mailboxOpen(mailbox, { readOnly: peek });
153
+
154
+ if (typeof start === 'number') start = new Date(start);
155
+ if (endingx && typeof endingx === 'number') endingx = new Date(endingx);
156
+ const ending = endingx as Date; // Hack for typescript typing
157
+ const searchCriteria: SearchObject = endingx ? { since: start, before: ending } : { on: start }
158
+
159
+ const fetchQuery: FetchQueryObject = {
160
+ uid: true,
161
+ envelope: true,
162
+ flags: true,
163
+ headers: true,
164
+ source: options.source
165
+ };
166
+ const messageObjects = await this.client.fetchAll(searchCriteria, fetchQuery);
167
+ // const messageObjects = await this.client.fetchAll(uids, fetchQuery);
168
+ const messages = messageObjects.map(msg => new FetchedMessage(msg));
169
+ // if (this.config.verbose) console.log(`Fetched ${messages.length} messages in ${Date.now() - startTime}ms`);
170
+ return messages;
171
+ } catch (error: any) {
172
+ console.error(`Error fetching messages by date`, error);
173
+ debugger;
174
+ throw error; // rethrow to handle it outside
175
+ } finally {
176
+ await this.client.mailboxClose();
177
+ }
178
+ }
179
+
180
+ async fetchMessages(
181
+ mailbox: string,
182
+ // start: number,
183
+ end: number,
184
+ count: number,
185
+ options?: iFetchOptions
186
+ ) {
187
+ try {
188
+ options = {...defaultFetchOptions, ...options}; // Merge with default options
189
+ await this.connect(); // Ensure connected
190
+ const peek = true; // always peek for now
191
+ await this.client.mailboxOpen(mailbox, { readOnly: peek });
192
+ let start = end - count + 1;
193
+ const range = `${start}:${end}`;
194
+ const startTime = Date.now();
195
+
196
+ const fetchQuery: FetchQuery = {
197
+ uid: true,
198
+ envelope: true,
199
+ flags: true,
200
+ // bodyStructure: true,
201
+ headers: true,
202
+ source: options.source // Fetch the raw RFC822 message
203
+ };
204
+
205
+ const messageObjects = await this.client.fetchAll(range, fetchQuery);
206
+ // Testing
207
+ const messages = messageObjects.map(msg => new FetchedMessage(msg));
208
+ // console.log(`Fetched ${messages.length} messages in ${Date.now() - startTime}ms`);
209
+ return messages;
210
+ }
211
+ catch (error: any) {
212
+ console.error(`Error fetching messages: ${error.message}`);
213
+ debugger;
214
+ throw error; // rethrow to handle it outside
215
+ }
216
+ finally {
217
+ await this.client.mailboxClose();
218
+ // await this.client.logout();
219
+ }
220
+ }
221
+
222
+ // async moveMessage(uid: number, sourceMailbox: string, targetMailbox: string) {
223
+ async moveMessage(msg: FetchedMessage, sourceMailbox: string, targetMailbox: string) {
224
+ try {
225
+ await this.connect(); // Ensure connected
226
+ await this.client.mailboxOpen(sourceMailbox, { readOnly: false }); // Open the source mailbox in read-write mode
227
+ const result = await this.client.messageMove([msg.uid], targetMailbox, { uid: true });
228
+ if (result && !result.uidMap) {
229
+ console.log(styleText(['red', 'bgBlack'],
230
+ `iflow/iMapClient: NO uidMap: Failed to move message UID ${msg.uid} from ${sourceMailbox} to ${targetMailbox}`));
231
+ return; // Treat as nonfatal
232
+ }
233
+ if (!result || !result.uidMap.has(msg.uid)) {
234
+ debugger;
235
+ throw new Error(`Message with UID ${msg.uid} was not moved.`);
236
+ }
237
+ } catch (error: any) {
238
+ const ermsg = `iflow/iMapClient: Error moving message UID ${msg.uid}: ${error.message}`;
239
+ console.log(styleText(['red', 'bgBlack'], ermsg));
240
+ debugger;
241
+ throw error;
242
+ } finally {
243
+ await this.client.mailboxClose();
244
+ }
245
+ }
246
+
247
+ async getFolderList() {
248
+ return this.withConnection(async () => {
249
+ return await this.client.list();
250
+ });
251
+ }
252
+
253
+ async getFolderTree() {
254
+ return this.withConnection(async () => {
255
+ return await this.client.listTree();
256
+ });
257
+ }
258
+
259
+ getSpecialFolders(mailboxes: Awaited<ReturnType<ImapClient["getFolderList"]>>) {
260
+ try {
261
+ // await this.client.connect();
262
+ const specialFolders = {
263
+ inbox: null as string,
264
+ trash: null as string,
265
+ spam: null as string,
266
+ sent: null as string,
267
+ drafts: null as string,
268
+ archive: null as string,
269
+ junk: null as string,
270
+ };
271
+
272
+ for (const box of mailboxes) {
273
+ // normalize flags/specialUse
274
+ const rawFlags = Array.isArray(box.flags)
275
+ ? box.flags
276
+ : box.flags
277
+ ? [box.flags]
278
+ : [];
279
+ const rawSpecial = Array.isArray(box.specialUse)
280
+ ? box.specialUse
281
+ : box.specialUse
282
+ ? [box.specialUse]
283
+ : [];
284
+ const attrs = [...rawFlags, ...rawSpecial];
285
+ const name = box.path;
286
+
287
+ if (attrs.includes('\\Inbox') || name.toLowerCase() === 'inbox')
288
+ specialFolders.inbox = name;
289
+ if (attrs.includes('\\Trash'))
290
+ specialFolders.trash = name;
291
+ if (attrs.includes('\\Junk') || attrs.includes('\\Spam'))
292
+ specialFolders.spam = name;
293
+ if (attrs.includes('\\Sent'))
294
+ specialFolders.sent = name;
295
+ if (attrs.includes('\\Drafts'))
296
+ specialFolders.drafts = name;
297
+ if (attrs.includes('\\Archive'))
298
+ specialFolders.archive = name;
299
+ if (attrs.includes('\\Junk'))
300
+ specialFolders.junk = name;
301
+
302
+ // TODO: Figure out why trash isn't trash
303
+ // specialFolders.trash = "TRASH"; // for dovcoat
304
+ 7; // Place for breakpoints
305
+ }
306
+
307
+ return specialFolders;
308
+ } catch (error: any) {
309
+ console.error(`Error getting special folders: ${error.message}`);
310
+ debugger;
311
+ throw error;
312
+ } finally {
313
+ // await this.client.logout();
314
+ }
315
+ }
316
+
317
+ async createmailbox(folder: string[] | string) {
318
+ return this.withConnection(async () => {
319
+ let info = await this.client.mailboxCreate(folder);
320
+ console.log(`Created mailbox: ${info.path}`);
321
+ return info;
322
+ });
323
+ }
324
+
325
+ async logout() {
326
+ this.isDestroying = true;
327
+ this.cleanup();
328
+ try {
329
+ await this.client.logout();
330
+ } catch (err: any) {
331
+ // Ignore logout errors - connection might already be closed
332
+ console.log(`Logout error (ignored): ${err.message}`);
333
+ }
334
+ }
335
+
336
+ async connect() {
337
+ try {
338
+ if (this.connectionPromise) {
339
+ await this.connectionPromise;
340
+ }
341
+ if (!this.client.usable) {
342
+ await this.client.connect();
343
+ }
344
+ } catch (err: any) {
345
+ console.error(`Connection failed: ${err.message}`);
346
+ if (!this.isDestroying) {
347
+ await this.handleReconnection();
348
+ }
349
+ throw err;
350
+ }
351
+ }
352
+
353
+ // Getter to access underlying ImapFlow client for advanced operations
354
+ // get imapFlowClient() {
355
+ // if (!this.client) {
356
+ // this.init();
357
+ // }
358
+ // return this.client;
359
+ // }
360
+
361
+ // static async run<T>(config: ImapClientConfig, f: () => Promise<T>) {
362
+ // const imap = new ImapClient(config);
363
+
364
+ // try {
365
+ // await imap.connect();
366
+ // return f();
367
+ // }
368
+ // catch (error: any) {
369
+ // logerr(`Error in ImapClient run`, error);
370
+ // debugger;
371
+ // throw error;
372
+ // } finally {
373
+ // await imap.logout();
374
+ // }
375
+ // }
376
+ }
@@ -0,0 +1,57 @@
1
+ import imapflow from 'imapflow';
2
+ declare class SimpleMap<T> {
3
+ private items;
4
+ add(key: string, value: T): void;
5
+ get(key: string): T[] | undefined;
6
+ }
7
+ export type MessageEnvelope = imapflow.MessageEnvelopeObject;
8
+ export interface ImapClientConfig {
9
+ server: string;
10
+ port: number;
11
+ username: string;
12
+ password: string;
13
+ verbose?: boolean;
14
+ }
15
+ export interface FetchQuery extends imapflow.FetchQueryObject {
16
+ }
17
+ export type FetchPart = keyof imapflow.FetchQueryObject;
18
+ export declare class FetchedMessage {
19
+ private fmo;
20
+ parsedHeader: string[][];
21
+ headerSet: SimpleMap<string>;
22
+ _source: string;
23
+ constructor(init: imapflow.FetchMessageObject);
24
+ get flagged(): boolean;
25
+ get answered(): boolean;
26
+ get deleted(): boolean;
27
+ get seen(): boolean;
28
+ get unread(): boolean;
29
+ get draft(): boolean;
30
+ get hasFlag(): boolean;
31
+ get to(): imapflow.MessageAddressObject[];
32
+ get cc(): imapflow.MessageAddressObject[];
33
+ get bcc(): imapflow.MessageAddressObject[];
34
+ get from(): imapflow.MessageAddressObject[];
35
+ get replyTo(): imapflow.MessageAddressObject[];
36
+ get sender(): imapflow.MessageAddressObject[];
37
+ get date(): Date;
38
+ get ymd(): string;
39
+ get tcsb(): Record<string, string[]>;
40
+ get seq(): number;
41
+ get uid(): number;
42
+ get modseq(): bigint;
43
+ private get envelope();
44
+ private get flags();
45
+ get internalDate(): string | Date;
46
+ get size(): number;
47
+ get bodyStructure(): imapflow.MessageStructureObject;
48
+ get source(): string;
49
+ get messageId(): string;
50
+ get listUnsubscribe(): string;
51
+ get xpriority(): string;
52
+ get importance(): string;
53
+ get deliveredTo(): string;
54
+ get subject(): string;
55
+ }
56
+ export {};
57
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAGhC,cAAM,SAAS,CAAC,CAAC;IACb,OAAO,CAAC,KAAK,CAA2B;IAExC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAMzB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,GAAG,SAAS;CAGpC;AAED,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,qBAAqB,CAAC;AAE7D,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAgBD,MAAM,WAAW,UAAW,SAAQ,QAAQ,CAAC,gBAAgB;CAC5D;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC;AAExD,qBAAa,cAAc;IACvB,OAAO,CAAC,GAAG,CAA8B;IACzC,YAAY,EAAE,MAAM,EAAE,EAAE,CAAC;IACzB,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;gBAEJ,IAAI,EAAE,QAAQ,CAAC,kBAAkB;IAa7C,IAAI,OAAO,YAA8C;IACzD,IAAI,QAAQ,YAA+C;IAC3D,IAAI,OAAO,YAA8C;IACzD,IAAI,IAAI,YAA2C;IACnD,IAAI,MAAM,YAAyB;IACnC,IAAI,KAAK,YAA4C;IACrD,IAAI,OAAO,YAAsC;IAEjD,IAAI,EAAE,oCAA+B;IACrC,IAAI,EAAE,oCAA+B;IACrC,IAAI,GAAG,oCAAgC;IACvC,IAAI,IAAI,oCAAiC;IACzC,IAAI,OAAO,oCAAoC;IAC/C,IAAI,MAAM,oCAAmC;IAC7C,IAAI,IAAI,SAAiC;IACzC,IAAI,GAAG,WAAkD;IACzD,IAAI,IAAI,IAA4B,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAI;IAEhE,IAAI,GAAG,WAA2B;IAClC,IAAI,GAAG,WAA2B;IAClC,IAAI,MAAM,WAA8B;IACxC,OAAO,KAAK,QAAQ,GAAgC;IACpD,OAAO,KAAK,KAAK,GAA6B;IAC9C,IAAI,YAAY,kBAAoC;IACpD,IAAI,IAAI,WAA4B;IACpC,IAAI,aAAa,oCAAqC;IACtD,IAAI,MAAM,WAA2B;IAErC,IAAI,SAAS,WAAsC;IACnD,IAAI,eAAe,WAA0D;IAC7E,IAAI,SAAS,WAAoD;IACjE,IAAI,UAAU,WAAoD;IAElE,IAAI,WAAW,WAAsD;IACrE,IAAI,OAAO,WAAoC;CAKlD"}
@@ -0,0 +1,63 @@
1
+ // Simple map implementation to replace maplca dependency
2
+ class SimpleMap {
3
+ items = {};
4
+ add(key, value) {
5
+ const k = key.toLowerCase();
6
+ if (!this.items[k])
7
+ this.items[k] = [];
8
+ this.items[k].push(value);
9
+ }
10
+ get(key) {
11
+ return this.items[key.toLowerCase()];
12
+ }
13
+ }
14
+ export class FetchedMessage {
15
+ fmo;
16
+ parsedHeader;
17
+ headerSet;
18
+ _source;
19
+ constructor(init) {
20
+ this.fmo = init;
21
+ this._source = init.source?.toString('utf8');
22
+ this.parsedHeader = this.fmo.headers.toString('utf8')
23
+ .replace(/\r\n(\s)/g, '$1')
24
+ .split('\r\n')
25
+ .filter(line => line.trim() !== '')
26
+ .map(line => line.split(': ', 2));
27
+ this.headerSet = new SimpleMap();
28
+ this.parsedHeader.forEach(([k, v]) => this.headerSet.add(k, v));
29
+ delete this.fmo.headers;
30
+ }
31
+ get flagged() { return this.fmo.flags.has('\\Flagged'); }
32
+ get answered() { return this.fmo.flags.has('\\Answered'); }
33
+ get deleted() { return this.fmo.flags.has('\\Deleted'); }
34
+ get seen() { return this.fmo.flags.has('\\Seen'); }
35
+ get unread() { return !this.seen; }
36
+ get draft() { return this.fmo.flags.has('\\Draft'); }
37
+ get hasFlag() { return this.fmo.flags.size > 0; }
38
+ get to() { return this.envelope.to; }
39
+ get cc() { return this.envelope.cc; }
40
+ get bcc() { return this.envelope.bcc; }
41
+ get from() { return this.envelope.from; }
42
+ get replyTo() { return this.envelope.replyTo; }
43
+ get sender() { return this.envelope.sender; }
44
+ get date() { return this.envelope.date; }
45
+ get ymd() { return this.date?.toLocaleDateString('se'); }
46
+ get tcsb() { return this; } // tcsb = to, cc, bcc
47
+ get seq() { return this.fmo.seq; }
48
+ get uid() { return this.fmo.uid; }
49
+ get modseq() { return this.fmo.modseq; }
50
+ get envelope() { return this.fmo.envelope; }
51
+ get flags() { return this.fmo.flags; }
52
+ get internalDate() { return this.fmo.internalDate; }
53
+ get size() { return this.fmo.size; }
54
+ get bodyStructure() { return this.fmo.bodyStructure; }
55
+ get source() { return this._source; }
56
+ get messageId() { return this.envelope.messageId; }
57
+ get listUnsubscribe() { return this.headerSet.get('List-Unsubscribe')?.[0]; }
58
+ get xpriority() { return this.headerSet.get('X-Priority')?.[0]; }
59
+ get importance() { return this.headerSet.get('Importance')?.[0]; }
60
+ get deliveredTo() { return this.headerSet.get('Delivered-To')?.[0]; }
61
+ get subject() { return this.envelope.subject; }
62
+ }
63
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ import imapflow from 'imapflow';
2
+
3
+ // Simple map implementation to replace maplca dependency
4
+ class SimpleMap<T> {
5
+ private items: Record<string, T[]> = {};
6
+
7
+ add(key: string, value: T) {
8
+ const k = key.toLowerCase();
9
+ if (!this.items[k]) this.items[k] = [];
10
+ this.items[k].push(value);
11
+ }
12
+
13
+ get(key: string): T[] | undefined {
14
+ return this.items[key.toLowerCase()];
15
+ }
16
+ }
17
+
18
+ export type MessageEnvelope = imapflow.MessageEnvelopeObject;
19
+
20
+ export interface ImapClientConfig {
21
+ server: string;
22
+ port: number;
23
+ username: string;
24
+ password: string;
25
+ verbose?: boolean;
26
+ }
27
+
28
+ // export interface ImapClientConfig {
29
+ // server: string;
30
+ // port: number;
31
+ // username: string;
32
+ // password?: string; // Make optional for OAuth
33
+ // oauth?: {
34
+ // accessToken: string;
35
+ // refreshToken: string;
36
+ // clientId: string;
37
+ // clientSecret: string;
38
+ // };
39
+ // verbose?: boolean;
40
+ // }
41
+
42
+ export interface FetchQuery extends imapflow.FetchQueryObject {
43
+ }
44
+
45
+ export type FetchPart = keyof imapflow.FetchQueryObject;
46
+
47
+ export class FetchedMessage {
48
+ private fmo: imapflow.FetchMessageObject;
49
+ parsedHeader: string[][];
50
+ headerSet: SimpleMap<string>;
51
+ _source: string;
52
+
53
+ constructor(init: imapflow.FetchMessageObject) {
54
+ this.fmo = init;
55
+ this._source = init.source?.toString('utf8');
56
+ this.parsedHeader = this.fmo.headers.toString('utf8')
57
+ .replace(/\r\n(\s)/g, '$1')
58
+ .split('\r\n')
59
+ .filter(line => line.trim() !== '')
60
+ .map(line => line.split(': ', 2));
61
+ this.headerSet = new SimpleMap<string>();
62
+ this.parsedHeader.forEach(([k, v]) => this.headerSet.add(k, v));
63
+ delete this.fmo.headers;
64
+ }
65
+
66
+ get flagged() { return this.fmo.flags.has('\\Flagged'); }
67
+ get answered() { return this.fmo.flags.has('\\Answered'); }
68
+ get deleted() { return this.fmo.flags.has('\\Deleted'); }
69
+ get seen() { return this.fmo.flags.has('\\Seen'); }
70
+ get unread() { return !this.seen; }
71
+ get draft() { return this.fmo.flags.has('\\Draft'); }
72
+ get hasFlag() { return this.fmo.flags.size > 0; }
73
+
74
+ get to() { return this.envelope.to; }
75
+ get cc() { return this.envelope.cc; }
76
+ get bcc() { return this.envelope.bcc; }
77
+ get from() { return this.envelope.from; }
78
+ get replyTo() { return this.envelope.replyTo; }
79
+ get sender() { return this.envelope.sender; }
80
+ get date() { return this.envelope.date; }
81
+ get ymd() { return this.date?.toLocaleDateString('se'); }
82
+ get tcsb() { return (this as any as Record<string, string[]>); } // tcsb = to, cc, bcc
83
+
84
+ get seq() { return this.fmo.seq; }
85
+ get uid() { return this.fmo.uid; }
86
+ get modseq() { return this.fmo.modseq; }
87
+ private get envelope() { return this.fmo.envelope; }
88
+ private get flags() { return this.fmo.flags; }
89
+ get internalDate() { return this.fmo.internalDate; }
90
+ get size() { return this.fmo.size; }
91
+ get bodyStructure() { return this.fmo.bodyStructure; }
92
+ get source() { return this._source; }
93
+
94
+ get messageId() { return this.envelope.messageId; }
95
+ get listUnsubscribe() { return this.headerSet.get('List-Unsubscribe')?.[0]; }
96
+ get xpriority() { return this.headerSet.get('X-Priority')?.[0]; }
97
+ get importance() { return this.headerSet.get('Importance')?.[0]; }
98
+
99
+ get deliveredTo() { return this.headerSet.get('Delivered-To')?.[0]; }
100
+ get subject() { return this.envelope.subject; }
101
+
102
+ // if you still need raw headers or bodyParts you can add:
103
+ // get rawHeaders() { return this.fmo.headers; }
104
+ // get bodyParts() { return this.fmo.bodyParts; }
105
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@bobfrankston/iflow",
3
+ "version": "1.0.2",
4
+ "description": "IMAP client wrapper library",
5
+ "main": "imaplib/ImapClient.js",
6
+ "types": "imaplib/ImapClient.ts",
7
+ "type": "module",
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "watch": "tsc -watch"
11
+ },
12
+ "keywords": [
13
+ "imap",
14
+ "email",
15
+ "client"
16
+ ],
17
+ "author": "",
18
+ "license": "ISC",
19
+ "dependencies": {
20
+ "@types/imapflow": "^1.0.22",
21
+ "imapflow": "^1.0.187"
22
+ },
23
+ "exports": {
24
+ ".": {
25
+ "types": "./imaplib/types.ts",
26
+ "default": "./imaplib/types.js"
27
+ },
28
+ "./client": {
29
+ "types": "./imaplib/ImapClient.ts",
30
+ "default": "./imaplib/ImapClient.js"
31
+ }
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^24.1.0"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/BobFrankston/iflow.git"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/BobFrankston/iflow/issues"
42
+ },
43
+ "homepage": "https://github.com/BobFrankston/iflow#readme"
44
+ }