@bobfrankston/iflow-direct 0.1.53 → 0.1.55

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.
Files changed (2) hide show
  1. package/imap-native.js +34 -2
  2. package/package.json +3 -3
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
- await this.fetchMessagesStream(uids.join(","), { source: true, headers: false }, (msg) => {
557
- if (msg.uid && msg.source)
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) {
@@ -1244,6 +1259,23 @@ export class NativeImapClient {
1244
1259
  // decode happens once per literal, not once per chunk that
1245
1260
  // arrives during the literal's transit.
1246
1261
  let literal = this.utf8Decoder.decode(literalBytes);
1262
+ // DESYNC CAPTURE (Bob 2026-06-16, overnight debug). A BODY[]
1263
+ // literal whose content is itself an IMAP command tag means the
1264
+ // byte-buffer parser misaligned: the declared {N} didn't match
1265
+ // reality and we swallowed a later command's bytes. Dump the
1266
+ // declared size + the misaligned content + the raw bytes that
1267
+ // PRECEDED the literal, so the mechanism (how the {N} boundary
1268
+ // slipped) is finally visible. Cheap; only fires on the desync.
1269
+ if (this.pendingCommand.currentLiteralKey?.startsWith("BODY")
1270
+ && /^[A-Za-z]+\d+\s+(SELECT|FETCH|UID|LOGIN|STATUS|EXAMINE|LIST|IDLE|NOOP|STORE|SEARCH|CAPABILITY|CLOSE|LOGOUT|APPEND|COPY|MOVE|EXPUNGE|CREATE|DELETE|RENAME|SUBSCRIBE|ENABLE|STARTTLS)\b/i.test(literal.slice(0, 80).replace(/^[\s*]+/, ""))) {
1271
+ const litStart = this.bufferOffset - neededBytes;
1272
+ const ctxStart = Math.max(0, litStart - 256);
1273
+ const ctx = this.buffer.subarray(ctxStart, litStart);
1274
+ const ascii = Array.from(ctx).map(b => (b >= 32 && b < 127) ? String.fromCharCode(b) : (b === 13 ? "\\r" : b === 10 ? "\\n" : ".")).join("");
1275
+ console.error(` [imap] DESYNC CAPTURE key=${this.pendingCommand.currentLiteralKey} declaredLiteral=${neededBytes}B bufferOffset=${this.bufferOffset} bufferLength=${this.bufferLength} litStart=${litStart}`);
1276
+ console.error(` [imap] DESYNC literal[0..200]=${JSON.stringify(literal.slice(0, 200))}`);
1277
+ console.error(` [imap] DESYNC preceding[${ctx.length}B]=${JSON.stringify(ascii)}`);
1278
+ }
1247
1279
  // For non-BODY literals (e.g. display names in ENVELOPE), wrap in quotes
1248
1280
  // so tokenizeParenList treats them as a single token
1249
1281
  if (!this.pendingCommand.currentLiteralKey) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/iflow-direct",
3
- "version": "0.1.53",
3
+ "version": "0.1.55",
4
4
  "description": "Direct IMAP client — transport-agnostic, no Node.js dependencies, browser-ready",
5
5
  "main": "index.js",
6
6
  "types": "index.ts",
@@ -19,7 +19,7 @@
19
19
  "author": "Bob Frankston",
20
20
  "license": "ISC",
21
21
  "dependencies": {
22
- "@bobfrankston/tcp-transport": "^0.1.6"
22
+ "@bobfrankston/tcp-transport": "^0.1.7"
23
23
  },
24
24
  "exports": {
25
25
  ".": {
@@ -50,7 +50,7 @@
50
50
  },
51
51
  ".transformedSnapshot": {
52
52
  "dependencies": {
53
- "@bobfrankston/tcp-transport": "^0.1.6"
53
+ "@bobfrankston/tcp-transport": "^0.1.7"
54
54
  }
55
55
  }
56
56
  }