dsh-email 0.10.7 → 0.12.0
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.en.md +86 -8
- package/README.md +66 -9
- package/lib/client.js +2344 -191
- package/lib/config.d.ts +161 -1
- package/lib/config.js +244 -11
- package/lib/index.d.ts +6 -2
- package/lib/index.js +3 -2
- package/lib/mail-client.d.ts +102 -3
- package/lib/mail-client.js +318 -73
- package/lib/oauth2.d.ts +124 -0
- package/lib/oauth2.js +417 -0
- package/lib/runtime.js +28 -8
- package/lib/settings.d.ts +22 -1
- package/lib/settings.js +122 -34
- package/lib/tool-contract.d.ts +3 -0
- package/lib/tool-contract.js +11 -2
- package/lib/tools.js +22 -6
- package/lib/types.d.ts +7 -0
- package/lib/web.d.ts +209 -2
- package/lib/web.js +917 -14
- package/package.json +1 -1
package/lib/mail-client.d.ts
CHANGED
|
@@ -5,6 +5,62 @@ export declare class MailError extends Error {
|
|
|
5
5
|
constructor(message: string);
|
|
6
6
|
}
|
|
7
7
|
export declare function messageOf(error: unknown, fallback: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Replace anything credential-shaped in a server's own error text before it
|
|
10
|
+
* reaches a user.
|
|
11
|
+
*
|
|
12
|
+
* IMAP and SMTP servers routinely quote back the authentication string they
|
|
13
|
+
* rejected. For XOAUTH2 that string is `user=…\x01auth=Bearer <token>\x01\x01`,
|
|
14
|
+
* usually base64'd — so the raw message carries a live access token, and these
|
|
15
|
+
* messages are rendered in the settings panel, returned by the mail tools, and
|
|
16
|
+
* pasted into bug reports.
|
|
17
|
+
*
|
|
18
|
+
* Two shapes are masked: a JWT (three base64url segments, which is what every
|
|
19
|
+
* OAuth2 access token looks like) and a long base64 run (the quoted XOAUTH2
|
|
20
|
+
* blob). The replacement keeps the length so a report still says how big the
|
|
21
|
+
* thing was, without saying what it was.
|
|
22
|
+
*/
|
|
23
|
+
export declare function redactCredentials(text: string): string;
|
|
24
|
+
/** The IMAP auth shape imapflow accepts: a password, or an OAuth2 access token. */
|
|
25
|
+
export interface ImapAuth {
|
|
26
|
+
user: string;
|
|
27
|
+
pass?: string;
|
|
28
|
+
accessToken?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The SMTP auth shape nodemailer accepts. `type` is the literal union
|
|
32
|
+
* nodemailer's typings model, not a loose string: anything wider makes the
|
|
33
|
+
* whole transport options object fail to match and silently degrades the type.
|
|
34
|
+
*/
|
|
35
|
+
export type SmtpAuth = {
|
|
36
|
+
user: string;
|
|
37
|
+
pass: string;
|
|
38
|
+
} | {
|
|
39
|
+
type: 'OAuth2';
|
|
40
|
+
user: string;
|
|
41
|
+
accessToken: string;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* The IMAP `auth` block for one account. Pure so the shape the library
|
|
45
|
+
* receives is testable without a socket: an OAuth2 account authenticates with
|
|
46
|
+
* `accessToken` (imapflow then runs AUTHENTICATE XOAUTH2) and a password
|
|
47
|
+
* account with `pass`, exactly as before.
|
|
48
|
+
*/
|
|
49
|
+
export declare function imapAuthOf(cfg: Pick<ResolvedEmailConfig, 'authUser' | 'authPassword' | 'authKind'>, accessToken?: string): ImapAuth;
|
|
50
|
+
/**
|
|
51
|
+
* Nodemailer consumes an OAuth2 token through accessToken, not pass.
|
|
52
|
+
* Refresh remains owned by this plugin; no refresh credentials leave here.
|
|
53
|
+
*/
|
|
54
|
+
export declare function smtpAuthOf(cfg: Pick<ResolvedEmailConfig, 'authUser' | 'authPassword' | 'authKind'>, accessToken?: string): SmtpAuth;
|
|
55
|
+
/** The message an OAuth2 account gets when the mailbox has to be logged into again. */
|
|
56
|
+
export declare const OAUTH2_RELOGIN_MESSAGE = "\u90AE\u7BB1\u767B\u5F55\u5931\u8D25\uFF1A\u8BF7\u5230\u8BBE\u7F6E\u9875\u91CD\u65B0\u767B\u5F55\uFF08Microsoft \u8D26\u53F7\u4F7F\u7528\u8BBE\u5907\u7801\u767B\u5F55\uFF0C\u4E0D\u4F7F\u7528\u6388\u6743\u7801\uFF09";
|
|
57
|
+
/**
|
|
58
|
+
* True for the errors both libraries report when the server rejects the
|
|
59
|
+
* credentials. An expired access token is indistinguishable from a wrong
|
|
60
|
+
* password at this level, so the connection retries once with a forced refresh
|
|
61
|
+
* before it believes the token is really dead.
|
|
62
|
+
*/
|
|
63
|
+
export declare function looksLikeAuthFailure(error: unknown): boolean;
|
|
8
64
|
interface AttachmentPart {
|
|
9
65
|
part: string;
|
|
10
66
|
filename: string;
|
|
@@ -50,7 +106,7 @@ export declare function extractMessageIds(source: Buffer): {
|
|
|
50
106
|
* be tested without a connection: recipients exclude the sending account,
|
|
51
107
|
* subject prefixes never stack, the original text is quoted underneath.
|
|
52
108
|
*/
|
|
53
|
-
export declare function buildReplyMessage(original: OriginalDigest, mode: EmailReplyMode, selfAddress: string, text: string, forwardTo?: string): BuiltReply;
|
|
109
|
+
export declare function buildReplyMessage(original: OriginalDigest, mode: EmailReplyMode, selfAddress: string | readonly string[], text: string, forwardTo?: string): BuiltReply;
|
|
54
110
|
/**
|
|
55
111
|
* One mailbox pool for the whole plugin: pooled IMAP connections per
|
|
56
112
|
* account plus pooled SMTP transporters, with idle sweep and error eviction.
|
|
@@ -66,19 +122,62 @@ export declare class EmailPool {
|
|
|
66
122
|
resolveName(name?: string): string;
|
|
67
123
|
/** Serialize operations per account: one IMAP connection serves one op at a time. */
|
|
68
124
|
private enqueue;
|
|
125
|
+
private readonly readCache;
|
|
126
|
+
private readonly folderCache;
|
|
127
|
+
/** Remember a parsed attachment index so email_attachment can skip the refetch. */
|
|
128
|
+
private rememberRead;
|
|
129
|
+
/**
|
|
130
|
+
* The attachment index for one message: the cached one when email_read already
|
|
131
|
+
* produced it, otherwise a fresh parse of the full source plus its bodyStructure.
|
|
132
|
+
*/
|
|
133
|
+
private attachmentIndexOf;
|
|
134
|
+
private recallRead;
|
|
69
135
|
withImap<T>(accountName: string | undefined, folder: string | null, run: (client: ImapFlow) => Promise<T>, readOnly?: boolean, signal?: AbortSignal): Promise<T>;
|
|
70
136
|
private createImap;
|
|
137
|
+
/**
|
|
138
|
+
* Dial and authenticate one fresh IMAP connection.
|
|
139
|
+
*
|
|
140
|
+
* A password account connects once. An OAuth2 account connects with a fresh
|
|
141
|
+
* access token and, when the server rejects it, refreshes once and tries
|
|
142
|
+
* again: a token that expired between the freshness check and the dial is
|
|
143
|
+
* indistinguishable from a wrong password at the socket, and guessing wrong
|
|
144
|
+
* would send the user through a browser login for nothing.
|
|
145
|
+
*/
|
|
146
|
+
private connectImap;
|
|
147
|
+
/** The token store's own errors are already actionable; never dress them as IMAP failures. */
|
|
148
|
+
private oauth2ErrorOf;
|
|
71
149
|
private imapRun;
|
|
72
150
|
private normalizeImapError;
|
|
73
151
|
private evictImap;
|
|
74
152
|
/** Reap IMAP connections idle for longer than idleTimeoutMs. */
|
|
75
153
|
startIdleSweep(): void;
|
|
76
154
|
dispose(): void;
|
|
155
|
+
/**
|
|
156
|
+
* A pooled transporter for one account. The token is captured when the
|
|
157
|
+
* transporter is built; an OAuth2 token that turns out to be stale is
|
|
158
|
+
* re-minted in sendMail, which rebuilds the transporter.
|
|
159
|
+
*/
|
|
77
160
|
private transporter;
|
|
78
|
-
|
|
161
|
+
private dropTransporter;
|
|
162
|
+
/**
|
|
163
|
+
* Send through the pooled transporter while making cancellation close it.
|
|
164
|
+
*
|
|
165
|
+
* An OAuth2 transporter carries a token that was minted when it was built,
|
|
166
|
+
* so a rejection is retried once against a freshly built one (and a fresh
|
|
167
|
+
* form of whatever stored token state exists). Password accounts keep the
|
|
168
|
+
* single attempt they always had.
|
|
169
|
+
*/
|
|
79
170
|
private sendMail;
|
|
80
171
|
list(accountName: string | undefined, folder: string, limit: number, offset: number, unreadOnly: boolean, since?: Date, until?: Date, signal?: AbortSignal): Promise<EmailListResult>;
|
|
81
|
-
search(accountName: string | undefined, query: string, folder: string, limit: number, since?: Date, until?: Date, signal?: AbortSignal): Promise<EmailSearchResult>;
|
|
172
|
+
search(accountName: string | undefined, query: string, folder: string, limit: number, offset: number, since?: Date, until?: Date, signal?: AbortSignal): Promise<EmailSearchResult>;
|
|
173
|
+
/**
|
|
174
|
+
* Confirm server-side hits against the mailbox itself: fetch the envelopes
|
|
175
|
+
* of the newest candidates — the same window the body-scan fallback looks at
|
|
176
|
+
* — and keep only those that really carry the query in subject/from/to/cc,
|
|
177
|
+
* the four fields the server was asked about. No body is downloaded here,
|
|
178
|
+
* and uids the server made up simply return nothing.
|
|
179
|
+
*/
|
|
180
|
+
private searchHits;
|
|
82
181
|
/** Client-side scan of the tail of the mailbox, newest first. */
|
|
83
182
|
private searchBodies;
|
|
84
183
|
private fetchListed;
|