@bobfrankston/mailx-imap 0.1.24 → 0.1.25

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 (3) hide show
  1. package/index.d.ts +1 -26
  2. package/index.js +7 -62
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import type { TransportFactory } from "@bobfrankston/tcp-transport";
7
7
  import { MailxDB, FileMessageStore } from "@bobfrankston/mailx-store";
8
- import type { AccountConfig, MessageEnvelope, EmailAddress, Folder } from "@bobfrankston/mailx-types";
8
+ import type { AccountConfig, MessageEnvelope, Folder } from "@bobfrankston/mailx-types";
9
9
  import { EventEmitter } from "node:events";
10
10
  /** Events emitted by the IMAP manager */
11
11
  export interface ImapManagerEvents {
@@ -336,31 +336,6 @@ export declare class ImapManager extends EventEmitter {
336
336
  processSyncActions(accountId: string): Promise<void>;
337
337
  /** Find a folder by specialUse, case-insensitive */
338
338
  private findFolder;
339
- /** Optimistic local-first Sent insert: write a row into the local DB's
340
- * Sent folder the moment the user hits Send, so the list reflects it
341
- * immediately instead of waiting for SMTP + IMAP-APPEND + syncFolder
342
- * (five server round-trips against a Dovecot that caps at 20 conns).
343
- *
344
- * Uses a synthetic negative UID so it can't collide with a real APPENDUID
345
- * (which is always positive). When the real sync eventually picks the
346
- * message up in Sent with the server's UID, `db.upsertMessage` spots
347
- * the Message-ID match and rebinds the existing row's UID — no duplicate.
348
- * Negative UID also makes the row render pink (getMessages flags uid<0
349
- * as pending) so the user sees it's not-yet-reconciled.
350
- *
351
- * Best-effort — any failure path (no Sent folder yet, parse error, store
352
- * write error) is logged and swallowed; the send itself is unaffected. */
353
- insertOptimisticSentRow(accountId: string, envelope: {
354
- messageId: string;
355
- inReplyTo: string;
356
- references: string[];
357
- subject: string;
358
- from: EmailAddress;
359
- to: EmailAddress[];
360
- cc: EmailAddress[];
361
- bcc: EmailAddress[];
362
- date: number;
363
- }, rawMessage: string): Promise<void>;
364
339
  /** Copy sent message to the Sent folder via IMAP APPEND */
365
340
  copyToSent(accountId: string, rawMessage: string | Buffer): Promise<void>;
366
341
  /** Save a draft to the Drafts folder via IMAP APPEND.
package/index.js CHANGED
@@ -874,7 +874,9 @@ export class ImapManager extends EventEmitter {
874
874
  if (!folder)
875
875
  throw new Error(`Folder ${folderId} not found`);
876
876
  this.emit("syncProgress", accountId, `sync:${folder.path}`, 0);
877
- // Get the highest UID we already have for this folder
877
+ // Get the highest UID we already have for this folder. IMAP UIDs are
878
+ // monotonically increasing within a UIDVALIDITY (RFC 3501); a
879
+ // high-water mark is the right anchor for incremental fetch.
878
880
  const highestUid = this.db.getHighestUid(accountId, folderId);
879
881
  console.log(` [sync] ${accountId}/${folder.path}: highestUid=${highestUid}, fetching...`);
880
882
  let messages;
@@ -2616,67 +2618,10 @@ export class ImapManager extends EventEmitter {
2616
2618
  return folders.find(f => f.specialUse === specialUse ||
2617
2619
  f.path.toLowerCase() === specialUse.toLowerCase()) || null;
2618
2620
  }
2619
- /** Optimistic local-first Sent insert: write a row into the local DB's
2620
- * Sent folder the moment the user hits Send, so the list reflects it
2621
- * immediately instead of waiting for SMTP + IMAP-APPEND + syncFolder
2622
- * (five server round-trips against a Dovecot that caps at 20 conns).
2623
- *
2624
- * Uses a synthetic negative UID so it can't collide with a real APPENDUID
2625
- * (which is always positive). When the real sync eventually picks the
2626
- * message up in Sent with the server's UID, `db.upsertMessage` spots
2627
- * the Message-ID match and rebinds the existing row's UID — no duplicate.
2628
- * Negative UID also makes the row render pink (getMessages flags uid<0
2629
- * as pending) so the user sees it's not-yet-reconciled.
2630
- *
2631
- * Best-effort — any failure path (no Sent folder yet, parse error, store
2632
- * write error) is logged and swallowed; the send itself is unaffected. */
2633
- async insertOptimisticSentRow(accountId, envelope, rawMessage) {
2634
- try {
2635
- const sent = this.findFolder(accountId, "sent");
2636
- if (!sent) {
2637
- console.log(` [sent-local] no Sent folder for ${accountId}; skipping optimistic row`);
2638
- return;
2639
- }
2640
- // Synthetic UID — negative ms timestamp is monotonic + won't
2641
- // collide with server UIDs. When the real APPENDUID returns via
2642
- // sync, upsertMessage's Message-ID rebind swaps this for the
2643
- // real positive value.
2644
- const synthUid = -Date.now();
2645
- const bodyPath = await this.bodyStore.putMessage(accountId, sent.id, synthUid, Buffer.from(rawMessage, "utf-8"));
2646
- const parsed = await extractPreview(rawMessage);
2647
- this.db.upsertMessage({
2648
- accountId,
2649
- folderId: sent.id,
2650
- uid: synthUid,
2651
- messageId: envelope.messageId,
2652
- inReplyTo: envelope.inReplyTo,
2653
- references: envelope.references,
2654
- date: envelope.date,
2655
- subject: envelope.subject,
2656
- from: envelope.from,
2657
- to: envelope.to,
2658
- cc: envelope.cc,
2659
- flags: ["\\Seen"],
2660
- size: rawMessage.length,
2661
- hasAttachments: parsed.hasAttachments,
2662
- preview: parsed.preview,
2663
- bodyPath,
2664
- });
2665
- // Folder-tree badge refresh + message-list reload if the user
2666
- // is currently on Sent — same event shape the sync path emits.
2667
- // (Was sending {accountId,folderId} as a single arg, which the
2668
- // IPC forwarder + UI handler decoded as garbage — the optimistic
2669
- // row landed in the DB but never appeared in the list.)
2670
- this.db.recalcFolderCounts(sent.id);
2671
- this.emit("folderCountsChanged", accountId, {});
2672
- console.log(` [sent-local] wrote optimistic row in Sent (uid=${synthUid}) for ${accountId}: ${envelope.subject}`);
2673
- }
2674
- catch (e) {
2675
- // Non-fatal — send continues, Sent folder just won't show the
2676
- // row until the real APPEND-then-sync cycle completes.
2677
- console.error(` [sent-local] optimistic insert failed: ${e?.message || e}`);
2678
- }
2679
- }
2621
+ // insertOptimisticSentRow removed the synthetic-negative-UID hack
2622
+ // wedged Sent's high-water-mark sync (a synthetic value polluted MAX(uid)
2623
+ // and made every Sent fetch take a stale code path). Sent is now what
2624
+ // the server has, period. Pending sends live in the Outbox view.
2680
2625
  /** Copy sent message to the Sent folder via IMAP APPEND */
2681
2626
  async copyToSent(accountId, rawMessage) {
2682
2627
  const sent = this.findFolder(accountId, "sent");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-imap",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",