@bobfrankston/mailx-imap 0.1.55 → 0.1.57

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/index.js +43 -4
  2. package/package.json +3 -3
package/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { createAutoImapConfig, CompatImapClient } from "@bobfrankston/iflow-direct";
7
7
  import { authenticateOAuth } from "@bobfrankston/oauthsupport";
8
- import { parseSerial, storeBus } from "@bobfrankston/mailx-store";
8
+ import { parseSerial, fixCharsetDeclString, storeBus } from "@bobfrankston/mailx-store";
9
9
  import { loadSettings, getConfigDir, getHistoryDays, getPrefetch } from "@bobfrankston/mailx-settings";
10
10
  import { EventEmitter } from "node:events";
11
11
  import * as fs from "node:fs";
@@ -106,7 +106,11 @@ function decodeEntities(text) {
106
106
  /** Extract a plain-text preview from message source */
107
107
  async function extractPreview(source) {
108
108
  try {
109
- const parsed = await parseSerial(source, "background");
109
+ // Normalize iso-8859-1 windows-1252 in the part charset declaration
110
+ // before parsing, so the preview decodes 0x80-0x9F (smart quotes,
111
+ // em-dash) correctly — same correction the viewer path gets via
112
+ // sniffAndFixCharset (Bob 2026-05-22).
113
+ const parsed = await parseSerial(fixCharsetDeclString(source), "background");
110
114
  const bodyText = parsed.text || "";
111
115
  const bodyHtml = parsed.html || "";
112
116
  // Use text part; fall back to stripping HTML tags if text is empty
@@ -3191,6 +3195,15 @@ export class ImapManager extends EventEmitter {
3191
3195
  }
3192
3196
  continue;
3193
3197
  }
3198
+ // Success — clear the in-flight delete/move
3199
+ // suppression (see the IMAP branch below for the
3200
+ // full rationale). After a move the local row lives
3201
+ // at the TARGET folder, so the (uid, folder) lookup
3202
+ // uses targetFolderId.
3203
+ if (action.action === "move") {
3204
+ this.db.clearTombstoneForUid(accountId, action.uid, action.targetFolderId);
3205
+ this.db.clearMessagePendingDelete(accountId, action.uid, action.targetFolderId);
3206
+ }
3194
3207
  this.db.completeSyncAction(action.id);
3195
3208
  }
3196
3209
  catch (e) {
@@ -3200,7 +3213,11 @@ export class ImapManager extends EventEmitter {
3200
3213
  // Terminal failure on delete/move → clear tombstone
3201
3214
  // so the row reappears on next sync (server still
3202
3215
  // has it). Same rationale as the IMAP branch below.
3203
- if (action.action === "delete" || action.action === "move") {
3216
+ if (action.action === "move") {
3217
+ this.db.clearTombstoneForUid(accountId, action.uid, action.targetFolderId);
3218
+ this.db.clearMessagePendingDelete(accountId, action.uid, action.targetFolderId);
3219
+ }
3220
+ else if (action.action === "delete") {
3204
3221
  this.db.clearTombstoneForUid(accountId, action.uid, action.folderId);
3205
3222
  }
3206
3223
  this.db.completeSyncAction(action.id);
@@ -3276,6 +3293,22 @@ export class ImapManager extends EventEmitter {
3276
3293
  break;
3277
3294
  }
3278
3295
  }
3296
+ // Success: the local action reached the server. Lift the
3297
+ // in-flight delete/move suppression so the destination
3298
+ // folder syncs the moved message under its real
3299
+ // post-move UID. hasTombstone's documented contract is
3300
+ // "cleared on successful action complete OR permanent
3301
+ // failure" — only the failure half had been wired, so a
3302
+ // successful move left a Message-ID tombstone alive for
3303
+ // 30 days, blocking the Trash folder from ever storing
3304
+ // the message (and resurrecting it once the tombstone
3305
+ // finally aged out). After a move the local row lives at
3306
+ // the TARGET folder, so the (uid, folder) lookup uses
3307
+ // targetFolderId.
3308
+ if (action.action === "move") {
3309
+ this.db.clearTombstoneForUid(accountId, action.uid, action.targetFolderId);
3310
+ this.db.clearMessagePendingDelete(accountId, action.uid, action.targetFolderId);
3311
+ }
3279
3312
  this.db.completeSyncAction(action.id);
3280
3313
  }
3281
3314
  catch (e) {
@@ -3292,7 +3325,13 @@ export class ImapManager extends EventEmitter {
3292
3325
  // reflecting "your action didn't take, here it is
3293
3326
  // again." Applies to delete + move; flags/append
3294
3327
  // never tombstone.
3295
- if (action.action === "delete" || action.action === "move") {
3328
+ if (action.action === "move") {
3329
+ // Local row is at the TARGET folder (the local
3330
+ // move committed; only the server move failed).
3331
+ this.db.clearTombstoneForUid(accountId, action.uid, action.targetFolderId);
3332
+ this.db.clearMessagePendingDelete(accountId, action.uid, action.targetFolderId);
3333
+ }
3334
+ else if (action.action === "delete") {
3296
3335
  this.db.clearTombstoneForUid(accountId, action.uid, action.folderId);
3297
3336
  }
3298
3337
  this.db.completeSyncAction(action.id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-imap",
3
- "version": "0.1.55",
3
+ "version": "0.1.57",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -11,7 +11,7 @@
11
11
  "dependencies": {
12
12
  "@bobfrankston/mailx-types": "^0.1.18",
13
13
  "@bobfrankston/mailx-settings": "^0.1.22",
14
- "@bobfrankston/mailx-store": "^0.1.32",
14
+ "@bobfrankston/mailx-store": "^0.1.34",
15
15
  "@bobfrankston/iflow-direct": "^0.1.50",
16
16
  "@bobfrankston/tcp-transport": "^0.1.6",
17
17
  "@bobfrankston/smtp-direct": "^0.1.8",
@@ -39,7 +39,7 @@
39
39
  "dependencies": {
40
40
  "@bobfrankston/mailx-types": "^0.1.18",
41
41
  "@bobfrankston/mailx-settings": "^0.1.22",
42
- "@bobfrankston/mailx-store": "^0.1.32",
42
+ "@bobfrankston/mailx-store": "^0.1.34",
43
43
  "@bobfrankston/iflow-direct": "^0.1.50",
44
44
  "@bobfrankston/tcp-transport": "^0.1.6",
45
45
  "@bobfrankston/smtp-direct": "^0.1.8",