@bobfrankston/iflow-direct 0.1.16 → 0.1.17

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.
package/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # @bobfrankston/iflow-direct
2
+
3
+ Transport-agnostic IMAP client. Zero Node.js deps — works in Node, Electron, and
4
+ the browser (via a bridge transport). Caller supplies a `TransportFactory`, OAuth
5
+ token provider (if needed), and drives the client.
6
+
7
+ See [MIGRATION.md](./MIGRATION.md) for the split from legacy `@bobfrankston/iflow`
8
+ and the desktop/browser architecture diagram.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install @bobfrankston/iflow-direct
14
+ # desktop TCP transport:
15
+ npm install @bobfrankston/tcp-transport
16
+ ```
17
+
18
+ ## Quick start (desktop)
19
+
20
+ ```typescript
21
+ import { NativeImapClient } from "@bobfrankston/iflow-direct";
22
+ import { TcpTransport } from "@bobfrankston/tcp-transport";
23
+
24
+ const client = new NativeImapClient(
25
+ { server: "imap.example.com", port: 993, username: "me", password: "..." },
26
+ () => new TcpTransport(),
27
+ );
28
+ await client.connect();
29
+ await client.select("INBOX");
30
+ const msgs = await client.fetchMessages("1:50", { source: false });
31
+ await client.logout();
32
+ ```
33
+
34
+ For the legacy API shape (`getFolderList`, etc.) use `CompatImapClient` from the
35
+ same package.
36
+
37
+ ## Batch body retrieval
38
+
39
+ Two APIs for pulling bodies across many UIDs in a single `UID FETCH` round trip,
40
+ with per-message streaming as responses arrive (not buffered until the tagged OK).
41
+
42
+ ### `fetchMessagesStream(range, options, onMessage?)`
43
+
44
+ Streaming variant of `fetchMessages`. `range` accepts any IMAP sequence set —
45
+ single UID (`"42"`), range (`"100:200"`), comma list (`"1,5,10,42"`), or mixed
46
+ (`"1:10,42,50:55"`). The server handles all forms.
47
+
48
+ ```typescript
49
+ await client.select("INBOX");
50
+ await client.fetchMessagesStream(
51
+ "1,5,10,42,100:150",
52
+ { source: true, headers: false },
53
+ (msg) => {
54
+ // Called once per message as soon as its FETCH response
55
+ // (literals included) is fully received. Tagged OK has not arrived yet.
56
+ console.log(msg.uid, msg.source.length);
57
+ },
58
+ );
59
+ ```
60
+
61
+ Returns the parsed messages at the tagged OK; if `onMessage` is omitted, behaves
62
+ like `fetchMessages` (collect-all).
63
+
64
+ ### `fetchBodiesBatch(folderPath, uids, onBody)`
65
+
66
+ Folder-scoped convenience. Selects the folder (skips if already selected), issues
67
+ one `UID FETCH <comma-list> (UID FLAGS ENVELOPE RFC822.SIZE INTERNALDATE BODY.PEEK[])`,
68
+ streams each `(uid, source)` through `onBody`, returns on tagged OK.
69
+
70
+ ```typescript
71
+ await client.fetchBodiesBatch("INBOX", [1, 5, 10, 42], (uid, source) => {
72
+ store.saveBody(uid, source);
73
+ });
74
+ ```
75
+
76
+ Intended for prefetch pipelines — e.g. mailx-imap's body-prefetch path — that
77
+ would otherwise issue one SELECT+FETCH per message.
78
+
79
+ ### Streaming guarantees
80
+
81
+ - `onMessage` / `onBody` fires on the same event-loop turn that the server's
82
+ FETCH response (with any literal bodies) is fully parsed.
83
+ - Ordering matches server response order. For a comma list, servers typically
84
+ return in the order requested, but RFC 3501 does not require it — treat order
85
+ as server-defined.
86
+ - Callback exceptions are caught and logged (verbose mode) so one bad message
87
+ doesn't abort the batch. Throw only if you want that behavior and wrap your
88
+ own try/catch.
89
+ - The tagged-OK promise still resolves with the full response list; callers
90
+ needing both streaming and a terminal "all done" signal should `await` the
91
+ returned promise.
92
+
93
+ ## Other APIs
94
+
95
+ `NativeImapClient` exposes the usual IMAP surface: `select`, `examine`,
96
+ `listFolders`, `getStatus`, `search`, `fetchMessage`, `fetchSinceUid`,
97
+ `fetchByDate` (both with optional `onChunk` chunked callbacks), `addFlags`,
98
+ `removeFlags`, `copyMessage`, `moveMessage`, `deleteMessage`, `expunge`,
99
+ `appendMessage`, `startIdle`, `logout`. See `imap-native.ts` for signatures.
100
+
101
+ IDLE auto-suspends when any other command is issued on the same connection and
102
+ re-enters afterwards — safe to interleave commands with a long-running IDLE.
103
+
104
+ ## Configuration
105
+
106
+ `ImapClientConfig` fields relevant to throughput/resilience:
107
+
108
+ | Field | Default | Notes |
109
+ |---|---|---|
110
+ | `inactivityTimeout` | 60000 | ms with no data before connection is declared dead. Timer resets on every byte. Slow Dovecot often needs 180000+. |
111
+ | `fetchChunkSize` | 25 | Initial chunk size for `fetchSinceUid`/`fetchByDate`. |
112
+ | `fetchChunkSizeMax` | 500 | Ramps up 4× per chunk. Batch APIs above bypass chunking — caller decides. |
113
+ | `verbose` | false | Log every command/response line + literal bookkeeping. |
114
+
115
+ ## Files
116
+
117
+ See [MIGRATION.md § Files in iflow-direct](./MIGRATION.md#files-in-iflow-direct).
package/imap-compat.d.ts CHANGED
@@ -38,6 +38,11 @@ export declare class CompatImapClient {
38
38
  fetchMessagesSinceUid(mailbox: string, sinceUid: number, options?: {
39
39
  source?: boolean;
40
40
  }): Promise<FetchedMessage[]>;
41
+ /** Batch-fetch bodies for many UIDs in one folder on one connection. Streams
42
+ * each body through `onBody` as it arrives. No per-message round trips —
43
+ * one SELECT, one UID FETCH, streaming response. Required by mailx-imap's
44
+ * batch prefetch path. */
45
+ fetchBodiesBatch(mailbox: string, uids: number[], onBody: (uid: number, source: string) => void): Promise<void>;
41
46
  /** Fetch messages by date range. Optional onChunk callback for incremental processing. */
42
47
  fetchMessageByDate(mailbox: string, start: Date, end?: Date, options?: {
43
48
  source?: boolean;
package/imap-compat.js CHANGED
@@ -93,6 +93,16 @@ export class CompatImapClient {
93
93
  await this.native.closeMailbox();
94
94
  return msgs.map(m => new FetchedMessage(m));
95
95
  }
96
+ /** Batch-fetch bodies for many UIDs in one folder on one connection. Streams
97
+ * each body through `onBody` as it arrives. No per-message round trips —
98
+ * one SELECT, one UID FETCH, streaming response. Required by mailx-imap's
99
+ * batch prefetch path. */
100
+ async fetchBodiesBatch(mailbox, uids, onBody) {
101
+ if (uids.length === 0)
102
+ return;
103
+ await this.ensureConnected();
104
+ await this.native.fetchBodiesBatch(mailbox, uids, onBody);
105
+ }
96
106
  /** Fetch messages by date range. Optional onChunk callback for incremental processing. */
97
107
  async fetchMessageByDate(mailbox, start, end, options, onChunk) {
98
108
  await this.ensureConnected();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/iflow-direct",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "Direct IMAP client — transport-agnostic, no Node.js dependencies, browser-ready",
5
5
  "main": "index.js",
6
6
  "types": "index.ts",