@bobfrankston/iflow-direct 0.1.67 → 0.1.69

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/imap-compat.d.ts CHANGED
@@ -65,6 +65,13 @@ export declare class CompatImapClient {
65
65
  }): Promise<FetchedMessage | null>;
66
66
  /** Get message count via STATUS (does not require SELECT) */
67
67
  getMessagesCount(mailbox: string): Promise<number>;
68
+ /** Mailbox size in octets + message count. STATUS=SIZE when the server has it, else a
69
+ * size-only UID FETCH. See NativeImapClient.getFolderSize. (2026-09-16, Claude Code) */
70
+ getFolderSize(mailbox: string): Promise<{
71
+ messages: number;
72
+ bytes: number;
73
+ method: "status" | "fetch";
74
+ }>;
68
75
  /** Get all UIDs in a mailbox */
69
76
  getUids(mailbox: string): Promise<number[]>;
70
77
  /** Get UIDs whose INTERNALDATE is on/after `since`. Bounded version of
package/imap-compat.js CHANGED
@@ -160,6 +160,12 @@ export class CompatImapClient {
160
160
  await this.ensureConnected();
161
161
  return this.native.getMessageCount(mailbox);
162
162
  }
163
+ /** Mailbox size in octets + message count. STATUS=SIZE when the server has it, else a
164
+ * size-only UID FETCH. See NativeImapClient.getFolderSize. (2026-09-16, Claude Code) */
165
+ async getFolderSize(mailbox) {
166
+ await this.ensureConnected();
167
+ return this.native.getFolderSize(mailbox);
168
+ }
163
169
  /** Get all UIDs in a mailbox */
