@bobfrankston/iflow 1.0.38 → 1.0.40
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/imaplib/ImapClient.js +6 -1
- package/imaplib/imap-native.d.ts +2 -0
- package/imaplib/imap-native.js +34 -5
- package/package.json +2 -2
package/imaplib/ImapClient.js
CHANGED
|
@@ -93,7 +93,12 @@ export class ImapClient {
|
|
|
93
93
|
}
|
|
94
94
|
catch (err) {
|
|
95
95
|
console.error(`${new Date().toISOString()} Reconnection failed: ${imapError(err)}`);
|
|
96
|
-
//
|
|
96
|
+
// Stop reconnecting if server says too many connections — let it cool down
|
|
97
|
+
const errMsg = imapError(err);
|
|
98
|
+
if (errMsg.includes("max_userip_connections") || errMsg.includes("Too many simultaneous")) {
|
|
99
|
+
console.error(`${new Date().toISOString()} Connection limit reached — stopping reconnection`);
|
|
100
|
+
this.isDestroying = true; // Prevent further reconnect attempts
|
|
101
|
+
}
|
|
97
102
|
}
|
|
98
103
|
}
|
|
99
104
|
catch (err) {
|
package/imaplib/imap-native.d.ts
CHANGED
|
@@ -112,6 +112,8 @@ export declare class NativeImapClient {
|
|
|
112
112
|
appendMessage(mailbox: string, message: string | Uint8Array, flags?: string[]): Promise<number | null>;
|
|
113
113
|
startIdle(onNewMail: (count: number) => void): Promise<() => Promise<void>>;
|
|
114
114
|
getMessageCount(mailbox: string): Promise<number>;
|
|
115
|
+
/** Default timeout for IMAP commands (30s). Prevents hanging connections. */
|
|
116
|
+
private commandTimeout;
|
|
115
117
|
private sendCommand;
|
|
116
118
|
private waitForContinuation;
|
|
117
119
|
private waitForTagged;
|
package/imaplib/imap-native.js
CHANGED
|
@@ -31,10 +31,24 @@ export class NativeImapClient {
|
|
|
31
31
|
async connect() {
|
|
32
32
|
const useTls = this.config.port === 993;
|
|
33
33
|
this.transport.onData((data) => this.handleData(data));
|
|
34
|
-
this.transport.onClose(() => {
|
|
34
|
+
this.transport.onClose(() => {
|
|
35
|
+
this._connected = false;
|
|
36
|
+
// Reject any pending command so it doesn't hang forever
|
|
37
|
+
if (this.pendingCommand) {
|
|
38
|
+
const { reject } = this.pendingCommand;
|
|
39
|
+
this.pendingCommand = null;
|
|
40
|
+
reject(new Error("Connection closed"));
|
|
41
|
+
}
|
|
42
|
+
});
|
|
35
43
|
this.transport.onError((err) => {
|
|
36
44
|
if (this.verbose)
|
|
37
45
|
console.error(` [imap] Transport error: ${err.message}`);
|
|
46
|
+
// Reject any pending command on transport error
|
|
47
|
+
if (this.pendingCommand) {
|
|
48
|
+
const { reject } = this.pendingCommand;
|
|
49
|
+
this.pendingCommand = null;
|
|
50
|
+
reject(err);
|
|
51
|
+
}
|
|
38
52
|
});
|
|
39
53
|
await this.transport.connect(this.config.server, this.config.port, useTls, this.config.server);
|
|
40
54
|
// Read server greeting
|
|
@@ -133,10 +147,15 @@ export class NativeImapClient {
|
|
|
133
147
|
try {
|
|
134
148
|
if (this._connected) {
|
|
135
149
|
const tag = proto.nextTag();
|
|
136
|
-
|
|
150
|
+
// Timeout the LOGOUT command — don't let it hang forever
|
|
151
|
+
await Promise.race([
|
|
152
|
+
this.sendCommand(tag, proto.logoutCommand(tag)),
|
|
153
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("LOGOUT timeout")), 5000))
|
|
154
|
+
]);
|
|
137
155
|
}
|
|
138
156
|
}
|
|
139
|
-
catch { /* ignore */ }
|
|
157
|
+
catch { /* ignore — always close transport below */ }
|
|
158
|
+
this.pendingCommand = null; // clear any hanging promise
|
|
140
159
|
this.transport.close();
|
|
141
160
|
this._connected = false;
|
|
142
161
|
}
|
|
@@ -399,13 +418,23 @@ export class NativeImapClient {
|
|
|
399
418
|
return status.messages || 0;
|
|
400
419
|
}
|
|
401
420
|
// ── Low-level command handling ──
|
|
421
|
+
/** Default timeout for IMAP commands (30s). Prevents hanging connections. */
|
|
422
|
+
commandTimeout = 30000;
|
|
402
423
|
sendCommand(tag, command) {
|
|
403
424
|
return new Promise((resolve, reject) => {
|
|
404
425
|
if (this.verbose && !command.includes("LOGIN") && !command.includes("AUTHENTICATE")) {
|
|
405
426
|
console.log(` [imap] > ${command.trimEnd()}`);
|
|
406
427
|
}
|
|
407
|
-
|
|
408
|
-
|
|
428
|
+
const timer = setTimeout(() => {
|
|
429
|
+
this.pendingCommand = null;
|
|
430
|
+
reject(new Error(`IMAP command timeout (${this.commandTimeout / 1000}s): ${command.split("\r")[0].substring(0, 50)}`));
|
|
431
|
+
}, this.commandTimeout);
|
|
432
|
+
this.pendingCommand = {
|
|
433
|
+
tag, responses: [],
|
|
434
|
+
resolve: (responses) => { clearTimeout(timer); resolve(responses); },
|
|
435
|
+
reject: (err) => { clearTimeout(timer); reject(err); },
|
|
436
|
+
};
|
|
437
|
+
this.transport.write(command).catch((err) => { clearTimeout(timer); reject(err); });
|
|
409
438
|
});
|
|
410
439
|
}
|
|
411
440
|
waitForContinuation(tag) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/iflow",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.40",
|
|
4
4
|
"description": "IMAP client wrapper library",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.ts",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"author": "",
|
|
24
24
|
"license": "ISC",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@bobfrankston/oauthsupport": "^1.0.
|
|
26
|
+
"@bobfrankston/oauthsupport": "^1.0.20",
|
|
27
27
|
"imapflow": "^1.0.187"
|
|
28
28
|
},
|
|
29
29
|
"exports": {
|