/** * Gmail API provider — replaces IMAP for Gmail accounts. * Uses REST API for fast, reliable sync without connection limit issues. * * SOURCE OF TRUTH: this file is the single canonical Gmail provider for * mailx. Both desktop (mailx-imap, Node) and Android (mailx-store-web, * WebView) re-export from here. Earlier they had parallel copies that * silently drifted (the 403-quota retry was added to desktop one day and * missed in the web copy until a user reported broken prefetch). * * Platform requirements: globalThis.fetch (Node 18+ and all browsers/WebViews), * atob, Uint8Array, TextDecoder. No Node-specific imports. */ import type { MailProvider, ProviderFolder, ProviderMessage, FetchOptions } from "./types.js"; export declare class GmailApiProvider implements MailProvider { private tokenProvider; constructor(tokenProvider: () => Promise); /** Block until (a) cooldown has elapsed and (b) a token is available. * Token-bucket refill happens lazily on each call. */ private acquireToken; private fetch; listFolders(): Promise; /** List message IDs matching a query, handling pagination. * IMPORTANT: on any error we throw — do NOT return a partial list, because * callers use this for sync reconciliation and a short list would delete * real messages from the local DB. Returning [] silently caused the * "INBOX empty in mailx" bug when a rate-limit hit mid-pagination. */ private listMessageIds; /** Batch-fetch message metadata or full content */ private batchFetch; /** Parse a Gmail API message response into ProviderMessage */ private parseMessage; fetchSince(folder: string, sinceUid: number, options?: FetchOptions): Promise; fetchByDate(folder: string, since: Date, before: Date, options?: FetchOptions, onChunk?: (msgs: ProviderMessage[]) => void): Promise; fetchByUids(folder: string, uids: number[], options?: FetchOptions): Promise; /** Bulk-fetch raw bodies for many UIDs in one "folder" (Gmail label). * Lists the label once, builds UID→ID map, then streams bodies through * `onBody`. Uses Gmail's HTTP batch endpoint (up to 100 sub-requests per * round-trip) when available, with a single-request fallback so a batch * protocol blip doesn't starve prefetch entirely. * * NOTE: Gmail's model is labels, not folders — a single message can be in * many labels. Treating each label as a folder causes duplicate fetches * across labels. Proper fix tracked as separate TODO ("Gmail label-native * model"). For now we mirror the IMAP folder grouping, accepting duplicate * fetches of multi-labeled messages. */ fetchBodiesBatch(folder: string, uids: number[], onBody: (uid: number, source: string) => void): Promise; /** POST /batch/gmail/v1 with up to 100 sub-requests. See * https://developers.google.com/gmail/api/guides/batch * for the multipart/mixed wire format. */ private batchFetchBodies; /** Fallback path when batch fails — original bounded-concurrency loop. * Kept on the degraded path so a single bad batch doesn't halt prefetch. */ private fetchBodiesIndividually; fetchOne(folder: string, uid: number, options?: FetchOptions): Promise; /** Apply the absolute flag state to a message. * Gmail model: flags are labels. `\Seen` is the *absence* of UNREAD; * `\Flagged` is the presence of STARRED. We always send both add and * remove so the end state matches regardless of what was there before, * which makes the call idempotent and safe to retry. */ setFlags(folder: string, uid: number, flags: string[], messageId?: string): Promise; /** Move a message to the trash label. Gmail treats trash as a label, not * as a destination folder — `POST /messages/{id}/trash` is the native * path (equivalent to setting TRASH and removing INBOX in one op). * Used by mailx's delete/trash path. */ trashMessage(folder: string, uid: number, gmailId?: string): Promise; /** Move between "folders" == swap one label for another via modifyLabels. * System labels (INBOX/SENT/TRASH/SPAM) are translated from the folder * path; user labels use the folder path verbatim as the label id. */ moveMessage(fromFolder: string, uid: number, toFolder: string, gmailId?: string): Promise; /** Folder path → Gmail label id. System folders map to uppercase label * constants; anything else is treated as a user label (identical name). */ private folderPathToLabelId; /** Gmail system label ids that the API forbids renaming — labels.patch on * any of these returns 400. The folder tree must refuse a rename of the * folders that map to them. */ private static readonly SYSTEM_LABELS; /** Rename (and/or reparent) a Gmail label. Gmail has no folders — a * "folder" is a label and the hierarchy is encoded in the label NAME as a * slash-path ("Parent/Child"). So both rename-in-place and reparent are the * same operation: PATCH the label to a new full name. * * - `oldPath` is the current label name (== path in our folder model). * - `newName` is the new LEAF name. * - `newParentPath` (optional) is the destination parent label's path; when * given, the new full name becomes `/`, otherwise * we keep the old parent prefix and just swap the leaf. * * System labels (INBOX/SENT/…) can't be renamed — reject with a clear * message the UI surfaces. We resolve the label id from the live label list * rather than trusting name-as-id, because a user label's id is an opaque * `Label_NN` on some accounts even though the name matches. */ renameFolder(oldPath: string, newName: string, newParentPath?: string): Promise; getUids(folder: string): Promise; close(): Promise; /** Map folder path to Gmail label query term */ private folderToLabel; /** Format date for Gmail query (YYYY/MM/DD) */ private formatDate; } //# sourceMappingURL=gmail.d.ts.map