@bobfrankston/iflow-direct 0.1.43 → 0.1.44

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.
@@ -35,6 +35,7 @@ export declare class FetchedMessage implements NativeFetchedMessage {
35
35
  flagged: boolean;
36
36
  answered: boolean;
37
37
  draft: boolean;
38
+ modSeq?: number;
38
39
  parsedHeader: string[][];
39
40
  headerSet: HeaderMap;
40
41
  constructor(init: NativeFetchedMessage);
@@ -40,6 +40,7 @@ export class FetchedMessage {
40
40
  flagged;
41
41
  answered;
42
42
  draft;
43
+ modSeq;
43
44
  // Parsed-header state
44
45
  parsedHeader;
45
46
  headerSet;
package/imap-compat.d.ts CHANGED
@@ -106,6 +106,16 @@ export declare class CompatImapClient {
106
106
  /** Cached CAPABILITY set parsed at connect/login. Callers gate
107
107
  * optional features (NOTIFY, QRESYNC, MOVE, ...) on this. */
108
108
  getCapabilities(): Set<string>;
109
+ /** Snapshot of the most recently SELECTed mailbox's info. Callers use this
110
+ * to read `highestModSeq` after any operation that did a SELECT — useful
111
+ * for seeding the QRESYNC modSeq watermark on the very first sync of a
112
+ * folder (before the QRESYNC path is eligible). */
113
+ getCurrentMailboxInfo(): {
114
+ uidValidity: number;
115
+ uidNext: number;
116
+ exists: number;
117
+ highestModSeq?: number;
118
+ };
109
119
  /** RFC 5161 ENABLE — activates an IMAP extension for the remainder of
110
120
  * the session. QRESYNC must be enabled before any SELECT for
111
121
  * `* VANISHED` responses + automatic `MODSEQ` to start flowing.
package/imap-compat.js CHANGED
@@ -279,6 +279,14 @@ export class CompatImapClient {
279
279
  getCapabilities() {
280
280
  return this.native.getCapabilities();
281
281
  }
282
+ /** Snapshot of the most recently SELECTed mailbox's info. Callers use this
283
+ * to read `highestModSeq` after any operation that did a SELECT — useful
284
+ * for seeding the QRESYNC modSeq watermark on the very first sync of a
285
+ * folder (before the QRESYNC path is eligible). */
286
+ getCurrentMailboxInfo() {
287
+ const info = this.native.getMailboxInfo?.();
288
+ return info || { uidValidity: 0, uidNext: 0, exists: 0 };
289
+ }
282
290
  /** RFC 5161 ENABLE — activates an IMAP extension for the remainder of
283
291
  * the session. QRESYNC must be enabled before any SELECT for
284
292
  * `* VANISHED` responses + automatic `MODSEQ` to start flowing.
@@ -319,12 +327,7 @@ export class CompatImapClient {
319
327
  async resyncFolder(mailbox, uidValidity, modSeq) {
320
328
  await this.ensureConnected();
321
329
  const result = await this.native.select(mailbox, { uidValidity, modSeq });
322
- // Untagged FETCH responses emitted during a QRESYNC SELECT are
323
- // pre-parsed by the native layer into NativeFetchedMessage objects;
324
- // expose them as FetchedMessage to compat callers. (The native
325
- // select() doesn't currently surface these — that wiring is in
326
- // imap-native.ts:select via the FETCH-during-select case.)
327
- const changedMessages = result.changedMessages?.map((m) => new FetchedMessage(m)) || [];
330
+ const changedMessages = result.changedMessages.map(m => new FetchedMessage(m));
328
331
  return {
329
332
  uidValidityChanged: result.uidValidityChanged,
330
333
  vanishedUids: result.vanishedUids,
package/imap-native.d.ts CHANGED
@@ -67,10 +67,15 @@ export interface QresyncParams {
67
67
  knownUids?: string;
68
68
  }
69
69
  /** Result of a QRESYNC-enabled SELECT. The caller drains `vanishedUids`
70
- * (delete from local store), processes any FETCH responses (upsert state
70
+ * (delete from local store), processes `changedMessages` (upsert state
71
71
  * changes), and saves the new `highestModSeq` for the next resync. */
72
72
  export interface SelectResult extends MailboxInfo {
73
73
  vanishedUids: number[];
74
+ /** Untagged `* FETCH` responses the server emitted during the QRESYNC
75
+ * SELECT — typically flag/MODSEQ changes for messages whose state
76
+ * shifted between the caller's previous modSeq and now. Empty for
77
+ * non-QRESYNC SELECTs. */
78
+ changedMessages: NativeFetchedMessage[];
74
79
  /** True if the server's UIDVALIDITY changed since the caller's last
75
80
  * visit — in that case knownUids/modSeq are invalid and the caller
76
81
  * must full-resync from scratch. */
@@ -146,6 +151,15 @@ export declare class NativeImapClient {
146
151
  * without re-issuing CAPABILITY. Returns a defensive copy so callers
147
152
  * can't mutate internal state. */
148
153
  getCapabilities(): Set<string>;
154
+ /** Snapshot of the currently-SELECTed mailbox's info. Lets the compat
155
+ * client read `highestModSeq` etc. after any select-implying operation
156
+ * (fetchMessagesSinceUid, etc.) without re-issuing SELECT. */
157
+ getMailboxInfo(): {
158
+ uidValidity: number;
159
+ uidNext: number;
160
+ exists: number;
161
+ highestModSeq?: number;
162
+ };
149
163
  private parseCapabilities;
150
164
  logout(): Promise<void>;
151
165
  /** SELECT a mailbox. Optional QRESYNC params trigger RFC 7162 fast resync —
package/imap-native.js CHANGED
@@ -236,6 +236,17 @@ export class NativeImapClient {
236
236
  getCapabilities() {
237
237
  return new Set(this.capabilities);
238
238
  }
239
+ /** Snapshot of the currently-SELECTed mailbox's info. Lets the compat
240
+ * client read `highestModSeq` etc. after any select-implying operation
241
+ * (fetchMessagesSinceUid, etc.) without re-issuing SELECT. */
242
+ getMailboxInfo() {
243
+ return {
244
+ uidValidity: this.mailboxInfo.uidValidity,
245
+ uidNext: this.mailboxInfo.uidNext,
246
+ exists: this.mailboxInfo.exists,
247
+ highestModSeq: this.mailboxInfo.highestModSeq,
248
+ };
249
+ }
239
250
  parseCapabilities(text) {
240
251
  const caps = text.replace(/^CAPABILITY\s*/i, "").split(/\s+/);
241
252
  this.capabilities.clear();
@@ -271,6 +282,10 @@ export class NativeImapClient {
271
282
  this.mailboxInfo.highestModSeq = undefined;
272
283
  const vanishedUids = [];
273
284
  const priorUidValidity = qresync?.uidValidity;
285
+ // Untagged FETCH responses emitted during a QRESYNC SELECT carry
286
+ // state-change deltas the server is volunteering up-front; parse
287
+ // them through the same path as normal FETCH so the shape matches.
288
+ const fetchResponses = responses.filter(r => r.tag === "*" && r.type === "FETCH");
274
289
  for (const r of responses) {
275
290
  if (r.tag !== "*")
276
291
  continue;
@@ -311,9 +326,13 @@ export class NativeImapClient {
311
326
  this.selectedMailbox = mailbox;
312
327
  const uidValidityChanged = priorUidValidity !== undefined
313
328
  && this.mailboxInfo.uidValidity !== priorUidValidity;
329
+ const changedMessages = fetchResponses.length > 0
330
+ ? this.parseFetchResponses(fetchResponses)
331
+ : [];
314
332
  return {
315
333
  ...this.mailboxInfo,
316
334
  vanishedUids,
335
+ changedMessages,
317
336
  uidValidityChanged,
318
337
  };
319
338
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/iflow-direct",
3
- "version": "0.1.43",
3
+ "version": "0.1.44",
4
4
  "description": "Direct IMAP client — transport-agnostic, no Node.js dependencies, browser-ready",
5
5
  "main": "index.js",
6
6
  "types": "index.ts",