import type { Email, Attachment, FetchEmailsProps, SearchEmailsProps, MailboxInfo, Folder, Namespace, Options, CopyUidInfo, AppendResult } from "./types/emails"; import { ImapError } from "./utils/imapStream"; import type { ResponseItem } from "./utils/imapStream"; export { ImapError }; export type { Options, Email, Attachment, FetchEmailsProps, SearchEmailsProps, MailboxInfo, Folder, Namespace, CopyUidInfo, AppendResult, ResponseItem }; export declare class CFImap { private options; private socket; private stream; private writer; private tagCounter; private busy; session: { id?: string; protocol?: string; }; /** Capabilities advertised by the server, e.g. ["IMAP4rev2", "UIDPLUS"] */ capabilities: string[]; /** * Only used to determine if a folder is selected */ selectedFolder: string; constructor(options: Options); private requireConnection; private requireFolder; private nextTag; private send; /** * Best-effort command send that never hangs: if the write does not settle * within timeoutMs (e.g. the peer reset the connection and workerd keeps * the write pending), returns false. Used to leave IDLE mode on a socket * that may already be dead. */ private sendBestEffort; /** True when the session is using IMAP4rev2 semantics (RFC 9051). */ private get isRev2(); /** * IMAP4rev2 uses UTF-8 (Net-Unicode) mailbox names; IMAP4rev1 uses * modified UTF-7. Encode/decode accordingly based on the negotiated version. */ private encodeMailboxName; private decodeMailboxName; /** * Serializes IMAP commands: only one command may be in flight at a time, * because responses are matched to tags. Nested command methods must use * the internal (unguarded) helpers instead. */ private command; /** * Connects to the IMAP server and authenticates. Must be run after * initialising the CFImap class, otherwise nothing will work. * * Handles, in order: greeting (incl. BYE rejection), STARTTLS (RFC 9051 * §6.2.1), authentication (AUTHENTICATE XOAUTH2 with an OAuth token when * configured, otherwise AUTHENTICATE PLAIN with SASL-IR falling back to * LOGIN when the server allows it — RFC 9051 §6.2.2/§6.2.3) and * ENABLE IMAP4rev2 when the server advertises both versions (Appendix A). */ connect: () => Promise; /** * Authenticates the session. * * When an OAuth 2.0 access token is configured (auth.accessToken or * auth.getAccessToken) and the server advertises AUTH=XOAUTH2, the * XOAUTH2 SASL mechanism is used — required by Gmail, Microsoft 365 / * Outlook.com and other modern providers that refuse passwords. * * Otherwise: AUTHENTICATE PLAIN (with SASL initial response) when the * server advertises AUTH=PLAIN, falling back to LOGIN — the "last * resort" per RFC 9051 §6.2.3 — unless the server advertises * LOGINDISABLED, in which case LOGIN is forbidden. */ private authenticate; /** * Authenticates via the XOAUTH2 SASL mechanism with an OAuth 2.0 access * token. Uses SASL-IR (RFC 4959) when advertised, otherwise answers the * server's challenge with the initial response. * * On failure the server sends a `+ ` challenge (error * status/schemes/scope); per the XOAUTH2 protocol the client MUST * acknowledge it with an empty line before the server responds with the * tagged NO — without that acknowledgment the exchange deadlocks. */ private authenticateXoauth2; /** * Enables IMAP extensions via the ENABLE command (RFC 9051 §6.3.1). * Only valid in the authenticated state, before any mailbox is selected. * @param capabilities - Capability names to enable, e.g. "IMAP4rev2" or ["CONDSTORE"] * @returns The capabilities the server confirmed as enabled */ enable: (capabilities: string | string[]) => Promise; private enableInternal; /** * Issues the CAPABILITY command, updates this.capabilities and returns * the current list (RFC 9051 §6.1.1). */ capability: () => Promise; private capabilityInternal; /** * Sends NOOP — useful to keep the connection alive and to trigger * unsolicited updates. Returns the untagged responses received. */ noop: () => Promise; /** * Returns the prefixes and hierarchy delimiters of the personal, other * and shared namespaces available to the logged in user. */ getNamespaces: () => Promise<{ personal: Namespace[]; other: Namespace[]; shared: Namespace[]; }>; /** * Returns all folders in the specified namespace along with their flags. * @param {string} namespace - From which namespace to list folders (usually "" or the prefix from getNamespaces()) * @param {string} filter - Pattern filter, e.g. "*" or "INBOX*" */ getFolders: (namespace: string, filter?: string) => Promise; /** * SELECT (or EXAMINE when `examine` is true) a mailbox and parse the * untagged responses (EXISTS, FLAGS, UIDVALIDITY, PERMANENTFLAGS, ...). */ private selectOrExamine; /** * Selects a folder for use in the email GET & FETCH functions. Must be * run before those commands (or pass the folder prop to fetchEmails()). * @param folder - Selectable folder */ selectFolder: (folder: string) => Promise; /** * Opens a mailbox read-only (RFC 9051 §6.3.3). Identical output to * selectFolder(), but no changes to the mailbox (including per-user * state such as flags) are permitted. * @param folder - Folder to examine */ examine: (folder: string) => Promise; /** * Fetches emails from a folder specified by the selectFolder() function * (or via the folder prop). * * @param {Object} props - Props * @param {number} [props.byteLimit] - Maximum size of the emails to fetch (optional, not recommended) * @param {[ number, number ] | number} props.limit - Range of sequence numbers to fetch (or a single one) * @param {boolean} [props.peek=true] - If true (default), fetching won't set the \Seen flag * @param {boolean} [props.fetchBody=true] - If true (default), the full message is fetched and parsed */ fetchEmails: ({ folder, limit, fetchBody, byteLimit, peek, useUid }: FetchEmailsProps) => Promise; /** * Searches emails based on the props given. Returns sequence numbers * (or UIDs with useUid: true). * * Handles both the IMAP4rev1 "* SEARCH" response and the IMAP4rev2 * "* ESEARCH" response (RFC 9051 §6.4.4). */ searchEmails: (props: SearchEmailsProps) => Promise; /** * Adds, removes or replaces flags on one or more messages. * @param target - Sequence number range (or UID range with useUid), e.g. "1:5" or "42" * @param flags - Flags without the backslash, e.g. ["Seen", "Flagged"] * @param mode - add (default), remove or replace */ storeFlags: (target: string, flags: string[], mode?: "add" | "remove" | "replace", useUid?: boolean) => Promise<{ seq: number; flags: string[]; }[]>; /** * Permanently removes messages marked with the \Deleted flag. * @param opts.range - Optional sequence/UID range to restrict expunging to. * @param opts.useUid - Use UID EXPUNGE (requires a range; only removes the expunged UIDs of the selected mailbox) */ expunge: (opts?: { range?: string; useUid?: boolean; }) => Promise; /** * Copies messages to another folder. * @param target - Destination folder * @param range - Sequence number range (or UID range with useUid), e.g. "1:5" * @returns The COPYUID mapping (source/destination UIDs) if the server reports one */ copy: (target: string, range: string, useUid?: boolean) => Promise; /** * Moves messages to another folder (part of the base protocol in * IMAP4rev2; requires the MOVE capability on IMAP4rev1 servers). * @param target - Destination folder * @param range - Sequence number range (or UID range with useUid), e.g. "1:5" * @returns The COPYUID mapping (source/destination UIDs) if the server reports one */ move: (target: string, range: string, useUid?: boolean) => Promise; /** * Requests mailbox status information. * @param folder - Folder to check * @param items - Which items to request (defaults to MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN; * IMAP4rev2 also supports DELETED and SIZE) */ status: (folder: string, items?: Array<"MESSAGES" | "RECENT" | "UIDNEXT" | "UIDVALIDITY" | "UNSEEN" | "DELETED" | "SIZE">) => Promise>; /** * Appends a message to a folder. * @param folder - Destination folder * @param message - Full raw message (headers + body), as string or bytes * @param flags - Flags to set on the appended message, e.g. ["Seen"] * @param internalDate - Internal date of the message * @returns The APPENDUID result (UIDVALIDITY + assigned UID) if the server reports one */ append: (folder: string, message: string | Uint8Array, flags?: string[], internalDate?: Date) => Promise; /** * Requests a "checkpoint" on the server, a.k.a requests that the server * does some housekeeping. Almost never used. * * Note: CHECK was removed in RFC 9051 (IMAP4rev2) — it only works against * IMAP4rev1 servers. Use NOOP or IDLE instead. */ check: () => Promise; /** * Enters IDLE mode (RFC 9051 §6.3.13): the server pushes unsolicited * updates (EXISTS, EXPUNGE, FETCH, ...) as they happen. The callback is * invoked for each untagged response and may return false to leave IDLE. * * IDLE ends cleanly (the connection stays usable) when the callback * returns false, when no updates arrive within timeoutMs (re-issue * idle() to keep watching), or when the server ends IDLE itself. * * While IDLE is active, no other command may be issued on this connection. */ idle: (onUpdate?: ((item: ResponseItem) => boolean | void) | undefined) => Promise; /** * Closes the selected mailbox (RFC 9051 §6.4.1): messages marked \Deleted * are permanently removed, then the mailbox is deselected. */ closeMailbox: () => Promise; /** * Deselects the currently selected mailbox without expunging * (RFC 9051 §6.4.2). */ unselect: () => Promise; /** * Logs the user out of the IMAP session and closes the socket. */ logout: () => Promise; /** Alias of logout() */ close: () => Promise; }