164
170
  async getUids(mailbox) {
165
171
  await this.ensureConnected();
package/imap-native.d.ts CHANGED
@@ -192,7 +192,24 @@ export declare class NativeImapClient {
192
192
  /** Close the currently selected mailbox */
193
193
  closeMailbox(): Promise<void>;
194
194
  listFolders(): Promise<NativeFolder[]>;
195
- getStatus(mailbox: string): Promise<proto.StatusData>;
195
+ getStatus(mailbox: string, items?: string[]): Promise<proto.StatusData>;
196
+ /**
197
+ * Total size of a mailbox in octets plus its message count (for imail -sizes; added
198
+ * 2026-09-16 by Claude Code, Fable 5.1, at Bob's direction).
199
+ *
200
+ * Fast path: RFC 8438 `STATUS (MESSAGES SIZE)` — one round trip, no message traffic —
201
+ * when the server advertises STATUS=SIZE (Dovecot, Gmail do).
202
+ * Fallback: EXAMINE (read-only) + `UID FETCH 1:* (UID RFC822.SIZE)` summed here. That
203
+ * is one FETCH line per message but no envelopes or headers, so it stays cheap even
204
+ * on a folder of tens of thousands of messages.
205
+ *
206
+ * `method` says which path produced the number so a log can show it.
207
+ */
208
+ getFolderSize(mailbox: string): Promise<{
209
+ messages: number;
210
+ bytes: number;
211
+ method: "status" | "fetch";
212
+ }>;
196
213
  createMailbox(mailbox: string): Promise<void>;
197
214
  deleteMailbox(mailbox: string): Promise<void>;
198
215
  renameMailbox(from: string, to: string): Promise<void>;
package/imap-native.js CHANGED
@@ -469,9 +469,11 @@ export class NativeImapClient {
469
469
  console.error(` [imap] ${unparsed} LIST responses could not be parsed (${responses.length} total responses)`);
470
470
  return folders;
471
471
  }
472
- async getStatus(mailbox) {
472
+ // 2026-09-16 — Claude Code (Fable 5.1), at Bob's direction: `items` parameter added so a
473
+ // caller can ask for RFC 8438 SIZE. Default unchanged, so existing callers see no difference.
474
+ async getStatus(mailbox, items = ["MESSAGES", "UIDNEXT", "UNSEEN"]) {
473
475
  const tag = proto.nextTag();
474
- const responses = await this.sendCommand(tag, proto.statusCommand(tag, mailbox, ["MESSAGES", "UIDNEXT", "UNSEEN"]));
476
+ const responses = await this.sendCommand(tag, proto.statusCommand(tag, mailbox, items));
475
477
  for (const r of responses) {
476
478
  if (r.tag === "*" && r.type === "STATUS") {
477
479
  const data = proto.parseStatusResponse(r.text);
@@ -481,6 +483,46 @@ export class NativeImapClient {
481
483
  }
482
484
  return {};
483
485
  }
486
+ /**
487
+ * Total size of a mailbox in octets plus its message count (for imail -sizes; added
488
+ * 2026-09-16 by Claude Code, Fable 5.1, at Bob's direction).
489
+ *
490
+ * Fast path: RFC 8438 `STATUS (MESSAGES SIZE)` — one round trip, no message traffic —
491
+ * when the server advertises STATUS=SIZE (Dovecot, Gmail do).
492
+ * Fallback: EXAMINE (read-only) + `UID FETCH 1:* (UID RFC822.SIZE)` summed here. That
493
+ * is one FETCH line per message but no envelopes or headers, so it stays cheap even
494
+ * on a folder of tens of thousands of messages.
495
+ *
496
+ * `method` says which path produced the number so a log can show it.
497
+ */
498
+ async getFolderSize(mailbox) {
499
+ if (this.capabilities.has("STATUS=SIZE")) {
500
+ const st = await this.getStatus(mailbox, ["MESSAGES", "SIZE"]);
501
+ if (typeof st.size === "number")
502
+ return { messages: st.messages || 0, bytes: st.size, method: "status" };
503
+ // Server claimed STATUS=SIZE but answered without SIZE — fall through to counting.
504
+ }
505
+ const info = await this.examine(mailbox);
506
+ try {
507
+ if (!info.exists)
508
+ return { messages: 0, bytes: 0, method: "fetch" };
509
+ let messages = 0, bytes = 0;
510
+ const tag = proto.nextTag();
511
+ await this.sendCommand(tag, proto.fetchCommand(tag, "1:*", ["UID", "RFC822.SIZE"]), (r) => {
512
+ if (r.tag !== "*" || r.type !== "FETCH")
513
+ return;
514
+ const m = r.text.match(/RFC822\.SIZE\s+(\d+)/);
515
+ if (!m)
516
+ return;
517
+ messages++;
518
+ bytes += parseInt(m[1]);
519
+ });
520
+ return { messages, bytes, method: "fetch" };
521
+ }
522
+ finally {
523
+ await this.closeMailbox();
524
+ }
525
+ }
484
526
  async createMailbox(mailbox) {
485
527
  const tag = proto.nextTag();
486
528
  const responses = await this.sendCommand(tag, proto.createCommand(tag, mailbox));
@@ -58,6 +58,9 @@ export interface StatusData {
58
58
  uidNext?: number;
59
59
  uidValidity?: number;
60
60
  unseen?: number;
61
+ /** Total mailbox size in octets — RFC 8438 `STATUS (SIZE)`, only when the
62
+ * server advertises STATUS=SIZE and the caller asked for it. */
63
+ size?: number;
61
64
  }
62
65
  /** Generate a unique command tag */
63
66
  export declare function nextTag(): string;
package/imap-protocol.js CHANGED
@@ -239,6 +239,9 @@ export function parseStatusResponse(text) {
239
239
  data.uidValidity = val;
240
240
  else if (key === "UNSEEN")
241
241
  data.unseen = val;
242
+ // 2026-09-16 — Claude Code (Fable 5.1), at Bob's direction: RFC 8438 SIZE, for imail -sizes.
243
+ else if (key === "SIZE")
244
+ data.size = val;
242
245
  }
243
246
  return data;
244
247
  }
@@ -267,6 +270,8 @@ export function parseStatusResponseFull(text) {
267
270
  data.uidValidity = val;
268
271
  else if (key === "UNSEEN")
269
272
  data.unseen = val;
273
+ else if (key === "SIZE")
274
+ data.size = val; // RFC 8438 (2026-09-16)
270
275
  }
271
276
  return { mailbox, data };
272
277
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/iflow-direct",
3
- "version": "0.1.67",
3
+ "version": "0.1.69",
4
4
  "description": "Direct IMAP client — transport-agnostic, no Node.js dependencies, browser-ready",
5
5
  "main": "index.js",
6
6
  "types": "index.ts",