@bobfrankston/rmfmail 1.2.217 → 1.2.219
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/package.json +5 -5
- package/packages/mailx-imap/index.d.ts +8 -0
- package/packages/mailx-imap/index.d.ts.map +1 -1
- package/packages/mailx-imap/index.js +36 -2
- package/packages/mailx-imap/index.js.map +1 -1
- package/packages/mailx-imap/index.ts +34 -3
- package/packages/mailx-imap/package-lock.json +2 -2
- package/packages/mailx-imap/package.json +1 -1
- /package/packages/mailx-imap/{node_modules.npmglobalize-stash-36516 → node_modules.npmglobalize-stash-24032}/.package-lock.json +0 -0
|
@@ -265,6 +265,14 @@ export class ImapManager extends EventEmitter {
|
|
|
265
265
|
// (folder syncs holding all permits, timing out at 360s), leaving new mail
|
|
266
266
|
// on 5-min polling for minutes at a time (Bob 2026-05-28).
|
|
267
267
|
private watcherClients: Map<string, any> = new Map();
|
|
268
|
+
/** Accounts whose IDLE watcher is mid-establish. `watchers` only gains an
|
|
269
|
+
* entry AFTER connect+login+SELECT+IDLE completes (seconds), so two
|
|
270
|
+
* overlapping startWatching() sweeps both see "no watcher" and both open
|
|
271
|
+
* a socket — a duplicate IDLE connection per account. Bob 2026-08-05:
|
|
272
|
+
* boot's startWatching was still in flight when the 60s deadman fired,
|
|
273
|
+
* giving two watchers each for bobma/gmail and pushing aol into
|
|
274
|
+
* "[LIMIT] LOGIN Rate limit hit". This set closes that window. */
|
|
275
|
+
private watchStarting: Set<string> = new Set();
|
|
268
276
|
private fetchClients: Map<string, any> = new Map();
|
|
269
277
|
/** The Store is the architectural nexus — owner of MailxDB +
|
|
270
278
|
* FileMessageStore + the event bus. This package (mailx-imap) is a
|
|
@@ -3098,14 +3106,25 @@ export class ImapManager extends EventEmitter {
|
|
|
3098
3106
|
// accounts with healthy IDLE are skipped). One log line per
|
|
3099
3107
|
// detected gap so a chronically failing account is obvious in
|
|
3100
3108
|
// the log.
|
|
3109
|
+
//
|
|
3110
|
+
// startWatching() sweeps EVERY account, so it must be called at most
|
|
3111
|
+
// once per tick: the old per-account call meant N missing accounts →
|
|
3112
|
+
// N concurrent sweeps, and since `watchers` is only populated after
|
|
3113
|
+
// the connect completes, every sweep opened its own socket for every
|
|
3114
|
+
// account (Bob 2026-08-05 boot: two IDLE watchers each for bobma and
|
|
3115
|
+
// gmail, aol refused with "[LIMIT] LOGIN Rate limit hit").
|
|
3101
3116
|
const deadmanInterval = setInterval(() => {
|
|
3117
|
+
const missing: string[] = [];
|
|
3102
3118
|
for (const [accountId] of this.configs) {
|
|
3103
3119
|
if (this.isApiAccount(accountId)) continue; // Gmail/Outlook = API, no IDLE
|
|
3104
3120
|
if (this.watchers.has(accountId)) continue;
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
console.error(` [idle-deadman] ${accountId} restart failed: ${e?.message || e}`));
|
|
3121
|
+
if (this.watchStarting.has(accountId)) continue; // first attempt still connecting
|
|
3122
|
+
missing.push(accountId);
|
|
3108
3123
|
}
|
|
3124
|
+
if (missing.length === 0) return;
|
|
3125
|
+
console.log(` [idle-deadman] no IDLE watcher for ${missing.join(", ")} — attempting restart`);
|
|
3126
|
+
this.startWatching().catch(e =>
|
|
3127
|
+
console.error(` [idle-deadman] restart failed: ${e?.message || e}`));
|
|
3109
3128
|
}, 60_000);
|
|
3110
3129
|
this.syncIntervals.set("idle-deadman", deadmanInterval);
|
|
3111
3130
|
|
|
@@ -3204,6 +3223,16 @@ export class ImapManager extends EventEmitter {
|
|
|
3204
3223
|
this.watchers.delete(accountId);
|
|
3205
3224
|
this.watcherClients.delete(accountId);
|
|
3206
3225
|
}
|
|
3226
|
+
// Another sweep is already connecting this account. `watchers`
|
|
3227
|
+
// stays empty for the whole connect+login+SELECT+IDLE round trip,
|
|
3228
|
+
// so without this the second sweep opens a duplicate IDLE socket
|
|
3229
|
+
// (and on connection-limited servers the duplicate is what gets
|
|
3230
|
+
// refused). See the watchStarting field comment.
|
|
3231
|
+
if (this.watchStarting.has(accountId)) {
|
|
3232
|
+
console.log(` [idle] ${accountId}: watcher already being established — skipping duplicate`);
|
|
3233
|
+
continue;
|
|
3234
|
+
}
|
|
3235
|
+
this.watchStarting.add(accountId);
|
|
3207
3236
|
try {
|
|
3208
3237
|
// IDLE keeps its own dedicated socket — once the connection
|
|
3209
3238
|
// is parked in IDLE, it's unusable for any other command, so
|
|
@@ -3301,6 +3330,8 @@ export class ImapManager extends EventEmitter {
|
|
|
3301
3330
|
? parts.join(" ")
|
|
3302
3331
|
: `<no message> ${typeof e} ${e?.constructor?.name || ""}`.trim();
|
|
3303
3332
|
console.error(` [idle] Failed to watch ${accountId}: ${desc}`);
|
|
3333
|
+
} finally {
|
|
3334
|
+
this.watchStarting.delete(accountId);
|
|
3304
3335
|
}
|
|
3305
3336
|
}
|
|
3306
3337
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx-imap",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.142",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bobfrankston/mailx-imap",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.142",
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@bobfrankston/iflow-direct": "^0.1.27",
|