@bobfrankston/iflow-direct 0.1.13 → 0.1.15
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/bridge-transport.d.ts +4 -27
- package/bridge-transport.js +4 -57
- package/fetched-message.d.ts +53 -0
- package/fetched-message.js +77 -0
- package/folder-tree.d.ts +27 -0
- package/folder-tree.js +57 -0
- package/imap-compat.d.ts +6 -5
- package/imap-compat.js +6 -43
- package/index.d.ts +3 -0
- package/index.js +4 -0
- package/package.json +12 -1
- package/transport.d.ts +8 -22
- package/transport.js +7 -2
package/bridge-transport.d.ts
CHANGED
|
@@ -1,30 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* The native shell provides:
|
|
6
|
-
* msgapi.tcp.connect(host, port, tls) → streamId
|
|
7
|
-
* msgapi.tcp.write(streamId, data)
|
|
8
|
-
* msgapi.tcp.onData(streamId, callback)
|
|
9
|
-
* msgapi.tcp.upgradeTLS(streamId, servername)
|
|
10
|
-
* msgapi.tcp.close(streamId)
|
|
11
|
-
*
|
|
12
|
-
* TLS is handled natively — JS never sees raw TLS handshake.
|
|
2
|
+
* Back-compat re-export. `BridgeTransport` was renamed to `BridgeTcpTransport`
|
|
3
|
+
* and moved to @bobfrankston/tcp-transport. New code should import that
|
|
4
|
+
* directly. This shim keeps existing imports working.
|
|
13
5
|
*/
|
|
14
|
-
|
|
15
|
-
export declare class BridgeTransport implements ImapTransport {
|
|
16
|
-
private streamId;
|
|
17
|
-
private dataHandler;
|
|
18
|
-
private closeHandler;
|
|
19
|
-
private errorHandler;
|
|
20
|
-
private _connected;
|
|
21
|
-
get connected(): boolean;
|
|
22
|
-
connect(host: string, port: number, tls: boolean, _servername?: string): Promise<void>;
|
|
23
|
-
upgradeTLS(servername?: string): Promise<void>;
|
|
24
|
-
write(data: string | Uint8Array): Promise<void>;
|
|
25
|
-
onData(handler: (data: string) => void): void;
|
|
26
|
-
onClose(handler: (hadError: boolean) => void): void;
|
|
27
|
-
onError(handler: (err: Error) => void): void;
|
|
28
|
-
close(): void;
|
|
29
|
-
}
|
|
6
|
+
export { BridgeTcpTransport as BridgeTransport } from "@bobfrankston/tcp-transport";
|
|
30
7
|
//# sourceMappingURL=bridge-transport.d.ts.map
|
package/bridge-transport.js
CHANGED
|
@@ -1,60 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* The native shell provides:
|
|
6
|
-
* msgapi.tcp.connect(host, port, tls) → streamId
|
|
7
|
-
* msgapi.tcp.write(streamId, data)
|
|
8
|
-
* msgapi.tcp.onData(streamId, callback)
|
|
9
|
-
* msgapi.tcp.upgradeTLS(streamId, servername)
|
|
10
|
-
* msgapi.tcp.close(streamId)
|
|
11
|
-
*
|
|
12
|
-
* TLS is handled natively — JS never sees raw TLS handshake.
|
|
2
|
+
* Back-compat re-export. `BridgeTransport` was renamed to `BridgeTcpTransport`
|
|
3
|
+
* and moved to @bobfrankston/tcp-transport. New code should import that
|
|
4
|
+
* directly. This shim keeps existing imports working.
|
|
13
5
|
*/
|
|
14
|
-
export
|
|
15
|
-
streamId = null;
|
|
16
|
-
dataHandler = null;
|
|
17
|
-
closeHandler = null;
|
|
18
|
-
errorHandler = null;
|
|
19
|
-
_connected = false;
|
|
20
|
-
get connected() { return this._connected; }
|
|
21
|
-
async connect(host, port, tls, _servername) {
|
|
22
|
-
this.streamId = await msgapi.tcp.connect(host, port, tls);
|
|
23
|
-
this._connected = true;
|
|
24
|
-
msgapi.tcp.onData(this.streamId, (data) => {
|
|
25
|
-
if (this.dataHandler)
|
|
26
|
-
this.dataHandler(data);
|
|
27
|
-
});
|
|
28
|
-
msgapi.tcp.onClose(this.streamId, (hadError) => {
|
|
29
|
-
this._connected = false;
|
|
30
|
-
if (this.closeHandler)
|
|
31
|
-
this.closeHandler(hadError);
|
|
32
|
-
});
|
|
33
|
-
msgapi.tcp.onError(this.streamId, (message) => {
|
|
34
|
-
if (this.errorHandler)
|
|
35
|
-
this.errorHandler(new Error(message));
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
async upgradeTLS(servername) {
|
|
39
|
-
if (this.streamId == null)
|
|
40
|
-
throw new Error("Not connected");
|
|
41
|
-
await msgapi.tcp.upgradeTLS(this.streamId, servername || "");
|
|
42
|
-
}
|
|
43
|
-
async write(data) {
|
|
44
|
-
if (this.streamId == null)
|
|
45
|
-
throw new Error("Not connected");
|
|
46
|
-
const str = typeof data === "string" ? data : new TextDecoder().decode(data);
|
|
47
|
-
await msgapi.tcp.write(this.streamId, str);
|
|
48
|
-
}
|
|
49
|
-
onData(handler) { this.dataHandler = handler; }
|
|
50
|
-
onClose(handler) { this.closeHandler = handler; }
|
|
51
|
-
onError(handler) { this.errorHandler = handler; }
|
|
52
|
-
close() {
|
|
53
|
-
this._connected = false;
|
|
54
|
-
if (this.streamId != null) {
|
|
55
|
-
msgapi.tcp.close(this.streamId);
|
|
56
|
-
this.streamId = null;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
6
|
+
export { BridgeTcpTransport as BridgeTransport } from "@bobfrankston/tcp-transport";
|
|
60
7
|
//# sourceMappingURL=bridge-transport.js.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FetchedMessage — NativeFetchedMessage plus parsed-header state and convenience getters.
|
|
3
|
+
*
|
|
4
|
+
* Inherits the NativeFetchedMessage shape (via `implements`) so a FetchedMessage
|
|
5
|
+
* is-a NativeFetchedMessage: it can be passed anywhere the native interface is
|
|
6
|
+
* expected. Callers that want the raw shape treat it as NativeFetchedMessage;
|
|
7
|
+
* callers that want extras read `.ymd`, `.listUnsubscribe`, etc. directly.
|
|
8
|
+
*/
|
|
9
|
+
import type { NativeFetchedMessage } from "./imap-native.js";
|
|
10
|
+
import type { AddressData } from "./imap-protocol.js";
|
|
11
|
+
/** Case-insensitive multi-value header map (one key may repeat). */
|
|
12
|
+
declare class HeaderMap {
|
|
13
|
+
private items;
|
|
14
|
+
add(key: string, value: string): void;
|
|
15
|
+
get(key: string): string[] | undefined;
|
|
16
|
+
}
|
|
17
|
+
export declare class FetchedMessage implements NativeFetchedMessage {
|
|
18
|
+
seq: number;
|
|
19
|
+
uid: number;
|
|
20
|
+
flags: Set<string>;
|
|
21
|
+
date: Date | null;
|
|
22
|
+
subject: string;
|
|
23
|
+
messageId: string;
|
|
24
|
+
from: AddressData[];
|
|
25
|
+
to: AddressData[];
|
|
26
|
+
cc: AddressData[];
|
|
27
|
+
bcc: AddressData[];
|
|
28
|
+
sender: AddressData[];
|
|
29
|
+
replyTo: AddressData[];
|
|
30
|
+
inReplyTo: string;
|
|
31
|
+
size: number;
|
|
32
|
+
source: string;
|
|
33
|
+
headers: string;
|
|
34
|
+
seen: boolean;
|
|
35
|
+
flagged: boolean;
|
|
36
|
+
answered: boolean;
|
|
37
|
+
draft: boolean;
|
|
38
|
+
parsedHeader: string[][];
|
|
39
|
+
headerSet: HeaderMap;
|
|
40
|
+
constructor(init: NativeFetchedMessage);
|
|
41
|
+
get unread(): boolean;
|
|
42
|
+
get deleted(): boolean;
|
|
43
|
+
get hasFlag(): boolean;
|
|
44
|
+
get ymd(): string | undefined;
|
|
45
|
+
/** Self-typed as a keyable record for dynamic address-field access by screening rules. */
|
|
46
|
+
get tcsb(): Record<string, AddressData[]>;
|
|
47
|
+
get listUnsubscribe(): string | undefined;
|
|
48
|
+
get xpriority(): string | undefined;
|
|
49
|
+
get importance(): string | undefined;
|
|
50
|
+
get deliveredTo(): string | undefined;
|
|
51
|
+
}
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=fetched-message.d.ts.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FetchedMessage — NativeFetchedMessage plus parsed-header state and convenience getters.
|
|
3
|
+
*
|
|
4
|
+
* Inherits the NativeFetchedMessage shape (via `implements`) so a FetchedMessage
|
|
5
|
+
* is-a NativeFetchedMessage: it can be passed anywhere the native interface is
|
|
6
|
+
* expected. Callers that want the raw shape treat it as NativeFetchedMessage;
|
|
7
|
+
* callers that want extras read `.ymd`, `.listUnsubscribe`, etc. directly.
|
|
8
|
+
*/
|
|
9
|
+
/** Case-insensitive multi-value header map (one key may repeat). */
|
|
10
|
+
class HeaderMap {
|
|
11
|
+
items = {};
|
|
12
|
+
add(key, value) {
|
|
13
|
+
const k = key.toLowerCase();
|
|
14
|
+
(this.items[k] ||= []).push(value);
|
|
15
|
+
}
|
|
16
|
+
get(key) {
|
|
17
|
+
return this.items[key.toLowerCase()];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class FetchedMessage {
|
|
21
|
+
// Native fields — populated by Object.assign in the constructor.
|
|
22
|
+
// `implements NativeFetchedMessage` forces this list to stay in sync.
|
|
23
|
+
seq;
|
|
24
|
+
uid;
|
|
25
|
+
flags;
|
|
26
|
+
date;
|
|
27
|
+
subject;
|
|
28
|
+
messageId;
|
|
29
|
+
from;
|
|
30
|
+
to;
|
|
31
|
+
cc;
|
|
32
|
+
bcc;
|
|
33
|
+
sender;
|
|
34
|
+
replyTo;
|
|
35
|
+
inReplyTo;
|
|
36
|
+
size;
|
|
37
|
+
source;
|
|
38
|
+
headers;
|
|
39
|
+
seen;
|
|
40
|
+
flagged;
|
|
41
|
+
answered;
|
|
42
|
+
draft;
|
|
43
|
+
// Parsed-header state
|
|
44
|
+
parsedHeader;
|
|
45
|
+
headerSet;
|
|
46
|
+
constructor(init) {
|
|
47
|
+
Object.assign(this, init);
|
|
48
|
+
// RFC 5322 unfold: a line starting with whitespace continues the prior one.
|
|
49
|
+
const unfolded = (init.headers || "").replace(/\r\n(\s)/g, "$1");
|
|
50
|
+
this.parsedHeader = unfolded
|
|
51
|
+
.split(/\r?\n/)
|
|
52
|
+
.filter(line => line.trim() !== "")
|
|
53
|
+
.map(line => {
|
|
54
|
+
const i = line.indexOf(":");
|
|
55
|
+
return i < 0 ? [line, ""] : [line.slice(0, i), line.slice(i + 1).trim()];
|
|
56
|
+
});
|
|
57
|
+
this.headerSet = new HeaderMap();
|
|
58
|
+
for (const [k, v] of this.parsedHeader)
|
|
59
|
+
this.headerSet.add(k, v);
|
|
60
|
+
}
|
|
61
|
+
// Flag convenience
|
|
62
|
+
get unread() { return !this.seen; }
|
|
63
|
+
get deleted() { return this.flags.has("\\Deleted"); }
|
|
64
|
+
get hasFlag() { return this.flags.size > 0; }
|
|
65
|
+
// Display
|
|
66
|
+
get ymd() { return this.date?.toLocaleDateString("se"); }
|
|
67
|
+
/** Self-typed as a keyable record for dynamic address-field access by screening rules. */
|
|
68
|
+
get tcsb() {
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
// Parsed-header lookups
|
|
72
|
+
get listUnsubscribe() { return this.headerSet.get("List-Unsubscribe")?.[0]; }
|
|
73
|
+
get xpriority() { return this.headerSet.get("X-Priority")?.[0]; }
|
|
74
|
+
get importance() { return this.headerSet.get("Importance")?.[0]; }
|
|
75
|
+
get deliveredTo() { return this.headerSet.get("Delivered-To")?.[0]; }
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=fetched-message.js.map
|
package/folder-tree.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Folder tree builder — converts a flat NativeFolder[] into a nested tree.
|
|
3
|
+
* Equivalent to imapflow's listTree(), but pure JS and transport-agnostic.
|
|
4
|
+
* Parents are derived from path + delimiter (NativeFolder has no pre-parsed parent[]).
|
|
5
|
+
*/
|
|
6
|
+
import type { NativeFolder } from "./imap-native.js";
|
|
7
|
+
export interface FolderTreeNode {
|
|
8
|
+
/** Root sentinel — only true on the returned top-level container */
|
|
9
|
+
root?: boolean;
|
|
10
|
+
/** Full mailbox path (empty string for root) */
|
|
11
|
+
path: string;
|
|
12
|
+
/** Leaf name (last segment of path) */
|
|
13
|
+
name: string;
|
|
14
|
+
/** Path delimiter from the server */
|
|
15
|
+
delimiter: string;
|
|
16
|
+
/** IMAP flags (e.g. "\\HasChildren", "\\Noselect", "\\Inbox") */
|
|
17
|
+
flags: string[];
|
|
18
|
+
/** Detected special-use tag (sent/trash/drafts/spam/archive/inbox), if known */
|
|
19
|
+
specialUse?: string;
|
|
20
|
+
/** True if the folder itself cannot be selected (e.g. "\\Noselect" flag) */
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
/** Child folders */
|
|
23
|
+
folders: FolderTreeNode[];
|
|
24
|
+
}
|
|
25
|
+
/** Build a nested tree from a flat folder list using path + delimiter. */
|
|
26
|
+
export declare function buildFolderTree(folders: NativeFolder[]): FolderTreeNode;
|
|
27
|
+
//# sourceMappingURL=folder-tree.d.ts.map
|
package/folder-tree.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Folder tree builder — converts a flat NativeFolder[] into a nested tree.
|
|
3
|
+
* Equivalent to imapflow's listTree(), but pure JS and transport-agnostic.
|
|
4
|
+
* Parents are derived from path + delimiter (NativeFolder has no pre-parsed parent[]).
|
|
5
|
+
*/
|
|
6
|
+
/** Build a nested tree from a flat folder list using path + delimiter. */
|
|
7
|
+
export function buildFolderTree(folders) {
|
|
8
|
+
const delimiter = folders[0]?.delimiter || ".";
|
|
9
|
+
const root = { root: true, path: "", name: "", delimiter, flags: [], folders: [] };
|
|
10
|
+
const byPath = new Map();
|
|
11
|
+
byPath.set("", root);
|
|
12
|
+
// Sort by path so parents are created before children
|
|
13
|
+
const sorted = [...folders].sort((a, b) => a.path.localeCompare(b.path));
|
|
14
|
+
for (const f of sorted) {
|
|
15
|
+
const delim = f.delimiter || delimiter;
|
|
16
|
+
const parts = f.path.split(delim);
|
|
17
|
+
const name = parts[parts.length - 1];
|
|
18
|
+
const node = {
|
|
19
|
+
path: f.path,
|
|
20
|
+
name,
|
|
21
|
+
delimiter: delim,
|
|
22
|
+
flags: f.flags,
|
|
23
|
+
folders: [],
|
|
24
|
+
};
|
|
25
|
+
if (f.flags.some(fl => fl.toLowerCase() === "\\noselect"))
|
|
26
|
+
node.disabled = true;
|
|
27
|
+
// Find or create parent chain (handles gaps where an intermediate LIST entry is missing)
|
|
28
|
+
let parentPath = "";
|
|
29
|
+
let parent = root;
|
|
30
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
31
|
+
parentPath = parentPath ? parentPath + delim + parts[i] : parts[i];
|
|
32
|
+
let p = byPath.get(parentPath);
|
|
33
|
+
if (!p) {
|
|
34
|
+
p = { path: parentPath, name: parts[i], delimiter: delim, flags: [], folders: [], disabled: true };
|
|
35
|
+
byPath.set(parentPath, p);
|
|
36
|
+
parent.folders.push(p);
|
|
37
|
+
}
|
|
38
|
+
parent = p;
|
|
39
|
+
}
|
|
40
|
+
const existing = byPath.get(f.path);
|
|
41
|
+
if (existing) {
|
|
42
|
+
// Fill in a placeholder that was created for a descendant
|
|
43
|
+
existing.flags = f.flags;
|
|
44
|
+
existing.name = name;
|
|
45
|
+
existing.delimiter = delim;
|
|
46
|
+
delete existing.disabled;
|
|
47
|
+
if (f.flags.some(fl => fl.toLowerCase() === "\\noselect"))
|
|
48
|
+
existing.disabled = true;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
byPath.set(f.path, node);
|
|
52
|
+
parent.folders.push(node);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return root;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=folder-tree.js.map
|
package/imap-compat.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Each method opens the mailbox, does the operation, and returns. No persistent state.
|
|
5
5
|
*/
|
|
6
6
|
import { type NativeFolder } from "./imap-native.js";
|
|
7
|
+
import { FetchedMessage } from "./fetched-message.js";
|
|
7
8
|
import type { ImapClientConfig } from "./types.js";
|
|
8
9
|
import type { TransportFactory } from "./transport.js";
|
|
9
10
|
/** Special folder detection result */
|
|
@@ -36,15 +37,15 @@ export declare class CompatImapClient {
|
|
|
36
37
|
/** Fetch messages since a UID in a mailbox */
|
|
37
38
|
fetchMessagesSinceUid(mailbox: string, sinceUid: number, options?: {
|
|
38
39
|
source?: boolean;
|
|
39
|
-
}): Promise<
|
|
40
|
+
}): Promise<FetchedMessage[]>;
|
|
40
41
|
/** Fetch messages by date range. Optional onChunk callback for incremental processing. */
|
|
41
42
|
fetchMessageByDate(mailbox: string, start: Date, end?: Date, options?: {
|
|
42
43
|
source?: boolean;
|
|
43
|
-
}, onChunk?: (msgs:
|
|
44
|
+
}, onChunk?: (msgs: FetchedMessage[]) => void): Promise<FetchedMessage[]>;
|
|
44
45
|
/** Fetch a single message by UID */
|
|
45
46
|
fetchMessageByUid(mailbox: string, uid: number, options?: {
|
|
46
47
|
source?: boolean;
|
|
47
|
-
}): Promise<
|
|
48
|
+
}): Promise<FetchedMessage | null>;
|
|
48
49
|
/** Get message count via STATUS (does not require SELECT) */
|
|
49
50
|
getMessagesCount(mailbox: string): Promise<number>;
|
|
50
51
|
/** Get all UIDs in a mailbox */
|
|
@@ -59,10 +60,10 @@ export declare class CompatImapClient {
|
|
|
59
60
|
* used by the puller. */
|
|
60
61
|
fetchMessages(mailbox: string, uidRange: string, options?: {
|
|
61
62
|
source?: boolean;
|
|
62
|
-
}): Promise<
|
|
63
|
+
}): Promise<FetchedMessage[]>;
|
|
63
64
|
fetchMessages(mailbox: string, end: number, count: number, options?: {
|
|
64
65
|
source?: boolean;
|
|
65
|
-
}): Promise<
|
|
66
|
+
}): Promise<FetchedMessage[]>;
|
|
66
67
|
/** Search messages in a mailbox */
|
|
67
68
|
searchMessages(mailbox: string, criteria: any): Promise<number[]>;
|
|
68
69
|
/** Search by header value — returns matching UIDs */
|
package/imap-compat.js
CHANGED
|
@@ -4,44 +4,7 @@
|
|
|
4
4
|
* Each method opens the mailbox, does the operation, and returns. No persistent state.
|
|
5
5
|
*/
|
|
6
6
|
import { NativeImapClient } from "./imap-native.js";
|
|
7
|
-
|
|
8
|
-
function toCompatMessage(msg) {
|
|
9
|
-
return {
|
|
10
|
-
uid: msg.uid,
|
|
11
|
-
seq: msg.seq,
|
|
12
|
-
flags: msg.flags,
|
|
13
|
-
seen: msg.seen,
|
|
14
|
-
flagged: msg.flagged,
|
|
15
|
-
answered: msg.answered,
|
|
16
|
-
draft: msg.draft,
|
|
17
|
-
date: msg.date,
|
|
18
|
-
subject: msg.subject,
|
|
19
|
-
messageId: msg.messageId,
|
|
20
|
-
from: msg.from,
|
|
21
|
-
to: msg.to,
|
|
22
|
-
cc: msg.cc,
|
|
23
|
-
bcc: msg.bcc,
|
|
24
|
-
sender: msg.sender,
|
|
25
|
-
replyTo: msg.replyTo,
|
|
26
|
-
inReplyTo: msg.inReplyTo,
|
|
27
|
-
size: msg.size,
|
|
28
|
-
source: msg.source,
|
|
29
|
-
headers: msg.headers,
|
|
30
|
-
// Compatibility: old FetchedMessage had envelope sub-object, but mailx-imap accesses directly
|
|
31
|
-
envelope: {
|
|
32
|
-
date: msg.date,
|
|
33
|
-
subject: msg.subject,
|
|
34
|
-
messageId: msg.messageId,
|
|
35
|
-
from: msg.from,
|
|
36
|
-
to: msg.to,
|
|
37
|
-
cc: msg.cc,
|
|
38
|
-
bcc: msg.bcc,
|
|
39
|
-
sender: msg.sender,
|
|
40
|
-
replyTo: msg.replyTo,
|
|
41
|
-
inReplyTo: msg.inReplyTo,
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
}
|
|
7
|
+
import { FetchedMessage } from "./fetched-message.js";
|
|
45
8
|
/**
|
|
46
9
|
* Compatibility IMAP client — wraps NativeImapClient with the old ImapClient API.
|
|
47
10
|
* Each method creates its own select/operation/close cycle.
|
|
@@ -128,16 +91,16 @@ export class CompatImapClient {
|
|
|
128
91
|
await this.native.select(mailbox);
|
|
129
92
|
const msgs = await this.native.fetchSinceUid(sinceUid, options);
|
|
130
93
|
await this.native.closeMailbox();
|
|
131
|
-
return msgs.map(
|
|
94
|
+
return msgs.map(m => new FetchedMessage(m));
|
|
132
95
|
}
|
|
133
96
|
/** Fetch messages by date range. Optional onChunk callback for incremental processing. */
|
|
134
97
|
async fetchMessageByDate(mailbox, start, end, options, onChunk) {
|
|
135
98
|
await this.ensureConnected();
|
|
136
99
|
await this.native.select(mailbox);
|
|
137
|
-
const chunkCb = onChunk ? (raw) => onChunk(raw.map(
|
|
100
|
+
const chunkCb = onChunk ? (raw) => onChunk(raw.map(m => new FetchedMessage(m))) : undefined;
|
|
138
101
|
const msgs = await this.native.fetchByDate(start, end, options, chunkCb);
|
|
139
102
|
await this.native.closeMailbox();
|
|
140
|
-
return msgs.map(
|
|
103
|
+
return msgs.map(m => new FetchedMessage(m));
|
|
141
104
|
}
|
|
142
105
|
/** Fetch a single message by UID */
|
|
143
106
|
async fetchMessageByUid(mailbox, uid, options) {
|
|
@@ -145,7 +108,7 @@ export class CompatImapClient {
|
|
|
145
108
|
await this.native.select(mailbox);
|
|
146
109
|
const msg = await this.native.fetchMessage(uid, options);
|
|
147
110
|
await this.native.closeMailbox();
|
|
148
|
-
return msg ?
|
|
111
|
+
return msg ? new FetchedMessage(msg) : null;
|
|
149
112
|
}
|
|
150
113
|
/** Get message count via STATUS (does not require SELECT) */
|
|
151
114
|
async getMessagesCount(mailbox) {
|
|
@@ -180,7 +143,7 @@ export class CompatImapClient {
|
|
|
180
143
|
await this.native.select(mailbox);
|
|
181
144
|
const msgs = await this.native.fetchMessages(range, options);
|
|
182
145
|
await this.native.closeMailbox();
|
|
183
|
-
return msgs.map(
|
|
146
|
+
return msgs.map(m => new FetchedMessage(m));
|
|
184
147
|
}
|
|
185
148
|
/** Search messages in a mailbox */
|
|
186
149
|
async searchMessages(mailbox, criteria) {
|
package/index.d.ts
CHANGED
|
@@ -14,5 +14,8 @@ export { NativeImapClient } from "./imap-native.js";
|
|
|
14
14
|
export type { NativeFetchedMessage, NativeFolder, MailboxInfo } from "./imap-native.js";
|
|
15
15
|
export { CompatImapClient } from "./imap-compat.js";
|
|
16
16
|
export type { SpecialFolders } from "./imap-compat.js";
|
|
17
|
+
export { FetchedMessage } from "./fetched-message.js";
|
|
18
|
+
export { buildFolderTree } from "./folder-tree.js";
|
|
19
|
+
export type { FolderTreeNode } from "./folder-tree.js";
|
|
17
20
|
export { isGmailUser, isGmailServer, createGmailConfig, createAutoImapConfig, type GmailOAuthConfig, } from "./gmail.js";
|
|
18
21
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -13,6 +13,10 @@ export * as proto from "./imap-protocol.js";
|
|
|
13
13
|
export { NativeImapClient } from "./imap-native.js";
|
|
14
14
|
// Compatibility wrapper (old ImapClient API shape)
|
|
15
15
|
export { CompatImapClient } from "./imap-compat.js";
|
|
16
|
+
// Rich message wrapper (ported from @bobfrankston/iflow — parses headers, exposes ymd/tcsb/etc.)
|
|
17
|
+
export { FetchedMessage } from "./fetched-message.js";
|
|
18
|
+
// Folder tree builder (equivalent to imapflow.listTree, pure JS)
|
|
19
|
+
export { buildFolderTree } from "./folder-tree.js";
|
|
16
20
|
// Gmail OAuth support (Node-free — caller provides tokenProvider)
|
|
17
21
|
export { isGmailUser, isGmailServer, createGmailConfig, createAutoImapConfig, } from "./gmail.js";
|
|
18
22
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/iflow-direct",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "Direct IMAP client — transport-agnostic, no Node.js dependencies, browser-ready",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.ts",
|
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
],
|
|
19
19
|
"author": "Bob Frankston",
|
|
20
20
|
"license": "ISC",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@bobfrankston/tcp-transport": "^0.1.0"
|
|
23
|
+
},
|
|
21
24
|
"exports": {
|
|
22
25
|
".": {
|
|
23
26
|
"types": "./index.ts",
|
|
@@ -38,5 +41,13 @@
|
|
|
38
41
|
"repository": {
|
|
39
42
|
"type": "git",
|
|
40
43
|
"url": "https://github.com/BobFrankston/iflow-direct.git"
|
|
44
|
+
},
|
|
45
|
+
".dependencies": {
|
|
46
|
+
"@bobfrankston/tcp-transport": "file:../tcp-transport"
|
|
47
|
+
},
|
|
48
|
+
".transformedSnapshot": {
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@bobfrankston/tcp-transport": "^0.1.0"
|
|
51
|
+
}
|
|
41
52
|
}
|
|
42
53
|
}
|
package/transport.d.ts
CHANGED
|
@@ -1,25 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Back-compat re-export. The TCP transport interface lives in
|
|
3
|
+
* @bobfrankston/tcp-transport — it's protocol-agnostic, used by SMTP, IMAP,
|
|
4
|
+
* and any other line-oriented TCP code.
|
|
5
|
+
*
|
|
6
|
+
* `ImapTransport` is kept as an alias for `TcpTransport` so existing callers
|
|
7
|
+
* continue to compile. New code should import `TcpTransport` directly from
|
|
8
|
+
* @bobfrankston/tcp-transport.
|
|
4
9
|
*/
|
|
5
|
-
export
|
|
6
|
-
/** Connect to host:port. If tls=true, connect with TLS directly (port 993). */
|
|
7
|
-
connect(host: string, port: number, tls: boolean, servername?: string): Promise<void>;
|
|
8
|
-
/** Upgrade an existing plaintext connection to TLS (STARTTLS). */
|
|
9
|
-
upgradeTLS(servername?: string): Promise<void>;
|
|
10
|
-
/** Write data to the connection. */
|
|
11
|
-
write(data: string | Uint8Array): Promise<void>;
|
|
12
|
-
/** Register a handler for incoming data. */
|
|
13
|
-
onData(handler: (data: string) => void): void;
|
|
14
|
-
/** Register a handler for connection close. */
|
|
15
|
-
onClose(handler: (hadError: boolean) => void): void;
|
|
16
|
-
/** Register a handler for errors. */
|
|
17
|
-
onError(handler: (err: Error) => void): void;
|
|
18
|
-
/** Close the connection. */
|
|
19
|
-
close(): void;
|
|
20
|
-
/** Whether the connection is active. */
|
|
21
|
-
readonly connected: boolean;
|
|
22
|
-
}
|
|
23
|
-
/** Factory function type for creating transports */
|
|
24
|
-
export type TransportFactory = () => ImapTransport;
|
|
10
|
+
export type { TcpTransport as ImapTransport, TransportFactory } from "@bobfrankston/tcp-transport";
|
|
25
11
|
//# sourceMappingURL=transport.d.ts.map
|
package/transport.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Back-compat re-export. The TCP transport interface lives in
|
|
3
|
+
* @bobfrankston/tcp-transport — it's protocol-agnostic, used by SMTP, IMAP,
|
|
4
|
+
* and any other line-oriented TCP code.
|
|
5
|
+
*
|
|
6
|
+
* `ImapTransport` is kept as an alias for `TcpTransport` so existing callers
|
|
7
|
+
* continue to compile. New code should import `TcpTransport` directly from
|
|
8
|
+
* @bobfrankston/tcp-transport.
|
|
4
9
|
*/
|
|
5
10
|
export {};
|
|
6
11
|
//# sourceMappingURL=transport.js.map
|