@bobfrankston/iflow-direct 0.1.54 → 0.1.56
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/fetched-message.d.ts +1 -0
- package/fetched-message.js +1 -0
- package/imap-native.d.ts +1 -0
- package/imap-native.js +33 -2
- package/imap-protocol.js +10 -2
- package/package.json +1 -1
package/fetched-message.d.ts
CHANGED
package/fetched-message.js
CHANGED
package/imap-native.d.ts
CHANGED
package/imap-native.js
CHANGED
|
@@ -553,10 +553,25 @@ export class NativeImapClient {
|
|
|
553
553
|
return;
|
|
554
554
|
if (this.selectedMailbox !== folderPath)
|
|
555
555
|
await this.select(folderPath);
|
|
556
|
-
|
|
557
|
-
|
|
556
|
+
// headers default (was `headers: false`): the single-message body fetch
|
|
557
|
+
// path (fetchMessage → fetchMessagesStream, headers DEFAULT) extracts
|
|
558
|
+
// BODY[] correctly, but this batch path passed `headers: false` and
|
|
559
|
+
// returned 0 bodies 100% of the time — so prefetch never cached anything
|
|
560
|
+
// and bodies only landed when the user opened a message (Bob 2026-06-17
|
|
561
|
+
// "predownloading still broken"). The only difference between the two
|
|
562
|
+
// paths was this flag; matching the working path. Diagnostic counters
|
|
563
|
+
// confirm extraction so a regression is visible in the log.
|
|
564
|
+
let got = 0, sawResp = 0;
|
|
565
|
+
await this.fetchMessagesStream(uids.join(","), { source: true }, (msg) => {
|
|
566
|
+
sawResp++;
|
|
567
|
+
if (msg.uid && msg.source) {
|
|
568
|
+
got++;
|
|
558
569
|
onBody(msg.uid, msg.source);
|
|
570
|
+
}
|
|
559
571
|
});
|
|
572
|
+
if (got === 0 && uids.length > 0) {
|
|
573
|
+
console.error(` [imap] fetchBodiesBatch ${folderPath}: 0/${uids.length} bodies (saw ${sawResp} FETCH responses) — body literal not extracted`);
|
|
574
|
+
}
|
|
560
575
|
}
|
|
561
576
|
/** Fetch messages since a UID */
|
|
562
577
|
async fetchSinceUid(sinceUid, options = {}, onChunk) {
|
|
@@ -691,6 +706,17 @@ export class NativeImapClient {
|
|
|
691
706
|
return proto.parseSearchResponse(r.text);
|
|
692
707
|
}
|
|
693
708
|
}
|
|
709
|
+
// No untagged * SEARCH seen. If the tagged status is anything but OK,
|
|
710
|
+
// the search FAILED — throw so callers can tell "failed" from "no
|
|
711
|
+
// matches". Silently returning [] here let a failed HEADER search read
|
|
712
|
+
// as "message missing from server", and mailx's sent-sweep then
|
|
713
|
+
// re-APPENDed a copy every 5-minute tick — 3,704 duplicates of one
|
|
714
|
+
// message over ~13 days (2026-07-02). Empty-but-OK still returns []
|
|
715
|
+
// (some servers omit the * SEARCH line when there are zero hits).
|
|
716
|
+
const tagged = responses.find(r => r.tag === tag);
|
|
717
|
+
if (!tagged || tagged.type !== "OK") {
|
|
718
|
+
throw new Error(`SEARCH failed: ${tagged ? `${tagged.type} ${tagged.text || ""}` : "no tagged response"}`);
|
|
719
|
+
}
|
|
694
720
|
return [];
|
|
695
721
|
}
|
|
696
722
|
/** Set flags on a message */
|
|
@@ -1498,6 +1524,11 @@ export class NativeImapClient {
|
|
|
1498
1524
|
if (envMatch) {
|
|
1499
1525
|
const env = proto.parseEnvelope(envMatch[1]);
|
|
1500
1526
|
msg.date = msg.date || env.date;
|
|
1527
|
+
// Expose the ENVELOPE Date: header (the SENT time) separately
|
|
1528
|
+
// from msg.date (INTERNALDATE = server-received time). Callers
|
|
1529
|
+
// that want messages to cluster by when they were sent — not by
|
|
1530
|
+
// per-account delivery time — use sentDate. Additive/optional.
|
|
1531
|
+
msg.sentDate = env.date || undefined;
|
|
1501
1532
|
msg.subject = env.subject;
|
|
1502
1533
|
msg.messageId = env.messageId;
|
|
1503
1534
|
msg.from = env.from;
|
package/imap-protocol.js
CHANGED
|
@@ -302,8 +302,16 @@ export function parseEnvelope(envStr) {
|
|
|
302
302
|
result.to = parseAddressList(tokens[5]);
|
|
303
303
|
result.cc = parseAddressList(tokens[6]);
|
|
304
304
|
result.bcc = parseAddressList(tokens[7]);
|
|
305
|
-
|
|
306
|
-
|
|
305
|
+
// Trim fold whitespace. Servers hand back the ENVELOPE msg-id /
|
|
306
|
+
// in-reply-to with the original header's folding intact when the
|
|
307
|
+
// Message-ID sat on a continuation line ("Message-ID:\r\n\t<...>")
|
|
308
|
+
// — the value arrives as "\t<...>". Stored untrimmed, that tab
|
|
309
|
+
// poisoned every later HEADER search ("missing on server") and
|
|
310
|
+
// fueled mailx's sent-sweep re-APPEND loop: 3,707 duplicates of
|
|
311
|
+
// one Outlook-composed reply (2026-07-02). Trim here so no
|
|
312
|
+
// consumer ever sees the fold whitespace.
|
|
313
|
+
result.inReplyTo = unquote(tokens[8]).trim();
|
|
314
|
+
result.messageId = unquote(tokens[9]).trim();
|
|
307
315
|
}
|
|
308
316
|
}
|
|
309
317
|
catch {
|