@bobfrankston/iflow-direct 0.1.42 → 0.1.43
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 +36 -0
- package/imap-compat.js +55 -0
- package/imap-native.d.ts +5 -0
- package/imap-native.js +8 -0
- package/package.json +1 -1
package/imap-compat.d.ts
CHANGED
|
@@ -106,6 +106,42 @@ 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
|
+
/** RFC 5161 ENABLE — activates an IMAP extension for the remainder of
|
|
110
|
+
* the session. QRESYNC must be enabled before any SELECT for
|
|
111
|
+
* `* VANISHED` responses + automatic `MODSEQ` to start flowing.
|
|
112
|
+
* Returns the set of extensions the server confirmed are active. */
|
|
113
|
+
enable(extensions: string[]): Promise<Set<string>>;
|
|
114
|
+
/** Convenience: enable QRESYNC if the server advertises it. Returns
|
|
115
|
+
* true if QRESYNC is active on the session afterwards. Idempotent —
|
|
116
|
+
* safe to call once per connection at connect time. */
|
|
117
|
+
enableQresync(): Promise<boolean>;
|
|
118
|
+
/** Fast resync of a mailbox via RFC 7162 QRESYNC. Caller supplies the
|
|
119
|
+
* `uidValidity` and last-seen `modSeq` from its prior visit; the
|
|
120
|
+
* server replies with `* VANISHED` for UIDs expunged since then and
|
|
121
|
+
* unsolicited `* FETCH` for state changes (flag updates etc.) since
|
|
122
|
+
* then, plus the current `HIGHESTMODSEQ` for the caller to persist
|
|
123
|
+
* as its new watermark.
|
|
124
|
+
*
|
|
125
|
+
* Returns:
|
|
126
|
+
* - `uidValidityChanged`: server's UIDVALIDITY no longer matches; the
|
|
127
|
+
* caller's UID set is stale and MUST be full-resynced.
|
|
128
|
+
* - `vanishedUids`: authoritative deletion list. No client-side diff.
|
|
129
|
+
* - `changedMessages`: state-change FETCH responses since the prior
|
|
130
|
+
* modSeq — each carries `uid`, `flags`, and `modSeq` (and possibly
|
|
131
|
+
* other FETCH items the caller asked for).
|
|
132
|
+
* - `newHighestModSeq`: persist this for the next resync.
|
|
133
|
+
*
|
|
134
|
+
* Pre-requisite: `enableQresync()` returned true once on the session.
|
|
135
|
+
* If the server doesn't support QRESYNC, fall back to the older
|
|
136
|
+
* `fetchMessagesSinceUid` / set-diff path. */
|
|
137
|
+
resyncFolder(mailbox: string, uidValidity: number, modSeq: number): Promise<{
|
|
138
|
+
uidValidityChanged: boolean;
|
|
139
|
+
vanishedUids: number[];
|
|
140
|
+
changedMessages: FetchedMessage[];
|
|
141
|
+
newHighestModSeq: number | undefined;
|
|
142
|
+
exists: number;
|
|
143
|
+
uidNext: number;
|
|
144
|
+
}>;
|
|
109
145
|
/** Watch a mailbox for new messages (IDLE). Optionally engage RFC 5465
|
|
110
146
|
* NOTIFY so the server also pushes STATUS responses for non-selected
|
|
111
147
|
* mailboxes named in `opts.notifySpec` — `opts.onMailboxStatus` fires
|
package/imap-compat.js
CHANGED
|
@@ -279,6 +279,61 @@ export class CompatImapClient {
|
|
|
279
279
|
getCapabilities() {
|
|
280
280
|
return this.native.getCapabilities();
|
|
281
281
|
}
|
|
282
|
+
/** RFC 5161 ENABLE — activates an IMAP extension for the remainder of
|
|
283
|
+
* the session. QRESYNC must be enabled before any SELECT for
|
|
284
|
+
* `* VANISHED` responses + automatic `MODSEQ` to start flowing.
|
|
285
|
+
* Returns the set of extensions the server confirmed are active. */
|
|
286
|
+
async enable(extensions) {
|
|
287
|
+
await this.ensureConnected();
|
|
288
|
+
return this.native.enable(extensions);
|
|
289
|
+
}
|
|
290
|
+
/** Convenience: enable QRESYNC if the server advertises it. Returns
|
|
291
|
+
* true if QRESYNC is active on the session afterwards. Idempotent —
|
|
292
|
+
* safe to call once per connection at connect time. */
|
|
293
|
+
async enableQresync() {
|
|
294
|
+
const caps = this.getCapabilities();
|
|
295
|
+
if (!caps.has("QRESYNC"))
|
|
296
|
+
return false;
|
|
297
|
+
const enabled = await this.enable(["QRESYNC"]);
|
|
298
|
+
return enabled.has("QRESYNC");
|
|
299
|
+
}
|
|
300
|
+
/** Fast resync of a mailbox via RFC 7162 QRESYNC. Caller supplies the
|
|
301
|
+
* `uidValidity` and last-seen `modSeq` from its prior visit; the
|
|
302
|
+
* server replies with `* VANISHED` for UIDs expunged since then and
|
|
303
|
+
* unsolicited `* FETCH` for state changes (flag updates etc.) since
|
|
304
|
+
* then, plus the current `HIGHESTMODSEQ` for the caller to persist
|
|
305
|
+
* as its new watermark.
|
|
306
|
+
*
|
|
307
|
+
* Returns:
|
|
308
|
+
* - `uidValidityChanged`: server's UIDVALIDITY no longer matches; the
|
|
309
|
+
* caller's UID set is stale and MUST be full-resynced.
|
|
310
|
+
* - `vanishedUids`: authoritative deletion list. No client-side diff.
|
|
311
|
+
* - `changedMessages`: state-change FETCH responses since the prior
|
|
312
|
+
* modSeq — each carries `uid`, `flags`, and `modSeq` (and possibly
|
|
313
|
+
* other FETCH items the caller asked for).
|
|
314
|
+
* - `newHighestModSeq`: persist this for the next resync.
|
|
315
|
+
*
|
|
316
|
+
* Pre-requisite: `enableQresync()` returned true once on the session.
|
|
317
|
+
* If the server doesn't support QRESYNC, fall back to the older
|
|
318
|
+
* `fetchMessagesSinceUid` / set-diff path. */
|
|
319
|
+
async resyncFolder(mailbox, uidValidity, modSeq) {
|
|
320
|
+
await this.ensureConnected();
|
|
321
|
+
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)) || [];
|
|
328
|
+
return {
|
|
329
|
+
uidValidityChanged: result.uidValidityChanged,
|
|
330
|
+
vanishedUids: result.vanishedUids,
|
|
331
|
+
changedMessages,
|
|
332
|
+
newHighestModSeq: result.highestModSeq,
|
|
333
|
+
exists: result.exists,
|
|
334
|
+
uidNext: result.uidNext,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
282
337
|
/** Watch a mailbox for new messages (IDLE). Optionally engage RFC 5465
|
|
283
338
|
* NOTIFY so the server also pushes STATUS responses for non-selected
|
|
284
339
|
* mailboxes named in `opts.notifySpec` — `opts.onMailboxStatus` fires
|
package/imap-native.d.ts
CHANGED
|
@@ -30,6 +30,11 @@ export interface NativeFetchedMessage {
|
|
|
30
30
|
flagged: boolean;
|
|
31
31
|
answered: boolean;
|
|
32
32
|
draft: boolean;
|
|
33
|
+
/** Per-message modification sequence (RFC 7162). Present when the
|
|
34
|
+
* server is CONDSTORE/QRESYNC-capable and the client either has
|
|
35
|
+
* ENABLE QRESYNC active or asked for MODSEQ explicitly. Used by the
|
|
36
|
+
* caller to track its own `last_modseq` watermark per folder. */
|
|
37
|
+
modSeq?: number;
|
|
33
38
|
}
|
|
34
39
|
export interface NativeFolder {
|
|
35
40
|
path: string;
|
package/imap-native.js
CHANGED
|
@@ -1336,6 +1336,14 @@ export class NativeImapClient {
|
|
|
1336
1336
|
const sizeMatch = r.text.match(/RFC822\.SIZE\s+(\d+)/);
|
|
1337
1337
|
if (sizeMatch)
|
|
1338
1338
|
msg.size = parseInt(sizeMatch[1]);
|
|
1339
|
+
// Extract MODSEQ (RFC 7162). Format: `MODSEQ (12345)`. Server
|
|
1340
|
+
// emits this when the session has ENABLE QRESYNC/CONDSTORE
|
|
1341
|
+
// active OR when the client explicitly asks for MODSEQ in the
|
|
1342
|
+
// FETCH command. Track on every FETCH so the caller can update
|
|
1343
|
+
// its `last_modseq` watermark.
|
|
1344
|
+
const modSeqMatch = r.text.match(/MODSEQ\s+\((\d+)\)/);
|
|
1345
|
+
if (modSeqMatch)
|
|
1346
|
+
msg.modSeq = parseInt(modSeqMatch[1]);
|
|
1339
1347
|
// Extract INTERNALDATE
|
|
1340
1348
|
const dateMatch = r.text.match(/INTERNALDATE\s+"([^"]+)"/);
|
|
1341
1349
|
if (dateMatch)
|