@bobfrankston/mailx-sync 0.1.8 → 0.1.9

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/gmail.d.ts +12 -0
  2. package/gmail.js +55 -0
  3. package/package.json +3 -3
package/gmail.d.ts CHANGED
@@ -51,6 +51,18 @@ export declare class GmailApiProvider implements MailProvider {
51
51
  * remove so the end state matches regardless of what was there before,
52
52
  * which makes the call idempotent and safe to retry. */
53
53
  setFlags(folder: string, uid: number, flags: string[]): Promise<void>;
54
+ /** Move a message to the trash label. Gmail treats trash as a label, not
55
+ * as a destination folder — `POST /messages/{id}/trash` is the native
56
+ * path (equivalent to setting TRASH and removing INBOX in one op).
57
+ * Used by mailx's delete/trash path. */
58
+ trashMessage(folder: string, uid: number): Promise<void>;
59
+ /** Move between "folders" == swap one label for another via modifyLabels.
60
+ * System labels (INBOX/SENT/TRASH/SPAM) are translated from the folder
61
+ * path; user labels use the folder path verbatim as the label id. */
62
+ moveMessage(fromFolder: string, uid: number, toFolder: string): Promise<void>;
63
+ /** Folder path → Gmail label id. System folders map to uppercase label
64
+ * constants; anything else is treated as a user label (identical name). */
65
+ private folderPathToLabelId;
54
66
  getUids(folder: string): Promise<number[]>;
55
67
  close(): Promise<void>;
56
68
  /** Map folder path to Gmail label query term */
package/gmail.js CHANGED
@@ -439,6 +439,61 @@ export class GmailApiProvider {
439
439
  body: JSON.stringify({ addLabelIds, removeLabelIds }),
440
440
  });
441
441
  }
442
+ /** Move a message to the trash label. Gmail treats trash as a label, not
443
+ * as a destination folder — `POST /messages/{id}/trash` is the native
444
+ * path (equivalent to setting TRASH and removing INBOX in one op).
445
+ * Used by mailx's delete/trash path. */
446
+ async trashMessage(folder, uid) {
447
+ const query = `in:${this.folderToLabel(folder)}`;
448
+ const ids = await this.listMessageIds(query, 1000);
449
+ const id = ids.find(id => idToUid(id) === uid);
450
+ if (!id)
451
+ throw new Error(`Gmail trashMessage: UID ${uid} not found in ${folder}`);
452
+ await this.fetch(`/messages/${id}/trash`, { method: "POST" });
453
+ }
454
+ /** Move between "folders" == swap one label for another via modifyLabels.
455
+ * System labels (INBOX/SENT/TRASH/SPAM) are translated from the folder
456
+ * path; user labels use the folder path verbatim as the label id. */
457
+ async moveMessage(fromFolder, uid, toFolder) {
458
+ const query = `in:${this.folderToLabel(fromFolder)}`;
459
+ const ids = await this.listMessageIds(query, 1000);
460
+ const id = ids.find(id => idToUid(id) === uid);
461
+ if (!id)
462
+ throw new Error(`Gmail moveMessage: UID ${uid} not found in ${fromFolder}`);
463
+ // Map the folder path to the label id. System labels are uppercased
464
+ // aliases; user labels are passed through as-is (Gmail's label ids
465
+ // for user-created labels match the visible label name).
466
+ const toLabel = this.folderPathToLabelId(toFolder);
467
+ const fromLabel = this.folderPathToLabelId(fromFolder);
468
+ const addLabelIds = [];
469
+ const removeLabelIds = [];
470
+ if (toLabel)
471
+ addLabelIds.push(toLabel);
472
+ if (fromLabel && fromLabel !== toLabel)
473
+ removeLabelIds.push(fromLabel);
474
+ await this.fetch(`/messages/${id}/modify`, {
475
+ method: "POST",
476
+ body: JSON.stringify({ addLabelIds, removeLabelIds }),
477
+ });
478
+ }
479
+ /** Folder path → Gmail label id. System folders map to uppercase label
480
+ * constants; anything else is treated as a user label (identical name). */
481
+ folderPathToLabelId(path) {
482
+ const lower = path.toLowerCase();
483
+ if (lower === "inbox")
484
+ return "INBOX";
485
+ if (lower === "sent" || lower === "[gmail]/sent mail")
486
+ return "SENT";
487
+ if (lower === "drafts" || lower === "[gmail]/drafts")
488
+ return "DRAFT";
489
+ if (lower === "trash" || lower === "[gmail]/trash")
490
+ return "TRASH";
491
+ if (lower === "spam" || lower === "junk email" || lower === "[gmail]/spam")
492
+ return "SPAM";
493
+ if (lower === "archive" || lower === "[gmail]/all mail")
494
+ return ""; // no-op — archive is absence-of-INBOX
495
+ return path; // user label — name-as-id
496
+ }
442
497
  async getUids(folder) {
443
498
  const query = `in:${this.folderToLabel(folder)}`;
444
499
  const ids = await this.listMessageIds(query, 10000);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-sync",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Platform-agnostic mail provider implementations + sync orchestration. Single source of truth for Gmail/IMAP/Outlook protocol code, consumed by both desktop (Node) and Android (WebView) — eliminates the parallel mailx-imap/mailx-store-web Gmail providers that drifted in practice.",
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/iflow-direct": "^0.1.23",
22
+ "@bobfrankston/iflow-direct": "^0.1.26",
23
23
  "@bobfrankston/tcp-transport": "^0.1.4"
24
24
  },
25
25
  "exports": {
@@ -44,7 +44,7 @@
44
44
  },
45
45
  ".transformedSnapshot": {
46
46
  "dependencies": {
47
- "@bobfrankston/iflow-direct": "^0.1.23",
47
+ "@bobfrankston/iflow-direct": "^0.1.26",
48
48
  "@bobfrankston/tcp-transport": "^0.1.4"
49
49
  }
50
50
  }