@remit/mailbox-service 0.0.114 → 0.0.116
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/dist/connection-factory.d.ts +8 -34
- package/dist/connection-factory.js +29 -35
- package/dist/connection-test.d.ts +3 -34
- package/dist/connection-test.js +14 -147
- package/dist/imapflow-connection.d.ts +6 -18
- package/dist/imapflow-connection.js +36 -48
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/message-sync.d.ts +4 -5
- package/dist/message-sync.js +8 -8
- package/dist/node-transport.d.ts +2 -0
- package/dist/node-transport.js +8 -0
- package/dist/test-helpers/fake-imap.d.ts +2 -0
- package/dist/test-helpers/fake-imap.js +13 -4
- package/dist/test-helpers/test-store.js +1 -1
- package/dist/types.d.ts +4 -3
- package/package.json +6 -4
|
@@ -3,52 +3,26 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Uses ImapFlow for all IMAP connections.
|
|
5
5
|
*/
|
|
6
|
-
import type { IImapConnection, ImapConnectionConfig, MailCredentials } from "./types.js";
|
|
6
|
+
import type { IImapConnection, ImapConnectionConfig, ImapTransport, MailCredentials } from "./types.js";
|
|
7
7
|
/**
|
|
8
8
|
* Create an IMAP connection
|
|
9
9
|
*/
|
|
10
10
|
export declare const createConnection: (config: ImapConnectionConfig) => IImapConnection;
|
|
11
|
-
/**
|
|
12
|
-
* Create an IMAP connection from account data using password credentials.
|
|
13
|
-
*/
|
|
14
|
-
export declare const createConnectionFromAccount: (account: {
|
|
15
|
-
imapHost: string;
|
|
16
|
-
imapPort: number;
|
|
17
|
-
imapTls: boolean;
|
|
18
|
-
username: string;
|
|
19
|
-
}, password: string) => IImapConnection;
|
|
20
11
|
/**
|
|
21
12
|
* Create an IMAP connection from account data using a MailCredentials union.
|
|
22
|
-
* Use this instead of createConnectionFromAccount for OAuth accounts.
|
|
23
13
|
*/
|
|
24
14
|
export declare const createConnectionWithCredentials: (account: {
|
|
25
15
|
imapHost: string;
|
|
26
16
|
imapPort: number;
|
|
27
17
|
imapTls: boolean;
|
|
28
18
|
username: string;
|
|
29
|
-
}, credentials: MailCredentials) => IImapConnection;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
* The factory handles connection lifecycle - callers should not disconnect.
|
|
34
|
-
* Call close() on the factory to disconnect when done.
|
|
35
|
-
*/
|
|
36
|
-
export interface ManagedConnectionFactory {
|
|
37
|
-
/** Get the cached connection (creates on first call) */
|
|
38
|
-
getConnection(): IImapConnection;
|
|
39
|
-
/** Disconnect and cleanup */
|
|
40
|
-
close(): Promise<void>;
|
|
19
|
+
}, credentials: MailCredentials, transport: ImapTransport) => IImapConnection;
|
|
20
|
+
export interface ConnectionScope {
|
|
21
|
+
getConnection: () => Promise<IImapConnection>;
|
|
22
|
+
disconnect: () => Promise<void>;
|
|
41
23
|
}
|
|
42
24
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* @example
|
|
46
|
-
* ```typescript
|
|
47
|
-
* const factory = createManagedConnectionFactory(config);
|
|
48
|
-
* const conn = factory.getConnection();
|
|
49
|
-
* await conn.connect();
|
|
50
|
-
* // ... reuse conn via factory.getConnection() ...
|
|
51
|
-
* await factory.close(); // Disconnect when done
|
|
52
|
-
* ```
|
|
25
|
+
* A lazily-connected, cached connection shared across the operations of one
|
|
26
|
+
* unit of work. Call disconnect() when done.
|
|
53
27
|
*/
|
|
54
|
-
export declare const
|
|
28
|
+
export declare const connectionScope: (config: ImapConnectionConfig) => ConnectionScope;
|
|
@@ -3,52 +3,46 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Uses ImapFlow for all IMAP connections.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { ImapFlowConnection } from "./imapflow-connection.js";
|
|
7
7
|
/**
|
|
8
8
|
* Create an IMAP connection
|
|
9
9
|
*/
|
|
10
10
|
export const createConnection = (config) => {
|
|
11
11
|
return new ImapFlowConnection(config);
|
|
12
12
|
};
|
|
13
|
-
/**
|
|
14
|
-
* Create an IMAP connection from account data using password credentials.
|
|
15
|
-
*/
|
|
16
|
-
export const createConnectionFromAccount = (account, password) => {
|
|
17
|
-
return createImapFlowConnectionFromAccount(account, password);
|
|
18
|
-
};
|
|
19
13
|
/**
|
|
20
14
|
* Create an IMAP connection from account data using a MailCredentials union.
|
|
21
|
-
* Use this instead of createConnectionFromAccount for OAuth accounts.
|
|
22
15
|
*/
|
|
23
|
-
export const createConnectionWithCredentials = (account, credentials) => {
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
export const createConnectionWithCredentials = (account, credentials, transport) => new ImapFlowConnection({
|
|
17
|
+
host: account.imapHost,
|
|
18
|
+
port: account.imapPort,
|
|
19
|
+
tls: account.imapTls,
|
|
20
|
+
transport,
|
|
21
|
+
user: account.username,
|
|
22
|
+
credentials,
|
|
23
|
+
});
|
|
26
24
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* ```typescript
|
|
31
|
-
* const factory = createManagedConnectionFactory(config);
|
|
32
|
-
* const conn = factory.getConnection();
|
|
33
|
-
* await conn.connect();
|
|
34
|
-
* // ... reuse conn via factory.getConnection() ...
|
|
35
|
-
* await factory.close(); // Disconnect when done
|
|
36
|
-
* ```
|
|
25
|
+
* A lazily-connected, cached connection shared across the operations of one
|
|
26
|
+
* unit of work. Call disconnect() when done.
|
|
37
27
|
*/
|
|
38
|
-
export const
|
|
28
|
+
export const connectionScope = (config) => {
|
|
39
29
|
let connection = null;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
30
|
+
let connectPromise = null;
|
|
31
|
+
const getConnection = async () => {
|
|
32
|
+
if (connectPromise) {
|
|
33
|
+
return connectPromise;
|
|
34
|
+
}
|
|
35
|
+
const conn = createConnection(config);
|
|
36
|
+
connection = conn;
|
|
37
|
+
connectPromise = conn.connect().then(() => conn);
|
|
38
|
+
return connectPromise;
|
|
39
|
+
};
|
|
40
|
+
const disconnect = async () => {
|
|
41
|
+
if (connection) {
|
|
42
|
+
await connection.disconnect();
|
|
43
|
+
connection = null;
|
|
44
|
+
connectPromise = null;
|
|
45
|
+
}
|
|
53
46
|
};
|
|
47
|
+
return { getConnection, disconnect };
|
|
54
48
|
};
|
|
@@ -1,41 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Used to validate credentials before saving account configuration.
|
|
5
|
-
*/
|
|
6
|
-
import type { MailCredentials } from "./types.js";
|
|
7
|
-
export interface TestResult {
|
|
8
|
-
success: boolean;
|
|
9
|
-
error?: string;
|
|
10
|
-
}
|
|
1
|
+
import type { TestResult } from "@remit/data-ports";
|
|
2
|
+
import type { ImapTransport, MailCredentials } from "./types.js";
|
|
11
3
|
export interface ImapTestConfig {
|
|
12
|
-
host: string;
|
|
13
|
-
port: number;
|
|
14
|
-
secure: boolean;
|
|
15
|
-
startTls?: boolean;
|
|
16
|
-
credentials: MailCredentials;
|
|
17
|
-
user: string;
|
|
18
|
-
}
|
|
19
|
-
export interface SmtpTestConfig {
|
|
20
4
|
host: string;
|
|
21
5
|
port: number;
|
|
22
6
|
secure: boolean;
|
|
23
7
|
credentials: MailCredentials;
|
|
24
8
|
user: string;
|
|
25
9
|
}
|
|
26
|
-
|
|
27
|
-
* Test IMAP connection with provided credentials.
|
|
28
|
-
*
|
|
29
|
-
* Uses a raw socket to send AUTH PLAIN directly — bypasses the CAPABILITY
|
|
30
|
-
* round-trip that IMAP clients typically perform first, avoiding deadlocks
|
|
31
|
-
* with servers that pipeline their tagged responses.
|
|
32
|
-
*
|
|
33
|
-
* Supports plaintext, direct TLS (secure: true), and STARTTLS (startTls: true).
|
|
34
|
-
*/
|
|
35
|
-
export declare const testImapConnection: (config: ImapTestConfig) => Promise<TestResult>;
|
|
36
|
-
/**
|
|
37
|
-
* Test SMTP connection with provided credentials
|
|
38
|
-
*
|
|
39
|
-
* Uses nodemailer's verify() method to test authentication.
|
|
40
|
-
*/
|
|
41
|
-
export declare const testSmtpConnection: (config: SmtpTestConfig) => Promise<TestResult>;
|
|
10
|
+
export declare const testImapConnection: (config: ImapTestConfig, transport: ImapTransport) => Promise<TestResult>;
|
package/dist/connection-test.js
CHANGED
|
@@ -1,152 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
* Connection testing utilities for IMAP and SMTP servers
|
|
3
|
-
*
|
|
4
|
-
* Used to validate credentials before saving account configuration.
|
|
5
|
-
*/
|
|
6
|
-
import { createConnection as createNetConnection } from "node:net";
|
|
7
|
-
import { connect as tlsConnect } from "node:tls";
|
|
8
|
-
import { createTransport } from "nodemailer";
|
|
9
|
-
import { MailConnectionError } from "./types.js";
|
|
10
|
-
// TODO(#468): Wire OAuth testConnection once authType/oauthRefreshTokenHash are
|
|
11
|
-
// available on Account. For OAuth accounts, mint a token via MailOAuthService
|
|
12
|
-
// before calling testImapConnection / testSmtpConnection, and map
|
|
13
|
-
// RefreshTokenError(reauth-required) to MailConnectionError("auth", ...).
|
|
1
|
+
import { ImapFlowConnection } from "./imapflow-connection.js";
|
|
14
2
|
const TEST_TIMEOUT_MS = 12_000;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
*/
|
|
18
|
-
const buildSmtpAuth = (user, credentials) => {
|
|
19
|
-
if (credentials.kind === "password") {
|
|
20
|
-
return { user, pass: credentials.password };
|
|
21
|
-
}
|
|
22
|
-
return {
|
|
23
|
-
type: "OAuth2",
|
|
24
|
-
user,
|
|
25
|
-
accessToken: credentials.accessToken,
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
/**
|
|
29
|
-
* Test IMAP connection with provided credentials.
|
|
30
|
-
*
|
|
31
|
-
* Uses a raw socket to send AUTH PLAIN directly — bypasses the CAPABILITY
|
|
32
|
-
* round-trip that IMAP clients typically perform first, avoiding deadlocks
|
|
33
|
-
* with servers that pipeline their tagged responses.
|
|
34
|
-
*
|
|
35
|
-
* Supports plaintext, direct TLS (secure: true), and STARTTLS (startTls: true).
|
|
36
|
-
*/
|
|
37
|
-
export const testImapConnection = async (config) => {
|
|
38
|
-
if (config.credentials.kind !== "password") {
|
|
39
|
-
return { success: false, error: "OAuth not supported for connection test" };
|
|
40
|
-
}
|
|
41
|
-
const { user, credentials } = config;
|
|
42
|
-
const authPlain = Buffer.from(`\x00${user}\x00${credentials.password}`).toString("base64");
|
|
43
|
-
return new Promise((resolve) => {
|
|
44
|
-
let settled = false;
|
|
45
|
-
const done = (result) => {
|
|
46
|
-
if (settled)
|
|
47
|
-
return;
|
|
48
|
-
settled = true;
|
|
49
|
-
clearTimeout(timeoutId);
|
|
50
|
-
resolve(result);
|
|
51
|
-
};
|
|
52
|
-
const timeoutId = setTimeout(() => done({ success: false, error: "Connection timed out" }), TEST_TIMEOUT_MS);
|
|
53
|
-
let buf = "";
|
|
54
|
-
let phase = "greeting";
|
|
55
|
-
// biome-ignore lint/suspicious/noExplicitAny: socket type varies between net.Socket and tls.TLSSocket
|
|
56
|
-
let sock;
|
|
57
|
-
const onData = (chunk) => {
|
|
58
|
-
buf += chunk.toString();
|
|
59
|
-
const lines = buf.split("\r\n");
|
|
60
|
-
buf = lines.pop() ?? "";
|
|
61
|
-
for (const line of lines) {
|
|
62
|
-
if (phase === "greeting" && /^\* OK/i.test(line)) {
|
|
63
|
-
if (config.startTls) {
|
|
64
|
-
phase = "starttls";
|
|
65
|
-
sock.write("A001 STARTTLS\r\n");
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
phase = "auth";
|
|
69
|
-
sock.write(`A001 AUTHENTICATE PLAIN ${authPlain}\r\n`);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
else if (phase === "starttls" && /^A001 OK/i.test(line)) {
|
|
73
|
-
phase = "tls-upgrade";
|
|
74
|
-
// Upgrade socket to TLS in place
|
|
75
|
-
const tlsSock = tlsConnect({
|
|
76
|
-
socket: sock,
|
|
77
|
-
host: config.host,
|
|
78
|
-
rejectUnauthorized: false,
|
|
79
|
-
});
|
|
80
|
-
tlsSock.on("data", onData);
|
|
81
|
-
tlsSock.on("error", onError);
|
|
82
|
-
tlsSock.once("secureConnect", () => {
|
|
83
|
-
phase = "auth";
|
|
84
|
-
sock = tlsSock;
|
|
85
|
-
sock.write(`A002 AUTHENTICATE PLAIN ${authPlain}\r\n`);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
else if (phase === "auth" &&
|
|
89
|
-
(/^A00[12] OK/i.test(line) || /^A001 OK/i.test(line))) {
|
|
90
|
-
phase = "done";
|
|
91
|
-
sock.write("A099 LOGOUT\r\n");
|
|
92
|
-
done({ success: true });
|
|
93
|
-
}
|
|
94
|
-
else if (phase === "auth" &&
|
|
95
|
-
(/^A00[12] NO/i.test(line) || /^A00[12] BAD/i.test(line))) {
|
|
96
|
-
phase = "done";
|
|
97
|
-
sock.destroy();
|
|
98
|
-
done({ success: false, error: "Authentication failed" });
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
const onError = (err) => {
|
|
103
|
-
done({ success: false, error: err.message });
|
|
104
|
-
};
|
|
105
|
-
if (config.secure) {
|
|
106
|
-
sock = tlsConnect({
|
|
107
|
-
host: config.host,
|
|
108
|
-
port: config.port,
|
|
109
|
-
rejectUnauthorized: false,
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
sock = createNetConnection({ host: config.host, port: config.port });
|
|
114
|
-
}
|
|
115
|
-
sock.on("data", onData);
|
|
116
|
-
sock.on("error", onError);
|
|
117
|
-
});
|
|
118
|
-
};
|
|
119
|
-
/**
|
|
120
|
-
* Test SMTP connection with provided credentials
|
|
121
|
-
*
|
|
122
|
-
* Uses nodemailer's verify() method to test authentication.
|
|
123
|
-
*/
|
|
124
|
-
export const testSmtpConnection = async (config) => {
|
|
125
|
-
const transport = createTransport({
|
|
3
|
+
export const testImapConnection = async (config, transport) => {
|
|
4
|
+
const connection = new ImapFlowConnection({
|
|
126
5
|
host: config.host,
|
|
127
6
|
port: config.port,
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
7
|
+
tls: config.secure,
|
|
8
|
+
user: config.user,
|
|
9
|
+
credentials: config.credentials,
|
|
10
|
+
transport,
|
|
11
|
+
connTimeout: TEST_TIMEOUT_MS,
|
|
133
12
|
});
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
return {
|
|
142
|
-
success: false,
|
|
143
|
-
error: error instanceof Error ? error.message : "Connection failed",
|
|
144
|
-
};
|
|
145
|
-
})
|
|
146
|
-
.finally(() => transport.close());
|
|
147
|
-
const timeout = new Promise((resolve) => setTimeout(() => {
|
|
148
|
-
transport.close();
|
|
149
|
-
resolve({ success: false, error: "Connection timed out" });
|
|
150
|
-
}, TEST_TIMEOUT_MS));
|
|
151
|
-
return Promise.race([attempt, timeout]);
|
|
13
|
+
const result = await connection.connectOnce().then(() => ({ success: true }), (error) => ({
|
|
14
|
+
success: false,
|
|
15
|
+
error: error instanceof Error ? error.message : "Connection failed",
|
|
16
|
+
}));
|
|
17
|
+
await connection.disconnect();
|
|
18
|
+
return result;
|
|
152
19
|
};
|
|
@@ -62,6 +62,7 @@ export declare class ImapFlowConnection {
|
|
|
62
62
|
private _state;
|
|
63
63
|
private config;
|
|
64
64
|
private currentMailbox;
|
|
65
|
+
private currentMailboxReadOnly;
|
|
65
66
|
constructor(config: ImapConnectionConfig);
|
|
66
67
|
/**
|
|
67
68
|
* Current connection state
|
|
@@ -78,6 +79,11 @@ export declare class ImapFlowConnection {
|
|
|
78
79
|
* Authentication errors are not retried.
|
|
79
80
|
*/
|
|
80
81
|
connect: () => Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Connect once, without retrying. A failure is classified into a
|
|
84
|
+
* MailConnectionError where possible and leaves the connection closed.
|
|
85
|
+
*/
|
|
86
|
+
connectOnce: () => Promise<void>;
|
|
81
87
|
/**
|
|
82
88
|
* Single connection attempt
|
|
83
89
|
*/
|
|
@@ -357,21 +363,3 @@ export declare class ImapFlowConnection {
|
|
|
357
363
|
*/
|
|
358
364
|
private ensureConnected;
|
|
359
365
|
}
|
|
360
|
-
/**
|
|
361
|
-
* Create an ImapFlow connection from account data using password credentials.
|
|
362
|
-
*/
|
|
363
|
-
export declare const createImapFlowConnectionFromAccount: (account: {
|
|
364
|
-
imapHost: string;
|
|
365
|
-
imapPort: number;
|
|
366
|
-
imapTls: boolean;
|
|
367
|
-
username: string;
|
|
368
|
-
}, password: string) => ImapFlowConnection;
|
|
369
|
-
/**
|
|
370
|
-
* Create an ImapFlow connection using a credentials union (password or OAuth access token).
|
|
371
|
-
*/
|
|
372
|
-
export declare const createImapFlowConnectionWithCredentials: (account: {
|
|
373
|
-
imapHost: string;
|
|
374
|
-
imapPort: number;
|
|
375
|
-
imapTls: boolean;
|
|
376
|
-
username: string;
|
|
377
|
-
}, credentials: import("./types.js").MailCredentials) => ImapFlowConnection;
|
|
@@ -149,6 +149,7 @@ export class ImapFlowConnection {
|
|
|
149
149
|
_state = "disconnected";
|
|
150
150
|
config;
|
|
151
151
|
currentMailbox = null;
|
|
152
|
+
currentMailboxReadOnly = true;
|
|
152
153
|
constructor(config) {
|
|
153
154
|
this.config = config;
|
|
154
155
|
}
|
|
@@ -171,35 +172,35 @@ export class ImapFlowConnection {
|
|
|
171
172
|
* Authentication errors are not retried.
|
|
172
173
|
*/
|
|
173
174
|
connect = async () => {
|
|
174
|
-
if (this.client) {
|
|
175
|
-
throw new Error("Already connected");
|
|
176
|
-
}
|
|
177
175
|
const maxRetries = 3;
|
|
178
176
|
const baseDelayMs = 1000;
|
|
179
177
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
180
|
-
|
|
181
|
-
|
|
178
|
+
const failure = await this.connectOnce().then(() => null, (error) => error);
|
|
179
|
+
if (failure === null) {
|
|
182
180
|
return;
|
|
183
181
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
// Final attempt exhausted: throw the classified error if we have
|
|
191
|
-
// one (network failures), otherwise let the raw error bubble.
|
|
192
|
-
if (attempt === maxRetries) {
|
|
193
|
-
throw classified ?? error;
|
|
194
|
-
}
|
|
195
|
-
// Network and unknown errors fall through to retry-with-backoff.
|
|
196
|
-
// Exponential backoff: 1s, 2s, 4s
|
|
197
|
-
const delay = baseDelayMs * 2 ** (attempt - 1);
|
|
198
|
-
await this.sleep(delay);
|
|
199
|
-
// Reset state for retry
|
|
200
|
-
this.cleanup();
|
|
182
|
+
if (failure instanceof MailConnectionError && failure.kind === "auth") {
|
|
183
|
+
throw failure;
|
|
184
|
+
}
|
|
185
|
+
if (attempt === maxRetries) {
|
|
186
|
+
throw failure;
|
|
201
187
|
}
|
|
188
|
+
await this.sleep(baseDelayMs * 2 ** (attempt - 1));
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Connect once, without retrying. A failure is classified into a
|
|
193
|
+
* MailConnectionError where possible and leaves the connection closed.
|
|
194
|
+
*/
|
|
195
|
+
connectOnce = async () => {
|
|
196
|
+
if (this.client) {
|
|
197
|
+
throw new Error("Already connected");
|
|
202
198
|
}
|
|
199
|
+
await this.attemptConnect().catch((error) => {
|
|
200
|
+
this.cleanup();
|
|
201
|
+
throw (classifyImapError(error, `${this.config.host}:${this.config.port}`) ??
|
|
202
|
+
error);
|
|
203
|
+
});
|
|
203
204
|
};
|
|
204
205
|
/**
|
|
205
206
|
* Single connection attempt
|
|
@@ -223,6 +224,9 @@ export class ImapFlowConnection {
|
|
|
223
224
|
host: this.config.host,
|
|
224
225
|
port: this.config.port,
|
|
225
226
|
secure: this.config.tls,
|
|
227
|
+
transport: this.config.transport,
|
|
228
|
+
connectionTimeout: this.config.connTimeout,
|
|
229
|
+
greetingTimeout: this.config.connTimeout,
|
|
226
230
|
servername: this.config.tlsOptions?.servername,
|
|
227
231
|
auth,
|
|
228
232
|
tls: tlsOptions,
|
|
@@ -356,8 +360,12 @@ export class ImapFlowConnection {
|
|
|
356
360
|
// Idempotency guard: re-selecting the currently-open mailbox is a no-op
|
|
357
361
|
// SELECT on the wire. `mailbox` exposes the live, already-selected box, so
|
|
358
362
|
// we can return its status without paying for another round-trip — this is
|
|
359
|
-
// what lets a batch issue one SELECT and many fetches on the same box.
|
|
360
|
-
|
|
363
|
+
// what lets a batch issue one SELECT and many fetches on the same box. A
|
|
364
|
+
// read-only open does not cover a read-write request: a pooled connection
|
|
365
|
+
// a sync left EXAMINEd must SELECT before a STORE, MOVE or EXPUNGE.
|
|
366
|
+
if (this.currentMailbox === mailboxPath &&
|
|
367
|
+
this.client?.mailbox &&
|
|
368
|
+
(readOnly || !this.currentMailboxReadOnly)) {
|
|
361
369
|
return this.toBoxStatus(this.client.mailbox, readOnly);
|
|
362
370
|
}
|
|
363
371
|
const mailbox = await this.client?.mailboxOpen(mailboxPath, {
|
|
@@ -367,6 +375,7 @@ export class ImapFlowConnection {
|
|
|
367
375
|
throw new Error(`Failed to open mailbox: ${mailboxPath}`);
|
|
368
376
|
}
|
|
369
377
|
this.currentMailbox = mailboxPath;
|
|
378
|
+
this.currentMailboxReadOnly = readOnly;
|
|
370
379
|
return this.toBoxStatus(mailbox, readOnly);
|
|
371
380
|
};
|
|
372
381
|
/**
|
|
@@ -995,6 +1004,9 @@ export class ImapFlowConnection {
|
|
|
995
1004
|
uidValidity: true,
|
|
996
1005
|
highestModseq: true,
|
|
997
1006
|
});
|
|
1007
|
+
if (status === false) {
|
|
1008
|
+
throw new Error(`IMAP STATUS returned no result for ${mailboxPath}`);
|
|
1009
|
+
}
|
|
998
1010
|
const deletedCount = await this.countDeleted(mailboxPath);
|
|
999
1011
|
return {
|
|
1000
1012
|
messages: status.messages ?? 0,
|
|
@@ -1052,30 +1064,6 @@ export class ImapFlowConnection {
|
|
|
1052
1064
|
}
|
|
1053
1065
|
};
|
|
1054
1066
|
}
|
|
1055
|
-
/**
|
|
1056
|
-
* Create an ImapFlow connection from account data using password credentials.
|
|
1057
|
-
*/
|
|
1058
|
-
export const createImapFlowConnectionFromAccount = (account, password) => {
|
|
1059
|
-
return new ImapFlowConnection({
|
|
1060
|
-
host: account.imapHost,
|
|
1061
|
-
port: account.imapPort,
|
|
1062
|
-
tls: account.imapTls,
|
|
1063
|
-
user: account.username,
|
|
1064
|
-
credentials: { kind: "password", password },
|
|
1065
|
-
});
|
|
1066
|
-
};
|
|
1067
|
-
/**
|
|
1068
|
-
* Create an ImapFlow connection using a credentials union (password or OAuth access token).
|
|
1069
|
-
*/
|
|
1070
|
-
export const createImapFlowConnectionWithCredentials = (account, credentials) => {
|
|
1071
|
-
return new ImapFlowConnection({
|
|
1072
|
-
host: account.imapHost,
|
|
1073
|
-
port: account.imapPort,
|
|
1074
|
-
tls: account.imapTls,
|
|
1075
|
-
user: account.username,
|
|
1076
|
-
credentials,
|
|
1077
|
-
});
|
|
1078
|
-
};
|
|
1079
1067
|
/**
|
|
1080
1068
|
* Build the imapflow auth object from mail credentials.
|
|
1081
1069
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -11,8 +11,8 @@ export { type BodySyncQueueConfig, type BodySyncQueueLogger, BodySyncQueueServic
|
|
|
11
11
|
export { isMessageBodySyncBroken, type ResolveExhaustedBodySyncDeps, type ResolveExhaustedBodySyncInput, type ResolveExhaustedBodySyncResult, resolveExhaustedBodySyncFailures, } from "./body-sync-terminal.js";
|
|
12
12
|
export { backfillClassifications, type ClassificationBackfillCheckpoint, type ClassificationBackfillCheckpointStore, type ClassificationBackfillDeps, type ClassificationBackfillLogger, type ClassificationBackfillOptions, type ClassificationBackfillProgress, type ClassificationBackfillResult, type ClassificationBackfillTotals, } from "./classification-backfill.js";
|
|
13
13
|
export { classifyParsedMessage, extractPrimaryFromEmail, } from "./classify-message.js";
|
|
14
|
-
export {
|
|
15
|
-
export { type ImapTestConfig,
|
|
14
|
+
export { type ConnectionScope, connectionScope, createConnection, createConnectionWithCredentials, } from "./connection-factory.js";
|
|
15
|
+
export { type ImapTestConfig, testImapConnection, } from "./connection-test.js";
|
|
16
16
|
export { type CreatedFilter, createFilter, type FilterPage, type FilterPatch, listFilters, type NewFilter, resolveFilterUpdate, updateFilter, } from "./filter-operations.js";
|
|
17
17
|
export { extractListId, normalizeListId } from "./filters/list-id.js";
|
|
18
18
|
export { clauseMatches, type FilterMessage, type LiteralClauseNarrowing, literalClausesMatch, literalClauseTerms, NO_ACTION, selectMoveWinner, } from "./filters/match.js";
|
|
@@ -27,7 +27,7 @@ export { classifyPlacement, type FolderPlacement, type PlacementAction, type Pla
|
|
|
27
27
|
export { classifyDisplayNameCorrespondence, extractOffDomainLinkDomains, extractSenderMismatch, type SenderMismatchContext, type SenderMismatchSignals, } from "./heuristics/senderMismatch.js";
|
|
28
28
|
export { SOCIAL_DOMAINS } from "./heuristics/socialDomains.js";
|
|
29
29
|
export { TRANSACTIONAL_DOMAINS } from "./heuristics/transactionalDomains.js";
|
|
30
|
-
export {
|
|
30
|
+
export { ImapFlowConnection } from "./imapflow-connection.js";
|
|
31
31
|
export { backfillListIds, type ListIdBackfillCheckpoint, type ListIdBackfillCheckpointStore, type ListIdBackfillDeps, type ListIdBackfillLogger, type ListIdBackfillOptions, type ListIdBackfillProgress, type ListIdBackfillResult, type ListIdBackfillTotals, } from "./list-id-backfill.js";
|
|
32
32
|
export { guardConnectionCursor, guardMailboxCursor, isCursorRebuildNeeded, type MailboxCursorCheck, type MailboxCursorGuardDeps, MailboxCursorPausedError, } from "./mailbox-cursor.js";
|
|
33
33
|
export { type CursorRebuildMatch, type CursorRebuildMatchResult, type CursorRebuildRow, type CursorRebuildSnapshot, matchCursorRebuild, } from "./mailbox-cursor-rebuild.js";
|
|
@@ -59,5 +59,5 @@ export { extractSearchText, extractSnippetFromEmail, generateSnippet, normalizeS
|
|
|
59
59
|
export { MoveNotSettledError, NoJunkMailboxError, type SpamReportConfig, type SpamReportLogger, type SpamReportParams, SpamReportService, } from "./spam-report.js";
|
|
60
60
|
export { type ServerUids, type SyncWarningContext, type SyncWarningRound, SyncWarningService, } from "./sync-warning.js";
|
|
61
61
|
export { buildThreadMessageMoveRevert, buildThreadMessageTrashUpdate, buildThreadMessageUndelete, type ThreadMessageRowState, } from "./thread-message-rows.js";
|
|
62
|
-
export type { FlatMailboxInfo, IImapConnection, ImapBoxStatus, ImapConnectionConfig, ImapConnectionState, ImapNamespace, ImapNamespaces, MailboxSyncResult, MailConnectionErrorKind, MailCredentials, } from "./types.js";
|
|
62
|
+
export type { FlatMailboxInfo, IImapConnection, ImapBoxStatus, ImapConnectionConfig, ImapConnectionState, ImapNamespace, ImapNamespaces, ImapTransport, MailboxSyncResult, MailConnectionErrorKind, MailCredentials, } from "./types.js";
|
|
63
63
|
export { MailConnectionError } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -12,9 +12,9 @@ export { isMessageBodySyncBroken, resolveExhaustedBodySyncFailures, } from "./bo
|
|
|
12
12
|
export { backfillClassifications, } from "./classification-backfill.js";
|
|
13
13
|
export { classifyParsedMessage, extractPrimaryFromEmail, } from "./classify-message.js";
|
|
14
14
|
// Connection factory
|
|
15
|
-
export {
|
|
15
|
+
export { connectionScope, createConnection, createConnectionWithCredentials, } from "./connection-factory.js";
|
|
16
16
|
// Connection testing
|
|
17
|
-
export { testImapConnection,
|
|
17
|
+
export { testImapConnection, } from "./connection-test.js";
|
|
18
18
|
export { createFilter, listFilters, resolveFilterUpdate, updateFilter, } from "./filter-operations.js";
|
|
19
19
|
export { extractListId, normalizeListId } from "./filters/list-id.js";
|
|
20
20
|
export { clauseMatches, literalClausesMatch, literalClauseTerms, NO_ACTION, selectMoveWinner, } from "./filters/match.js";
|
|
@@ -30,7 +30,7 @@ export { classifyDisplayNameCorrespondence, extractOffDomainLinkDomains, extract
|
|
|
30
30
|
export { SOCIAL_DOMAINS } from "./heuristics/socialDomains.js";
|
|
31
31
|
export { TRANSACTIONAL_DOMAINS } from "./heuristics/transactionalDomains.js";
|
|
32
32
|
// IMAP connection (ImapFlow-based)
|
|
33
|
-
export {
|
|
33
|
+
export { ImapFlowConnection } from "./imapflow-connection.js";
|
|
34
34
|
export { backfillListIds, } from "./list-id-backfill.js";
|
|
35
35
|
export { guardConnectionCursor, guardMailboxCursor, isCursorRebuildNeeded, MailboxCursorPausedError, } from "./mailbox-cursor.js";
|
|
36
36
|
export { matchCursorRebuild, } from "./mailbox-cursor-rebuild.js";
|
package/dist/message-sync.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { IAddressRepository, IEnvelopeRepository, IMailboxRepository, IMailboxSpecialUseRepository, IMessageFlagPushRepository, IMessageFlagRepository, IMessageRepository, IThreadMessageRepository } from "@remit/data-ports";
|
|
2
2
|
import type { JunkRoleMailboxes } from "@remit/data-ports/folder-role";
|
|
3
|
-
import type { ManagedConnectionFactory } from "./connection-factory.js";
|
|
4
3
|
import type { QuarantineService } from "./quarantine.js";
|
|
5
4
|
import type { SyncWarningService } from "./sync-warning.js";
|
|
6
|
-
import type { ImapAddress } from "./types.js";
|
|
5
|
+
import type { IImapConnection, ImapAddress } from "./types.js";
|
|
7
6
|
/**
|
|
8
7
|
* A From address is only usable when it looks like a real mailbox: both parts
|
|
9
8
|
* present, neither is a known "could not parse" sentinel, and the host carries
|
|
@@ -63,7 +62,7 @@ export declare const parseHeaderDate: (raw: string | undefined, fallbackMs: numb
|
|
|
63
62
|
usedFallback: boolean;
|
|
64
63
|
};
|
|
65
64
|
/**
|
|
66
|
-
* @deprecated
|
|
65
|
+
* @deprecated Pass the IImapConnection to MessageSyncService instead
|
|
67
66
|
*/
|
|
68
67
|
export type ImapConnectionFactory = () => {
|
|
69
68
|
connect(): Promise<void>;
|
|
@@ -101,7 +100,7 @@ export interface SyncMessagesResult {
|
|
|
101
100
|
*/
|
|
102
101
|
export declare const selectUidsToSync: (allUids: number[], lastSyncUid: number, highWaterMarkUid: number) => number[];
|
|
103
102
|
export declare class MessageSyncService {
|
|
104
|
-
private
|
|
103
|
+
private connection;
|
|
105
104
|
private mailboxService;
|
|
106
105
|
/**
|
|
107
106
|
* The account’s folder-role map. Required, not optional: which folder
|
|
@@ -135,7 +134,7 @@ export declare class MessageSyncService {
|
|
|
135
134
|
*/
|
|
136
135
|
private quarantineService?;
|
|
137
136
|
private log;
|
|
138
|
-
constructor(
|
|
137
|
+
constructor(connection: IImapConnection, mailboxService: IMailboxRepository,
|
|
139
138
|
/**
|
|
140
139
|
* The account’s folder-role map. Required, not optional: which folder
|
|
141
140
|
* holds the Junk role decides whether a sighting withholds its sender
|
package/dist/message-sync.js
CHANGED
|
@@ -147,7 +147,7 @@ const messageIdForSighting = (accountId, mailboxId, msg) => deriveMessageIdFromS
|
|
|
147
147
|
fromHost: msg.envelope.from?.[0]?.host,
|
|
148
148
|
});
|
|
149
149
|
export class MessageSyncService {
|
|
150
|
-
|
|
150
|
+
connection;
|
|
151
151
|
mailboxService;
|
|
152
152
|
mailboxSpecialUseService;
|
|
153
153
|
messageService;
|
|
@@ -159,7 +159,7 @@ export class MessageSyncService {
|
|
|
159
159
|
messageFlagService;
|
|
160
160
|
quarantineService;
|
|
161
161
|
log;
|
|
162
|
-
constructor(
|
|
162
|
+
constructor(connection, mailboxService,
|
|
163
163
|
/**
|
|
164
164
|
* The account’s folder-role map. Required, not optional: which folder
|
|
165
165
|
* holds the Junk role decides whether a sighting withholds its sender
|
|
@@ -186,7 +186,7 @@ export class MessageSyncService {
|
|
|
186
186
|
* not re-fetched on every round.
|
|
187
187
|
*/
|
|
188
188
|
quarantineService) {
|
|
189
|
-
this.
|
|
189
|
+
this.connection = connection;
|
|
190
190
|
this.mailboxService = mailboxService;
|
|
191
191
|
this.mailboxSpecialUseService = mailboxSpecialUseService;
|
|
192
192
|
this.messageService = messageService;
|
|
@@ -227,7 +227,7 @@ export class MessageSyncService {
|
|
|
227
227
|
}
|
|
228
228
|
const lastSyncUid = mailbox.lastSyncUid || 0;
|
|
229
229
|
const highWaterMarkUid = mailbox.highWaterMarkUid || 0;
|
|
230
|
-
const connection = this.
|
|
230
|
+
const connection = this.connection;
|
|
231
231
|
const box = await connection.openBox(mailboxPath);
|
|
232
232
|
const status = await connection.getMailboxStatus(mailboxPath);
|
|
233
233
|
// Detection: the served UIDVALIDITY may have changed since it was last
|
|
@@ -453,7 +453,7 @@ export class MessageSyncService {
|
|
|
453
453
|
await this.mailboxService.update(accountId, mailboxId, {
|
|
454
454
|
cursorState: MailboxCursorState.rebuilding,
|
|
455
455
|
});
|
|
456
|
-
const connection = this.
|
|
456
|
+
const connection = this.connection;
|
|
457
457
|
const box = await connection.openBox(mailboxPath);
|
|
458
458
|
// Read before the pass, never after: a message arriving while the
|
|
459
459
|
// rebuild runs is absent from the snapshot below, and a HIGHESTMODSEQ
|
|
@@ -652,7 +652,7 @@ export class MessageSyncService {
|
|
|
652
652
|
const { mailbox, accountId, accountConfigId, cursor, box, status, batchSize, } = params;
|
|
653
653
|
const mailboxId = mailbox.mailboxId;
|
|
654
654
|
const mailboxPath = mailbox.fullPath;
|
|
655
|
-
const connection = this.
|
|
655
|
+
const connection = this.connection;
|
|
656
656
|
// Ask from the last COMPLETE mod-sequence, so a group left part-applied
|
|
657
657
|
// by an earlier round is served again in full; its applied members are
|
|
658
658
|
// then dropped by position, without a lookup.
|
|
@@ -963,7 +963,7 @@ export class MessageSyncService {
|
|
|
963
963
|
* Assumes mailbox is already open from fetchUidsToSync.
|
|
964
964
|
*/
|
|
965
965
|
async fetchMessageBatch(uids) {
|
|
966
|
-
const connection = this.
|
|
966
|
+
const connection = this.connection;
|
|
967
967
|
return await connection.fetchMessages(uids);
|
|
968
968
|
}
|
|
969
969
|
/**
|
|
@@ -1004,7 +1004,7 @@ export class MessageSyncService {
|
|
|
1004
1004
|
*/
|
|
1005
1005
|
async probeDepartures(mailbox, accountId, applicable) {
|
|
1006
1006
|
return confirmDepartures({
|
|
1007
|
-
connection: this.
|
|
1007
|
+
connection: this.connection,
|
|
1008
1008
|
mailboxService: this.mailboxService,
|
|
1009
1009
|
messageService: this.messageService,
|
|
1010
1010
|
log: this.log,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { connect as netConnect } from "node:net";
|
|
2
|
+
import { connect as tlsConnect } from "node:tls";
|
|
3
|
+
export const nodeTransport = () => ({
|
|
4
|
+
connect: ({ host, port, servername, tls, secure }, onConnect) => secure
|
|
5
|
+
? tlsConnect({ host, port, servername, ...tls }, onConnect)
|
|
6
|
+
: netConnect({ host, port }, onConnect),
|
|
7
|
+
upgrade: (socket, { host, port, servername, tls }, onSecure) => tlsConnect({ socket, host, port, servername, ...tls }, onSecure),
|
|
8
|
+
});
|
|
@@ -91,8 +91,10 @@ export declare class FakeImapConnection implements IImapConnection {
|
|
|
91
91
|
readonly state: ImapConnectionState;
|
|
92
92
|
readonly isConnected = true;
|
|
93
93
|
private selected;
|
|
94
|
+
private selectedReadOnly;
|
|
94
95
|
constructor(server: FakeImapServer);
|
|
95
96
|
private open;
|
|
97
|
+
private writable;
|
|
96
98
|
private uidList;
|
|
97
99
|
connect: () => Promise<void>;
|
|
98
100
|
disconnect: () => Promise<void>;
|
|
@@ -216,6 +216,7 @@ export class FakeImapConnection {
|
|
|
216
216
|
state = "authenticated";
|
|
217
217
|
isConnected = true;
|
|
218
218
|
selected = null;
|
|
219
|
+
selectedReadOnly = true;
|
|
219
220
|
constructor(server) {
|
|
220
221
|
this.server = server;
|
|
221
222
|
}
|
|
@@ -224,6 +225,13 @@ export class FakeImapConnection {
|
|
|
224
225
|
throw new Error("No mailbox selected");
|
|
225
226
|
return this.server.folder(this.selected);
|
|
226
227
|
}
|
|
228
|
+
writable() {
|
|
229
|
+
const folder = this.open();
|
|
230
|
+
if (this.selectedReadOnly) {
|
|
231
|
+
throw imapError("READ-ONLY", `Mailbox is read-only: ${this.selected}`);
|
|
232
|
+
}
|
|
233
|
+
return folder;
|
|
234
|
+
}
|
|
227
235
|
uidList = (uids) => uids.join(",");
|
|
228
236
|
connect = async () => { };
|
|
229
237
|
disconnect = async () => { };
|
|
@@ -248,10 +256,11 @@ export class FakeImapConnection {
|
|
|
248
256
|
};
|
|
249
257
|
});
|
|
250
258
|
};
|
|
251
|
-
openBox = async (path, readOnly =
|
|
259
|
+
openBox = async (path, readOnly = true) => {
|
|
252
260
|
this.server.run(readOnly ? "EXAMINE" : "SELECT", path);
|
|
253
261
|
const folder = this.server.folder(path);
|
|
254
262
|
this.selected = path;
|
|
263
|
+
this.selectedReadOnly = readOnly;
|
|
255
264
|
return {
|
|
256
265
|
name: path,
|
|
257
266
|
readOnly,
|
|
@@ -389,7 +398,7 @@ export class FakeImapConnection {
|
|
|
389
398
|
}
|
|
390
399
|
touch = (detail, uids, change) => {
|
|
391
400
|
this.server.run("STORE", `${this.selected} ${this.uidList(uids)} ${detail}`);
|
|
392
|
-
const folder = this.
|
|
401
|
+
const folder = this.writable();
|
|
393
402
|
const modseq = this.server.bumpModseq();
|
|
394
403
|
for (const uid of uids) {
|
|
395
404
|
const stored = folder.messages.get(uid);
|
|
@@ -446,7 +455,7 @@ export class FakeImapConnection {
|
|
|
446
455
|
if (this.server.ignoreNext.delete(verb)) {
|
|
447
456
|
return { destination, uidValidity: target.uidValidity, uidMap };
|
|
448
457
|
}
|
|
449
|
-
const source = this.open();
|
|
458
|
+
const source = verb === "MOVE" ? this.writable() : this.open();
|
|
450
459
|
for (const uid of uids) {
|
|
451
460
|
const stored = source.messages.get(uid);
|
|
452
461
|
if (!stored)
|
|
@@ -468,7 +477,7 @@ export class FakeImapConnection {
|
|
|
468
477
|
copyMessages = async (uids, destination) => this.transfer("COPY", uids, destination);
|
|
469
478
|
deleteMessages = async (uids) => {
|
|
470
479
|
this.server.run("EXPUNGE", `${this.selected} ${this.uidList(uids)}`);
|
|
471
|
-
const folder = this.
|
|
480
|
+
const folder = this.writable();
|
|
472
481
|
let deleted = 0;
|
|
473
482
|
for (const uid of uids) {
|
|
474
483
|
if (folder.messages.delete(uid))
|
|
@@ -146,7 +146,7 @@ export const silentLog = {
|
|
|
146
146
|
};
|
|
147
147
|
export const syncFolder = async (store, account, mailbox, imap) => {
|
|
148
148
|
const connection = imap.connection();
|
|
149
|
-
const service = new MessageSyncService(
|
|
149
|
+
const service = new MessageSyncService(connection, store.mailbox, store.mailboxSpecialUse, store.message, store.envelope, store.address, store.threadMessage, new SyncWarningService(store.syncWarning), silentLog, store.flagPush, store.messageFlag);
|
|
150
150
|
const synced = [];
|
|
151
151
|
for (;;) {
|
|
152
152
|
const result = await service.syncMessages(mailbox.mailboxId, account.accountId, account.accountConfigId);
|
package/dist/types.d.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Types for the remit-mailbox-service
|
|
3
3
|
*/
|
|
4
4
|
import type { Readable } from "node:stream";
|
|
5
|
+
import type { ImapTransport } from "imapflow";
|
|
6
|
+
export type { ImapTransport };
|
|
5
7
|
/**
|
|
6
8
|
* Discriminated union of mail authentication credentials.
|
|
7
9
|
*
|
|
@@ -40,14 +42,13 @@ export interface ImapConnectionConfig {
|
|
|
40
42
|
user: string;
|
|
41
43
|
credentials: MailCredentials;
|
|
42
44
|
tls: boolean;
|
|
45
|
+
transport: ImapTransport;
|
|
43
46
|
tlsOptions?: {
|
|
44
47
|
rejectUnauthorized?: boolean;
|
|
45
48
|
servername?: string;
|
|
46
49
|
};
|
|
47
|
-
/** Connection timeout in milliseconds */
|
|
50
|
+
/** Connection and greeting timeout in milliseconds */
|
|
48
51
|
connTimeout?: number;
|
|
49
|
-
/** Authentication timeout in milliseconds */
|
|
50
|
-
authTimeout?: number;
|
|
51
52
|
}
|
|
52
53
|
/**
|
|
53
54
|
* IMAP connection state
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remit/mailbox-service",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.116",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
"types": "./dist/imapflow-connection.d.ts",
|
|
22
22
|
"default": "./dist/imapflow-connection.js"
|
|
23
23
|
},
|
|
24
|
+
"./node-transport": {
|
|
25
|
+
"types": "./dist/node-transport.d.ts",
|
|
26
|
+
"default": "./dist/node-transport.js"
|
|
27
|
+
},
|
|
24
28
|
"./account-credentials": {
|
|
25
29
|
"types": "./dist/account-credentials.d.ts",
|
|
26
30
|
"default": "./dist/account-credentials.js"
|
|
@@ -55,9 +59,8 @@
|
|
|
55
59
|
"better-sqlite3": "^12.11.1"
|
|
56
60
|
},
|
|
57
61
|
"dependencies": {
|
|
58
|
-
"imapflow": "^
|
|
62
|
+
"imapflow": "^2.9.1",
|
|
59
63
|
"mailparser": "3.12.2",
|
|
60
|
-
"nodemailer": "^10.0.6",
|
|
61
64
|
"p-map": "^7.0.4",
|
|
62
65
|
"tldts": "^7.4.9",
|
|
63
66
|
"@remit/data-ports": "*",
|
|
@@ -69,7 +72,6 @@
|
|
|
69
72
|
"natural": "^8.1.1",
|
|
70
73
|
"stopword": "^3.1.5",
|
|
71
74
|
"@types/mailparser": "^3.4.6",
|
|
72
|
-
"@types/nodemailer": "^8.0.1",
|
|
73
75
|
"@types/stopword": "^2.0.3",
|
|
74
76
|
"@remit/calendar-service": "*"
|
|
75
77
|
},
|