@bobfrankston/mailx-imap 0.1.169 → 0.1.171
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/index.d.ts +1 -0
- package/index.js +34 -4
- package/package.json +7 -7
package/index.d.ts
CHANGED
|
@@ -237,6 +237,7 @@ export declare class ImapManager extends EventEmitter {
|
|
|
237
237
|
slow?: boolean;
|
|
238
238
|
lane?: string;
|
|
239
239
|
timeoutMs?: number;
|
|
240
|
+
priority?: boolean;
|
|
240
241
|
}): Promise<T>;
|
|
241
242
|
/** Drain the next queued task on EVERY lane for the account. Lanes run
|
|
242
243
|
* concurrently — each on its own connection — and the per-lane running
|
package/index.js
CHANGED
|
@@ -874,7 +874,19 @@ export class ImapManager extends EventEmitter {
|
|
|
874
874
|
reject(e);
|
|
875
875
|
}
|
|
876
876
|
};
|
|
877
|
-
|
|
877
|
+
// A task the user is waiting on goes to the FRONT of its lane.
|
|
878
|
+
// Lanes are strictly sequential, so without this a click-time body
|
|
879
|
+
// fetch sits behind whatever background work was queued first and
|
|
880
|
+
// can only start after all of it — Bob 2026-08-29: "if I look at a
|
|
881
|
+
// letter while sync things get confused … viewing the body should
|
|
882
|
+
// get priority instead of waiting for sync". It cannot preempt the
|
|
883
|
+
// command already in flight (an IMAP command cannot be cancelled
|
|
884
|
+
// without dropping the connection), but it no longer waits for the
|
|
885
|
+
// whole backlog.
|
|
886
|
+
if (opts.priority)
|
|
887
|
+
queue.tasks.unshift(task);
|
|
888
|
+
else
|
|
889
|
+
queue.tasks.push(task);
|
|
878
890
|
this.drainOpsQueue(accountId);
|
|
879
891
|
});
|
|
880
892
|
}
|
|
@@ -3762,6 +3774,13 @@ export class ImapManager extends EventEmitter {
|
|
|
3762
3774
|
// IMAP: fast lane on the ops queue. One try; if the socket is stale,
|
|
3763
3775
|
// withConnection's discard-on-error logic drops the client so the
|
|
3764
3776
|
// next attempt (caller-driven retry) gets a fresh one.
|
|
3777
|
+
//
|
|
3778
|
+
// An interactive fetch (`force`) jumps the lane queue and gets a
|
|
3779
|
+
// tighter cap: the reader is staring at a blank pane, and 90 s of
|
|
3780
|
+
// silence followed by a banner is the worst of both. 30 s is long
|
|
3781
|
+
// enough for a big body on a slow server and short enough that a
|
|
3782
|
+
// wedged lane becomes a RETRYABLE timeout while the reader is still
|
|
3783
|
+
// looking at the message.
|
|
3765
3784
|
try {
|
|
3766
3785
|
const raw = await this.withConnection(accountId, async (client) => {
|
|
3767
3786
|
const msg = await client.fetchMessageByUid(folder.path, uid, { source: true });
|
|
@@ -3770,7 +3789,7 @@ export class ImapManager extends EventEmitter {
|
|
|
3770
3789
|
if (!msg.source)
|
|
3771
3790
|
return null;
|
|
3772
3791
|
return Buffer.from(msg.source, "utf-8");
|
|
3773
|
-
});
|
|
3792
|
+
}, force ? { priority: true, timeoutMs: 30_000 } : {});
|
|
3774
3793
|
if (!raw)
|
|
3775
3794
|
return null;
|
|
3776
3795
|
const bodyPath = await this.bodyStore.putMessage(accountId, folderId, uid, raw);
|
|
@@ -3785,8 +3804,19 @@ export class ImapManager extends EventEmitter {
|
|
|
3785
3804
|
if (e?.isNotFound)
|
|
3786
3805
|
throw e;
|
|
3787
3806
|
console.error(` Body fetch error (${accountId}/${uid}): ${e?.message || e}`);
|
|
3788
|
-
//
|
|
3789
|
-
//
|
|
3807
|
+
// A lane timeout / dropped socket says nothing about whether this
|
|
3808
|
+
// message HAS a body. Swallowing it into `null` told the caller
|
|
3809
|
+
// "the server has no body for this" — the reconciler classifies
|
|
3810
|
+
// that as non-transient, skips its retry budget, and banners.
|
|
3811
|
+
// That is the "eventually reports an error rather than retrying"
|
|
3812
|
+
// Bob hit (2026-08-29). Rethrow instead, so the reconciler's
|
|
3813
|
+
// transient test sees the word "timeout" and retries with backoff.
|
|
3814
|
+
const transient = /timeout|discarding client|connection|not connected|ECONN|ETIMEDOUT|ENOTFOUND|socket/i
|
|
3815
|
+
.test(String(e?.message || ""));
|
|
3816
|
+
if (transient)
|
|
3817
|
+
throw e;
|
|
3818
|
+
// A genuine "no body here" — arm the backoff so prefetch and the
|
|
3819
|
+
// next view don't re-attempt a fetch the server won't satisfy.
|
|
3790
3820
|
try {
|
|
3791
3821
|
this.markPrefetchEmpty(accountId, folderId, uid);
|
|
3792
3822
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx-imap",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.171",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
},
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@bobfrankston/mailx-types": "^0.1.
|
|
13
|
-
"@bobfrankston/mailx-settings": "^0.1.
|
|
14
|
-
"@bobfrankston/mailx-store": "^0.1.
|
|
12
|
+
"@bobfrankston/mailx-types": "^0.1.77",
|
|
13
|
+
"@bobfrankston/mailx-settings": "^0.1.73",
|
|
14
|
+
"@bobfrankston/mailx-store": "^0.1.109",
|
|
15
15
|
"@bobfrankston/iflow-direct": "^0.1.68",
|
|
16
16
|
"@bobfrankston/tcp-transport": "^0.1.8",
|
|
17
17
|
"@bobfrankston/smtp-direct": "^0.1.9",
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
},
|
|
38
38
|
".transformedSnapshot": {
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@bobfrankston/mailx-types": "^0.1.
|
|
41
|
-
"@bobfrankston/mailx-settings": "^0.1.
|
|
42
|
-
"@bobfrankston/mailx-store": "^0.1.
|
|
40
|
+
"@bobfrankston/mailx-types": "^0.1.77",
|
|
41
|
+
"@bobfrankston/mailx-settings": "^0.1.73",
|
|
42
|
+
"@bobfrankston/mailx-store": "^0.1.109",
|
|
43
43
|
"@bobfrankston/iflow-direct": "^0.1.68",
|
|
44
44
|
"@bobfrankston/tcp-transport": "^0.1.8",
|
|
45
45
|
"@bobfrankston/smtp-direct": "^0.1.9",
|