@remit/mailbox-service 0.0.1
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/README.md +147 -0
- package/package.json +65 -0
- package/src/account-credentials.ts +128 -0
- package/src/adversarial-mime.e2e.test.ts +311 -0
- package/src/attribute-mapper.test.ts +78 -0
- package/src/attribute-mapper.ts +147 -0
- package/src/body-part-mapper.property.test.ts +354 -0
- package/src/body-part-mapper.ts +360 -0
- package/src/body-sync-queue.test.ts +124 -0
- package/src/body-sync-queue.ts +131 -0
- package/src/body-sync-terminal.test.ts +265 -0
- package/src/body-sync-terminal.ts +172 -0
- package/src/body-sync.ts +1202 -0
- package/src/connection-factory.ts +102 -0
- package/src/connection-test.ts +208 -0
- package/src/credentials.test.ts +181 -0
- package/src/filters/match.test.ts +181 -0
- package/src/filters/match.ts +143 -0
- package/src/filters/pipeline.ts +243 -0
- package/src/flag-push-terminal.test.ts +156 -0
- package/src/flag-push-terminal.ts +106 -0
- package/src/flag-push.test.ts +408 -0
- package/src/flag-push.ts +198 -0
- package/src/flag-queue.ts +500 -0
- package/src/heuristics/classifyByHeaders.test.ts +578 -0
- package/src/heuristics/classifyByHeaders.ts +369 -0
- package/src/heuristics/classifyPlacement.ts +96 -0
- package/src/heuristics/socialDomains.ts +23 -0
- package/src/heuristics/transactionalDomains.ts +26 -0
- package/src/imapflow-connect-retry.test.ts +108 -0
- package/src/imapflow-connection.e2e.test.ts +355 -0
- package/src/imapflow-connection.integ.test.ts +327 -0
- package/src/imapflow-connection.test.ts +161 -0
- package/src/imapflow-connection.ts +1374 -0
- package/src/index.ts +214 -0
- package/src/mailbox-cursor-rebuild.test.ts +171 -0
- package/src/mailbox-cursor-rebuild.ts +146 -0
- package/src/mailbox-cursor.test.ts +249 -0
- package/src/mailbox-cursor.ts +177 -0
- package/src/mailbox-management.test.ts +103 -0
- package/src/mailbox-management.ts +235 -0
- package/src/mailbox-queue.ts +258 -0
- package/src/mailbox-sync.test.ts +153 -0
- package/src/mailbox-sync.ts +590 -0
- package/src/message-move.ts +825 -0
- package/src/message-parser.ts +26 -0
- package/src/message-sync.ts +1021 -0
- package/src/mime-walker.test.ts +311 -0
- package/src/mime-walker.ts +305 -0
- package/src/outbox-queue.ts +294 -0
- package/src/pass-through-unit-of-work.ts +17 -0
- package/src/placement-move-terminal.test.ts +179 -0
- package/src/placement-move-terminal.ts +104 -0
- package/src/placement-move.ts +224 -0
- package/src/snippet.test.ts +213 -0
- package/src/snippet.ts +152 -0
- package/src/stale-message-reconcile.test.ts +82 -0
- package/src/stale-message-reconcile.ts +55 -0
- package/src/test-helpers/isolated-mailbox.ts +44 -0
- package/src/test-helpers/mailfuzz-connection.ts +27 -0
- package/src/test-helpers/mime-tree-generator.ts +461 -0
- package/src/text/normalizer.test.ts +223 -0
- package/src/text/normalizer.ts +140 -0
- package/src/types/mailparser-augment.d.ts +15 -0
- package/src/types/natural-porter-stemmer.d.ts +50 -0
- package/src/types/stopword.d.ts +69 -0
- package/src/types.ts +349 -0
- package/test/fixtures/mime/01-flat-text-plain.bodyparts.json +8 -0
- package/test/fixtures/mime/01-flat-text-plain.eml +10 -0
- package/test/fixtures/mime/01-flat-text-plain.expected.json +10 -0
- package/test/fixtures/mime/02-flat-text-html.bodyparts.json +8 -0
- package/test/fixtures/mime/02-flat-text-html.eml +10 -0
- package/test/fixtures/mime/02-flat-text-html.expected.json +10 -0
- package/test/fixtures/mime/03-alternative-text-html.bodyparts.json +20 -0
- package/test/fixtures/mime/03-alternative-text-html.eml +19 -0
- package/test/fixtures/mime/03-alternative-text-html.expected.json +16 -0
- package/test/fixtures/mime/04-mixed-html-pdf-attachment.bodyparts.json +22 -0
- package/test/fixtures/mime/04-mixed-html-pdf-attachment.eml +20 -0
- package/test/fixtures/mime/04-mixed-html-pdf-attachment.expected.json +16 -0
- package/test/fixtures/mime/05-mixed-html-octet-stream-pdf.bodyparts.json +22 -0
- package/test/fixtures/mime/05-mixed-html-octet-stream-pdf.eml +21 -0
- package/test/fixtures/mime/05-mixed-html-octet-stream-pdf.expected.json +16 -0
- package/test/fixtures/mime/06-related-html-cid-image.bodyparts.json +23 -0
- package/test/fixtures/mime/06-related-html-cid-image.eml +21 -0
- package/test/fixtures/mime/06-related-html-cid-image.expected.json +16 -0
- package/test/fixtures/mime/07-mixed-alternative-related-attachment.bodyparts.json +55 -0
- package/test/fixtures/mime/07-mixed-alternative-related-attachment.eml +47 -0
- package/test/fixtures/mime/07-mixed-alternative-related-attachment.expected.json +34 -0
- package/test/fixtures/mime/08-two-pdfs-same-filename.bodyparts.json +30 -0
- package/test/fixtures/mime/08-two-pdfs-same-filename.eml +26 -0
- package/test/fixtures/mime/08-two-pdfs-same-filename.expected.json +22 -0
- package/test/fixtures/mime/09-attachment-no-filename.bodyparts.json +21 -0
- package/test/fixtures/mime/09-attachment-no-filename.eml +20 -0
- package/test/fixtures/mime/09-attachment-no-filename.expected.json +16 -0
- package/test/fixtures/mime/10-calendar-invite.bodyparts.json +22 -0
- package/test/fixtures/mime/10-calendar-invite.eml +33 -0
- package/test/fixtures/mime/10-calendar-invite.expected.json +16 -0
- package/test/fixtures/mime/11-quoted-printable-text.bodyparts.json +8 -0
- package/test/fixtures/mime/11-quoted-printable-text.eml +10 -0
- package/test/fixtures/mime/11-quoted-printable-text.expected.json +10 -0
- package/test/fixtures/mime/12-base64-binary.bodyparts.json +22 -0
- package/test/fixtures/mime/12-base64-binary.eml +20 -0
- package/test/fixtures/mime/12-base64-binary.expected.json +16 -0
- package/test/fixtures/mime/13-message-rfc822-forward.bodyparts.json +22 -0
- package/test/fixtures/mime/13-message-rfc822-forward.eml +28 -0
- package/test/fixtures/mime/13-message-rfc822-forward.expected.json +16 -0
- package/test/fixtures/mime/14-deeply-nested-5-levels.bodyparts.json +38 -0
- package/test/fixtures/mime/14-deeply-nested-5-levels.eml +34 -0
- package/test/fixtures/mime/14-deeply-nested-5-levels.expected.json +10 -0
- package/test/fixtures/mime/15-empty-text-part.bodyparts.json +21 -0
- package/test/fixtures/mime/15-empty-text-part.eml +18 -0
- package/test/fixtures/mime/15-empty-text-part.expected.json +16 -0
- package/test/fixtures/mime/16-empty-non-text-part.bodyparts.json +30 -0
- package/test/fixtures/mime/16-empty-non-text-part.eml +24 -0
- package/test/fixtures/mime/16-empty-non-text-part.expected.json +22 -0
- package/test/fixtures/mime-adversarial-e2e/01-octet-stream-pdf.eml +21 -0
- package/test/fixtures/mime-adversarial-e2e/02-nested-related-cid-image.eml +31 -0
- package/test/fixtures/mime-adversarial-e2e/03-calendar-invite.eml +33 -0
- package/test/fixtures/mime-adversarial-e2e/04-forwarded-rfc822-with-attachments.eml +42 -0
- package/test/fixtures/mime-adversarial-e2e/05-tnef-winmail.eml +25 -0
- package/test/fixtures/mime-adversarial-e2e/06-8bit-non-ascii.eml +15 -0
- package/test/fixtures/mime-adversarial-e2e/07-quoted-printable-soft-breaks.eml +17 -0
- package/test/fixtures/mime-adversarial-e2e/08-alternative-empty-text.eml +18 -0
- package/tsconfig.json +8 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the remit-mailbox-service
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Readable } from "node:stream";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Discriminated union of mail authentication credentials.
|
|
9
|
+
*
|
|
10
|
+
* Exactly one credential kind can be present at a time — the union makes
|
|
11
|
+
* "both password and accessToken simultaneously" unrepresentable.
|
|
12
|
+
*/
|
|
13
|
+
export type MailCredentials =
|
|
14
|
+
| { kind: "password"; password: string }
|
|
15
|
+
| { kind: "accessToken"; accessToken: string };
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Classification of mail connection errors.
|
|
19
|
+
*/
|
|
20
|
+
export type MailConnectionErrorKind = "auth" | "network";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Typed error for IMAP/SMTP connection failures.
|
|
24
|
+
*
|
|
25
|
+
* `kind: "auth"` — authentication rejected (bad credentials / token expired).
|
|
26
|
+
* `kind: "network"` — transport-level failure (ECONNREFUSED, ETIMEDOUT, etc.).
|
|
27
|
+
*
|
|
28
|
+
* IMPORTANT: access tokens must NEVER appear in error messages or causes.
|
|
29
|
+
*/
|
|
30
|
+
export class MailConnectionError extends Error {
|
|
31
|
+
readonly kind: MailConnectionErrorKind;
|
|
32
|
+
|
|
33
|
+
constructor(kind: MailConnectionErrorKind, message: string, cause?: unknown) {
|
|
34
|
+
super(message, { cause });
|
|
35
|
+
this.name = "MailConnectionError";
|
|
36
|
+
this.kind = kind;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* IMAP connection configuration
|
|
42
|
+
*/
|
|
43
|
+
export interface ImapConnectionConfig {
|
|
44
|
+
host: string;
|
|
45
|
+
port: number;
|
|
46
|
+
user: string;
|
|
47
|
+
credentials: MailCredentials;
|
|
48
|
+
tls: boolean;
|
|
49
|
+
tlsOptions?: {
|
|
50
|
+
rejectUnauthorized?: boolean;
|
|
51
|
+
servername?: string;
|
|
52
|
+
};
|
|
53
|
+
/** Connection timeout in milliseconds */
|
|
54
|
+
connTimeout?: number;
|
|
55
|
+
/** Authentication timeout in milliseconds */
|
|
56
|
+
authTimeout?: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* IMAP connection state
|
|
61
|
+
*/
|
|
62
|
+
export type ImapConnectionState =
|
|
63
|
+
| "disconnected"
|
|
64
|
+
| "connecting"
|
|
65
|
+
| "connected"
|
|
66
|
+
| "authenticated"
|
|
67
|
+
| "error";
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Namespace structure from IMAP NAMESPACE command
|
|
71
|
+
*/
|
|
72
|
+
export interface ImapNamespace {
|
|
73
|
+
prefix: string;
|
|
74
|
+
delimiter: string | false;
|
|
75
|
+
extensions?: Array<{
|
|
76
|
+
name: string;
|
|
77
|
+
params: string[] | null;
|
|
78
|
+
}>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* IMAP namespaces grouped by type
|
|
83
|
+
*/
|
|
84
|
+
export interface ImapNamespaces {
|
|
85
|
+
personal: ImapNamespace[];
|
|
86
|
+
other: ImapNamespace[];
|
|
87
|
+
shared: ImapNamespace[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* IMAP mailbox status after opening
|
|
92
|
+
*/
|
|
93
|
+
export interface ImapBoxStatus {
|
|
94
|
+
name: string;
|
|
95
|
+
readOnly: boolean;
|
|
96
|
+
uidvalidity: number;
|
|
97
|
+
uidnext: number;
|
|
98
|
+
flags: string[];
|
|
99
|
+
permFlags: string[];
|
|
100
|
+
persistentUIDs: boolean;
|
|
101
|
+
messages: {
|
|
102
|
+
total: number;
|
|
103
|
+
new: number;
|
|
104
|
+
};
|
|
105
|
+
newKeywords: boolean;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* IMAP mailbox status from STATUS command
|
|
110
|
+
*/
|
|
111
|
+
export interface ImapMailboxStatus {
|
|
112
|
+
messages: number;
|
|
113
|
+
recent: number;
|
|
114
|
+
unseen: number;
|
|
115
|
+
uidNext: number;
|
|
116
|
+
uidValidity: number;
|
|
117
|
+
/** Highest modification sequence (CONDSTORE). 0 if server doesn't support CONDSTORE. */
|
|
118
|
+
highestModseq: number;
|
|
119
|
+
/** Count of messages flagged \Deleted but not yet expunged. */
|
|
120
|
+
deletedCount: number;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Flattened mailbox info for processing
|
|
125
|
+
*/
|
|
126
|
+
export interface FlatMailboxInfo {
|
|
127
|
+
fullPath: string;
|
|
128
|
+
name: string;
|
|
129
|
+
delimiter: string;
|
|
130
|
+
attributes: string[];
|
|
131
|
+
parentPath: string | null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface ImapAddress {
|
|
135
|
+
name?: string;
|
|
136
|
+
mailbox: string;
|
|
137
|
+
host: string;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface ImapEnvelope {
|
|
141
|
+
date: string;
|
|
142
|
+
subject: string;
|
|
143
|
+
from: ImapAddress[];
|
|
144
|
+
sender: ImapAddress[];
|
|
145
|
+
replyTo: ImapAddress[];
|
|
146
|
+
to: ImapAddress[];
|
|
147
|
+
cc: ImapAddress[];
|
|
148
|
+
bcc: ImapAddress[];
|
|
149
|
+
inReplyTo: string;
|
|
150
|
+
messageId: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Subset of imapflow's `MessageStructureObject` we depend on. Restated
|
|
155
|
+
* here so non-imapflow code (and tests) can build fixtures without pulling
|
|
156
|
+
* the imapflow types in.
|
|
157
|
+
*/
|
|
158
|
+
export interface ImapBodyStructure {
|
|
159
|
+
/** Dot-numbered MIME path; absent on the root node. */
|
|
160
|
+
part?: string;
|
|
161
|
+
/** Full Content-Type, e.g. "text/plain" or "multipart/mixed". */
|
|
162
|
+
type: string;
|
|
163
|
+
parameters?: Record<string, string>;
|
|
164
|
+
id?: string;
|
|
165
|
+
description?: string;
|
|
166
|
+
encoding?: string;
|
|
167
|
+
size?: number;
|
|
168
|
+
lineCount?: number;
|
|
169
|
+
md5?: string;
|
|
170
|
+
disposition?: string;
|
|
171
|
+
dispositionParameters?: Record<string, string>;
|
|
172
|
+
language?: string[];
|
|
173
|
+
location?: string;
|
|
174
|
+
childNodes?: ImapBodyStructure[];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* One server message from the cheap envelope-only FETCH the UIDVALIDITY
|
|
179
|
+
* cursor rebuild uses (#1272): UID + Message-ID + INTERNALDATE, nothing
|
|
180
|
+
* else — no flags, no BODYSTRUCTURE, no body fetch.
|
|
181
|
+
*/
|
|
182
|
+
export interface ImapEnvelopeSnapshot {
|
|
183
|
+
uid: number;
|
|
184
|
+
/** Raw RFC 822 Message-ID header; empty string when absent/unparseable. */
|
|
185
|
+
messageId: string;
|
|
186
|
+
internalDate: Date;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export interface ImapMessage {
|
|
190
|
+
uid: number;
|
|
191
|
+
seq: number;
|
|
192
|
+
flags: string[];
|
|
193
|
+
internalDate: Date;
|
|
194
|
+
size: number;
|
|
195
|
+
envelope?: ImapEnvelope;
|
|
196
|
+
/**
|
|
197
|
+
* References header from RFC 2822.
|
|
198
|
+
* Contains Message-IDs of ancestor messages, with the first being the thread root.
|
|
199
|
+
*/
|
|
200
|
+
references?: string[];
|
|
201
|
+
/** Parsed BODYSTRUCTURE tree (RFC 9051 Section 7.5.2). */
|
|
202
|
+
bodyStructure?: ImapBodyStructure;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Result of a mailbox sync operation
|
|
207
|
+
*/
|
|
208
|
+
export interface MailboxSyncResult {
|
|
209
|
+
created: number;
|
|
210
|
+
updated: number;
|
|
211
|
+
deleted: number;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Common interface for IMAP connections
|
|
216
|
+
*/
|
|
217
|
+
export interface IImapConnection {
|
|
218
|
+
readonly state: ImapConnectionState;
|
|
219
|
+
readonly isConnected: boolean;
|
|
220
|
+
connect(): Promise<void>;
|
|
221
|
+
disconnect(): Promise<void>;
|
|
222
|
+
getNamespaces(): Promise<ImapNamespaces>;
|
|
223
|
+
listMailboxes(nsPrefix?: string): Promise<FlatMailboxInfo[]>;
|
|
224
|
+
openBox(mailboxPath: string, readOnly?: boolean): Promise<ImapBoxStatus>;
|
|
225
|
+
closeBox(expunge?: boolean): Promise<void>;
|
|
226
|
+
search(criteria: unknown[]): Promise<number[]>;
|
|
227
|
+
fetchMessages(uids: number[]): Promise<ImapMessage[]>;
|
|
228
|
+
/**
|
|
229
|
+
* Cheap envelope-only pass for the UIDVALIDITY cursor rebuild (#1272): UID
|
|
230
|
+
* + Message-ID + INTERNALDATE for every UID, no BODYSTRUCTURE, no flags,
|
|
231
|
+
* no body. Requires a mailbox to be open.
|
|
232
|
+
*/
|
|
233
|
+
fetchEnvelopeSnapshots(uids: number[]): Promise<ImapEnvelopeSnapshot[]>;
|
|
234
|
+
fetchMessageBody(uid: number): Promise<Buffer>;
|
|
235
|
+
/**
|
|
236
|
+
* Fetch full message bodies for many UIDs in ONE pipelined ranged UID FETCH.
|
|
237
|
+
* Yields `{ uid, source }` per message; `source` is a readable stream the
|
|
238
|
+
* caller must consume before requesting the next item. Requires a mailbox to
|
|
239
|
+
* be open (one SELECT for the whole batch).
|
|
240
|
+
*/
|
|
241
|
+
fetchMessageBodies(
|
|
242
|
+
uids: number[],
|
|
243
|
+
): AsyncGenerator<{ uid: number; source: Readable }>;
|
|
244
|
+
/**
|
|
245
|
+
* Add flags to messages by UID.
|
|
246
|
+
* Requires mailbox to be open.
|
|
247
|
+
*/
|
|
248
|
+
addFlags(uids: number[], flags: string[]): Promise<void>;
|
|
249
|
+
/**
|
|
250
|
+
* Remove flags from messages by UID.
|
|
251
|
+
* Requires mailbox to be open.
|
|
252
|
+
*/
|
|
253
|
+
removeFlags(uids: number[], flags: string[]): Promise<void>;
|
|
254
|
+
/**
|
|
255
|
+
* Replace all flags on messages by UID.
|
|
256
|
+
* Requires mailbox to be open.
|
|
257
|
+
*/
|
|
258
|
+
setFlags(uids: number[], flags: string[]): Promise<void>;
|
|
259
|
+
/**
|
|
260
|
+
* Create a new mailbox.
|
|
261
|
+
*/
|
|
262
|
+
createMailbox(path: string): Promise<{ path: string; created: boolean }>;
|
|
263
|
+
/**
|
|
264
|
+
* Delete a mailbox.
|
|
265
|
+
*/
|
|
266
|
+
deleteMailbox(path: string): Promise<{ path: string }>;
|
|
267
|
+
/**
|
|
268
|
+
* Rename a mailbox.
|
|
269
|
+
*/
|
|
270
|
+
renameMailbox(
|
|
271
|
+
oldPath: string,
|
|
272
|
+
newPath: string,
|
|
273
|
+
): Promise<{ path: string; newPath: string }>;
|
|
274
|
+
/**
|
|
275
|
+
* Subscribe to a mailbox.
|
|
276
|
+
*/
|
|
277
|
+
subscribeMailbox(path: string): Promise<void>;
|
|
278
|
+
/**
|
|
279
|
+
* Unsubscribe from a mailbox.
|
|
280
|
+
*/
|
|
281
|
+
unsubscribeMailbox(path: string): Promise<void>;
|
|
282
|
+
/**
|
|
283
|
+
* Move messages to another mailbox.
|
|
284
|
+
* Returns mapping of source UIDs to destination UIDs.
|
|
285
|
+
* Requires mailbox to be open.
|
|
286
|
+
*
|
|
287
|
+
* @param uids - Array of message UIDs to move
|
|
288
|
+
* @param destination - Destination mailbox path
|
|
289
|
+
* @returns Object with destination path, uidValidity, and uidMap
|
|
290
|
+
*/
|
|
291
|
+
moveMessages(
|
|
292
|
+
uids: number[],
|
|
293
|
+
destination: string,
|
|
294
|
+
): Promise<{
|
|
295
|
+
destination: string;
|
|
296
|
+
uidValidity: number;
|
|
297
|
+
uidMap: Map<number, number>;
|
|
298
|
+
}>;
|
|
299
|
+
/**
|
|
300
|
+
* Copy messages to another mailbox.
|
|
301
|
+
* Returns mapping of source UIDs to destination UIDs.
|
|
302
|
+
* Requires mailbox to be open.
|
|
303
|
+
*
|
|
304
|
+
* @param uids - Array of message UIDs to copy
|
|
305
|
+
* @param destination - Destination mailbox path
|
|
306
|
+
* @returns Object with destination path, uidValidity, and uidMap
|
|
307
|
+
*/
|
|
308
|
+
copyMessages(
|
|
309
|
+
uids: number[],
|
|
310
|
+
destination: string,
|
|
311
|
+
): Promise<{
|
|
312
|
+
destination: string;
|
|
313
|
+
uidValidity: number;
|
|
314
|
+
uidMap: Map<number, number>;
|
|
315
|
+
}>;
|
|
316
|
+
/**
|
|
317
|
+
* Permanently delete messages (mark \Deleted + expunge).
|
|
318
|
+
* Requires mailbox to be open.
|
|
319
|
+
*
|
|
320
|
+
* @param uids - Array of message UIDs to delete
|
|
321
|
+
* @returns Number of messages deleted
|
|
322
|
+
*/
|
|
323
|
+
deleteMessages(uids: number[]): Promise<number>;
|
|
324
|
+
/**
|
|
325
|
+
* Get mailbox status without opening it.
|
|
326
|
+
* Uses IMAP STATUS command to get message counts.
|
|
327
|
+
*
|
|
328
|
+
* @param mailboxPath - Full path to the mailbox
|
|
329
|
+
* @returns Mailbox status including unseen count
|
|
330
|
+
*/
|
|
331
|
+
getMailboxStatus(mailboxPath: string): Promise<ImapMailboxStatus>;
|
|
332
|
+
/**
|
|
333
|
+
* Append a message to a mailbox via IMAP APPEND command.
|
|
334
|
+
*
|
|
335
|
+
* @param mailbox - Full path of the target mailbox
|
|
336
|
+
* @param message - RFC 822 message content
|
|
337
|
+
* @param flags - Optional flags to set on the message
|
|
338
|
+
* @returns Object with destination path, uidValidity, and uid of the appended message
|
|
339
|
+
*/
|
|
340
|
+
append(
|
|
341
|
+
mailbox: string,
|
|
342
|
+
message: string | Buffer,
|
|
343
|
+
flags?: string[],
|
|
344
|
+
): Promise<{
|
|
345
|
+
destination: string;
|
|
346
|
+
uidValidity: number;
|
|
347
|
+
uid: number;
|
|
348
|
+
}>;
|
|
349
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
From: alice@example.org
|
|
2
|
+
To: bob@example.org
|
|
3
|
+
Subject: Flat text/plain fixture 01
|
|
4
|
+
Date: Thu, 28 May 2026 09:00:00 +0000
|
|
5
|
+
Message-Id: <fixture-01@example.org>
|
|
6
|
+
MIME-Version: 1.0
|
|
7
|
+
Content-Type: text/plain; charset=us-ascii
|
|
8
|
+
Content-Transfer-Encoding: 7bit
|
|
9
|
+
|
|
10
|
+
Hello bob, this is a flat text/plain message.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
From: alice@example.org
|
|
2
|
+
To: bob@example.org
|
|
3
|
+
Subject: Flat text/html fixture 02
|
|
4
|
+
Date: Thu, 28 May 2026 09:00:00 +0000
|
|
5
|
+
Message-Id: <fixture-02@example.org>
|
|
6
|
+
MIME-Version: 1.0
|
|
7
|
+
Content-Type: text/html; charset=us-ascii
|
|
8
|
+
Content-Transfer-Encoding: 7bit
|
|
9
|
+
|
|
10
|
+
<html><body><p>Hello bob, this is a flat <b>text/html</b> message.</p></body></html>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"partPath": "0",
|
|
4
|
+
"isMultipart": true,
|
|
5
|
+
"mediaType": "MULTIPART",
|
|
6
|
+
"mediaSubtype": "alternative"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"partPath": "1",
|
|
10
|
+
"isMultipart": false,
|
|
11
|
+
"mediaType": "TEXT",
|
|
12
|
+
"mediaSubtype": "plain"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"partPath": "2",
|
|
16
|
+
"isMultipart": false,
|
|
17
|
+
"mediaType": "TEXT",
|
|
18
|
+
"mediaSubtype": "html"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
From: alice@example.org
|
|
2
|
+
To: bob@example.org
|
|
3
|
+
Subject: Alternative text+html fixture 03
|
|
4
|
+
Date: Thu, 28 May 2026 09:00:00 +0000
|
|
5
|
+
Message-Id: <fixture-03@example.org>
|
|
6
|
+
MIME-Version: 1.0
|
|
7
|
+
Content-Type: multipart/alternative; boundary="=_boundary_03_="
|
|
8
|
+
|
|
9
|
+
--=_boundary_03_=
|
|
10
|
+
Content-Type: text/plain; charset=us-ascii
|
|
11
|
+
Content-Transfer-Encoding: 7bit
|
|
12
|
+
|
|
13
|
+
Hello bob, plain text body.
|
|
14
|
+
--=_boundary_03_=
|
|
15
|
+
Content-Type: text/html; charset=us-ascii
|
|
16
|
+
Content-Transfer-Encoding: 7bit
|
|
17
|
+
|
|
18
|
+
<html><body><p>Hello bob, html body.</p></body></html>
|
|
19
|
+
--=_boundary_03_=--
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pairs": [
|
|
3
|
+
{
|
|
4
|
+
"partPath": "1",
|
|
5
|
+
"contentType": "text/plain",
|
|
6
|
+
"contentSha256": "54178e683cea517723189e2851efd8449bac73f2b57c50bd81a0c8a6df4ce7c8",
|
|
7
|
+
"contentLength": 27
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"partPath": "2",
|
|
11
|
+
"contentType": "text/html",
|
|
12
|
+
"contentSha256": "71744f8d83087de2acde32d5b9ec93ecfe1b5ba677f760655643dabfd0ce661d",
|
|
13
|
+
"contentLength": 54
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"partPath": "0",
|
|
4
|
+
"isMultipart": true,
|
|
5
|
+
"mediaType": "MULTIPART",
|
|
6
|
+
"mediaSubtype": "mixed"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"partPath": "1",
|
|
10
|
+
"isMultipart": false,
|
|
11
|
+
"mediaType": "TEXT",
|
|
12
|
+
"mediaSubtype": "html"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"partPath": "2",
|
|
16
|
+
"isMultipart": false,
|
|
17
|
+
"mediaType": "APPLICATION",
|
|
18
|
+
"mediaSubtype": "pdf",
|
|
19
|
+
"dispositionFilename": "invoice.pdf",
|
|
20
|
+
"disposition": "attachment"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
From: alice@example.org
|
|
2
|
+
To: bob@example.org
|
|
3
|
+
Subject: Mixed html + pdf fixture 04
|
|
4
|
+
Date: Thu, 28 May 2026 09:00:00 +0000
|
|
5
|
+
Message-Id: <fixture-04@example.org>
|
|
6
|
+
MIME-Version: 1.0
|
|
7
|
+
Content-Type: multipart/mixed; boundary="=_boundary_04_="
|
|
8
|
+
|
|
9
|
+
--=_boundary_04_=
|
|
10
|
+
Content-Type: text/html; charset=us-ascii
|
|
11
|
+
Content-Transfer-Encoding: 7bit
|
|
12
|
+
|
|
13
|
+
<html><body><p>See attached invoice.</p></body></html>
|
|
14
|
+
--=_boundary_04_=
|
|
15
|
+
Content-Type: application/pdf; name="invoice.pdf"
|
|
16
|
+
Content-Transfer-Encoding: base64
|
|
17
|
+
Content-Disposition: attachment; filename="invoice.pdf"
|
|
18
|
+
|
|
19
|
+
JVBERi0xLjQKJWZha2UgcGRmIGZvciBmaXh0dXJlIDA0CiVFT0YK
|
|
20
|
+
--=_boundary_04_=--
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pairs": [
|
|
3
|
+
{
|
|
4
|
+
"partPath": "1",
|
|
5
|
+
"contentType": "text/html",
|
|
6
|
+
"contentSha256": "d8d4d207e4f8d121a1d60fe74922da8b562ebada6a4a58b2d0f2b1bcac2ae5d7",
|
|
7
|
+
"contentLength": 54
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"partPath": "2",
|
|
11
|
+
"contentType": "application/pdf",
|
|
12
|
+
"contentSha256": "1e82c5ee13ccddf36b4abc368c682a08ed1bc5671101c76062183c8e9914fb41",
|
|
13
|
+
"contentLength": 39
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"partPath": "0",
|
|
4
|
+
"isMultipart": true,
|
|
5
|
+
"mediaType": "MULTIPART",
|
|
6
|
+
"mediaSubtype": "mixed"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"partPath": "1",
|
|
10
|
+
"isMultipart": false,
|
|
11
|
+
"mediaType": "TEXT",
|
|
12
|
+
"mediaSubtype": "html"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"partPath": "2",
|
|
16
|
+
"isMultipart": false,
|
|
17
|
+
"mediaType": "APPLICATION",
|
|
18
|
+
"mediaSubtype": "octet-stream",
|
|
19
|
+
"dispositionFilename": "contract.pdf",
|
|
20
|
+
"disposition": "attachment"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
From: alice@example.org
|
|
2
|
+
To: bob@example.org
|
|
3
|
+
Subject: Mixed html + octet-stream PDF fixture 05 (Odido bug)
|
|
4
|
+
Date: Thu, 28 May 2026 09:00:00 +0000
|
|
5
|
+
Message-Id: <fixture-05@example.org>
|
|
6
|
+
MIME-Version: 1.0
|
|
7
|
+
Content-Type: multipart/mixed; boundary="=_boundary_05_="
|
|
8
|
+
|
|
9
|
+
--=_boundary_05_=
|
|
10
|
+
Content-Type: text/html; charset=us-ascii
|
|
11
|
+
Content-Transfer-Encoding: 7bit
|
|
12
|
+
|
|
13
|
+
<html><body><p>Your contract PDF is attached.</p></body></html>
|
|
14
|
+
--=_boundary_05_=
|
|
15
|
+
Content-Type: application/octet-stream; name="contract.pdf"
|
|
16
|
+
Content-Transfer-Encoding: base64
|
|
17
|
+
Content-Disposition: attachment; filename="contract.pdf"
|
|
18
|
+
|
|
19
|
+
JVBERi0xLjQKJWZha2UgcGRmIGZvciBmaXh0dXJlIDA1IG9jdGV0LXN0cmVhbQol
|
|
20
|
+
RU9GCg==
|
|
21
|
+
--=_boundary_05_=--
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pairs": [
|
|
3
|
+
{
|
|
4
|
+
"partPath": "1",
|
|
5
|
+
"contentType": "text/html",
|
|
6
|
+
"contentSha256": "9fd8608290b5e5c88848e336235fbcf48d4797290c631b9f73bb9f13c7ce3515",
|
|
7
|
+
"contentLength": 63
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"partPath": "2",
|
|
11
|
+
"contentType": "application/octet-stream",
|
|
12
|
+
"contentSha256": "649331fa0bcf7487ba5529a34be1b2629f76e7906548321283d9255ad4ef53eb",
|
|
13
|
+
"contentLength": 52
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"partPath": "0",
|
|
4
|
+
"isMultipart": true,
|
|
5
|
+
"mediaType": "MULTIPART",
|
|
6
|
+
"mediaSubtype": "related"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"partPath": "1",
|
|
10
|
+
"isMultipart": false,
|
|
11
|
+
"mediaType": "TEXT",
|
|
12
|
+
"mediaSubtype": "html"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"partPath": "2",
|
|
16
|
+
"isMultipart": false,
|
|
17
|
+
"mediaType": "IMAGE",
|
|
18
|
+
"mediaSubtype": "png",
|
|
19
|
+
"contentId": "logo@example.org",
|
|
20
|
+
"dispositionFilename": "logo.png",
|
|
21
|
+
"disposition": "inline"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
From: alice@example.org
|
|
2
|
+
To: bob@example.org
|
|
3
|
+
Subject: Related html + cid image fixture 06
|
|
4
|
+
Date: Thu, 28 May 2026 09:00:00 +0000
|
|
5
|
+
Message-Id: <fixture-06@example.org>
|
|
6
|
+
MIME-Version: 1.0
|
|
7
|
+
Content-Type: multipart/related; boundary="=_boundary_06_="; type="text/html"
|
|
8
|
+
|
|
9
|
+
--=_boundary_06_=
|
|
10
|
+
Content-Type: text/html; charset=us-ascii
|
|
11
|
+
Content-Transfer-Encoding: 7bit
|
|
12
|
+
|
|
13
|
+
<html><body><p>Inline image: <img src="cid:logo@example.org"></p></body></html>
|
|
14
|
+
--=_boundary_06_=
|
|
15
|
+
Content-Type: image/png; name="logo.png"
|
|
16
|
+
Content-Transfer-Encoding: base64
|
|
17
|
+
Content-Disposition: inline; filename="logo.png"
|
|
18
|
+
Content-ID: <logo@example.org>
|
|
19
|
+
|
|
20
|
+
iVBORw0KGgpmaXh0dXJlMDZwbmdieXRlcw==
|
|
21
|
+
--=_boundary_06_=--
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"pairs": [
|
|
3
|
+
{
|
|
4
|
+
"partPath": "1",
|
|
5
|
+
"contentType": "text/html",
|
|
6
|
+
"contentSha256": "212c7f36413be9efaf441c588da97719f947a2210f76c98d05f21ad7480f1c8a",
|
|
7
|
+
"contentLength": 117
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"partPath": "2",
|
|
11
|
+
"contentType": "image/png",
|
|
12
|
+
"contentSha256": "c5a79659b71cb6866a73f688ef63a29e163065f6dbb6aba3da932bc872e475d6",
|
|
13
|
+
"contentLength": 25
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|