@bumail/smtp 0.11.1 → 0.12.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 +30 -2
- package/dist/client/connect.d.ts +18 -0
- package/dist/client/connect.d.ts.map +1 -0
- package/dist/client/inbox.d.ts +2 -0
- package/dist/client/inbox.d.ts.map +1 -1
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +679 -344
- package/dist/client/index.js.map +20 -11
- package/dist/client/options.d.ts +2 -0
- package/dist/client/options.d.ts.map +1 -1
- package/dist/client/pool/idle.d.ts +10 -0
- package/dist/client/pool/idle.d.ts.map +1 -0
- package/dist/client/pool/index.d.ts +18 -0
- package/dist/client/pool/index.d.ts.map +1 -0
- package/dist/client/pool/options.d.ts +26 -0
- package/dist/client/pool/options.d.ts.map +1 -0
- package/dist/client/pool/reset.d.ts +9 -0
- package/dist/client/pool/reset.d.ts.map +1 -0
- package/dist/client/pool/send.d.ts +8 -0
- package/dist/client/pool/send.d.ts.map +1 -0
- package/dist/client/pool/state.d.ts +28 -0
- package/dist/client/pool/state.d.ts.map +1 -0
- package/dist/client/send.d.ts +10 -1
- package/dist/client/send.d.ts.map +1 -1
- package/dist/client/session.d.ts +2 -2
- package/dist/client/session.d.ts.map +1 -1
- package/dist/client/settings.d.ts +11 -5
- package/dist/client/settings.d.ts.map +1 -1
- package/dist/client/socket-events.d.ts +15 -0
- package/dist/client/socket-events.d.ts.map +1 -0
- package/dist/client/socket-waits.d.ts +36 -0
- package/dist/client/socket-waits.d.ts.map +1 -0
- package/dist/client/socket.d.ts +14 -4
- package/dist/client/socket.d.ts.map +1 -1
- package/dist/client/transaction.d.ts +9 -3
- package/dist/client/transaction.d.ts.map +1 -1
- package/docs/README.md +3 -3
- package/docs/guide.md +25 -3
- package/docs/roadmap.md +10 -4
- package/docs/troubleshooting.md +84 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/client/options.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/client/options.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Clock } from '../target';
|
|
2
|
+
import { type Identity } from './options';
|
|
3
|
+
import { type Entry, type PoolState } from './state';
|
|
4
|
+
/** Takes `entry` out of the idle list and stops its timer. */
|
|
5
|
+
export declare function forget(state: PoolState, entry: Entry): void;
|
|
6
|
+
/** Keeps the connection for the next message, or says goodbye when it is spent. */
|
|
7
|
+
export declare function release(state: PoolState, entry: Entry): void;
|
|
8
|
+
/** An idle connection to the same server, reset; none when no one is fit. */
|
|
9
|
+
export declare function borrow(state: PoolState, identity: Identity, seconds: number, clock: Clock): Promise<Entry | undefined>;
|
|
10
|
+
//# sourceMappingURL=idle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idle.d.ts","sourceRoot":"","sources":["../../../src/client/pool/idle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,KAAK,QAAQ,EAAgB,MAAM,WAAW,CAAC;AAExD,OAAO,EAAQ,KAAK,KAAK,EAAU,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAEnE,8DAA8D;AAC9D,wBAAgB,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAI3D;AAED,mFAAmF;AACnF,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAwB5D;AAED,6EAA6E;AAC7E,wBAAsB,MAAM,CAC3B,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,KAAK,GACV,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAa5B"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { MessageSource, SendMailOptions, SendMailResult } from '../options';
|
|
2
|
+
import { type SmtpPoolOptions } from './options';
|
|
3
|
+
export type { SmtpPoolOptions } from './options';
|
|
4
|
+
/** A pool of SMTP connections: a delivery takes an idle one to the same server, as `sendMail` would open, or opens one. */
|
|
5
|
+
export interface SmtpPool {
|
|
6
|
+
/** `sendMail`, over a pooled connection. `result.reused` tells whether one carried a message before. */
|
|
7
|
+
sendMail(message: MessageSource, options: SendMailOptions): Promise<SendMailResult>;
|
|
8
|
+
/** QUITs the idle connections, waits for the busy ones, and refuses any further `sendMail` with `STOPPED`. */
|
|
9
|
+
close(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Creates a pool. Connections are kept apart by what they are: server
|
|
13
|
+
* address, greeting name, TLS and credentials. One is reset (RSET) when it
|
|
14
|
+
* is lent, and closed after a 421, a timeout, a bad reply, a lost stream,
|
|
15
|
+
* or bytes it was not asked for.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createSmtpPool(options?: SmtpPoolOptions): SmtpPool;
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/pool/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,aAAa,EACb,eAAe,EACf,cAAc,EACd,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAY,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AAI3D,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjD,2HAA2H;AAC3H,MAAM,WAAW,QAAQ;IACxB,wGAAwG;IACxG,QAAQ,CACP,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,eAAe,GACtB,OAAO,CAAC,cAAc,CAAC,CAAC;IAC3B,8GAA8G;IAC9G,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,QAAQ,CAiClE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ConnectionSettings } from '../settings';
|
|
2
|
+
import type { Endpoint } from '../target';
|
|
3
|
+
/** How a pool keeps its connections. */
|
|
4
|
+
export interface SmtpPoolOptions {
|
|
5
|
+
/** Messages one connection carries before it is closed. Default 100. */
|
|
6
|
+
readonly maxMessages?: number;
|
|
7
|
+
/** Seconds an idle connection waits for another message. Default 5. */
|
|
8
|
+
readonly idleSeconds?: number;
|
|
9
|
+
/** Seconds a connection lives, from its opening. Default 300. */
|
|
10
|
+
readonly maxAgeSeconds?: number;
|
|
11
|
+
/** Idle connections kept per mail host. Default 4. */
|
|
12
|
+
readonly maxIdlePerHost?: number;
|
|
13
|
+
}
|
|
14
|
+
/** `SmtpPoolOptions` checked, with their defaults. */
|
|
15
|
+
export type PoolLimits = Required<SmtpPoolOptions>;
|
|
16
|
+
/** Checks the options; throws `INVALID_OPTION`. */
|
|
17
|
+
export declare function limitsOf(options?: SmtpPoolOptions): PoolLimits;
|
|
18
|
+
/** What a connection is: where it goes and how it greets, secures and authenticates. Timeouts and the envelope are the message's. */
|
|
19
|
+
export interface Identity extends Endpoint {
|
|
20
|
+
readonly connection: Omit<ConnectionSettings, 'timeouts'>;
|
|
21
|
+
}
|
|
22
|
+
/** Whether two connections are the same, field by field: credentials included, and never joined into a string key. */
|
|
23
|
+
export declare function sameIdentity(a: Identity, b: Identity): boolean;
|
|
24
|
+
/** The identity of a delivery to `endpoint`. */
|
|
25
|
+
export declare const identityOf: (endpoint: Endpoint, settings: ConnectionSettings) => Identity;
|
|
26
|
+
//# sourceMappingURL=options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../src/client/pool/options.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,kBAAkB,EAAe,MAAM,aAAa,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1C,wCAAwC;AACxC,MAAM,WAAW,eAAe;IAC/B,wEAAwE;IACxE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,uEAAuE;IACvE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,sDAAsD;IACtD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,sDAAsD;AACtD,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC;AAwBnD,mDAAmD;AACnD,wBAAgB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,UAAU,CASlE;AAED,qIAAqI;AACrI,MAAM,WAAW,QAAS,SAAQ,QAAQ;IACzC,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;CAC1D;AAoBD,sHAAsH;AACtH,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAc9D;AAED,gDAAgD;AAChD,eAAO,MAAM,UAAU,GACtB,UAAU,QAAQ,EAClB,UAAU,kBAAkB,KAC1B,QAYD,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Clock } from '../target';
|
|
2
|
+
import type { Entry } from './state';
|
|
3
|
+
/** Seconds to wait for the reply to an RSET, at most: a silent dead connection is stale, not worth 300 s. */
|
|
4
|
+
export declare const RESET_WAIT = 5;
|
|
5
|
+
/** RSET, answered 250 within `seconds`; false when the connection is no good. */
|
|
6
|
+
export declare function reset(entry: Entry, clock: Clock, seconds: number): Promise<boolean>;
|
|
7
|
+
/** A reused connection that died before the server answered MAIL FROM: nothing was taken. */
|
|
8
|
+
export declare const stale: (entry: Entry, before: number, error: unknown) => boolean;
|
|
9
|
+
//# sourceMappingURL=reset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reset.d.ts","sourceRoot":"","sources":["../../../src/client/pool/reset.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,6GAA6G;AAC7G,eAAO,MAAM,UAAU,IAAI,CAAC;AAE5B,iFAAiF;AACjF,wBAAsB,KAAK,CAC1B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED,6FAA6F;AAC7F,eAAO,MAAM,KAAK,GAAI,OAAO,KAAK,EAAE,QAAQ,MAAM,EAAE,OAAO,OAAO,KAAG,OAKrC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Content } from '../content';
|
|
2
|
+
import type { SendMailResult } from '../options';
|
|
3
|
+
import type { ClientSettings } from '../settings';
|
|
4
|
+
import { Clock, type Endpoint } from '../target';
|
|
5
|
+
import { type PoolState } from './state';
|
|
6
|
+
/** One delivery to one server address, over a pooled connection when there is one. */
|
|
7
|
+
export declare function attempt(state: PoolState, endpoint: Endpoint, settings: ClientSettings, content: Content, clock: Clock): Promise<SendMailResult>;
|
|
8
|
+
//# sourceMappingURL=send.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../../../src/client/pool/send.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAKjD,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AA+C7D,sFAAsF;AACtF,wBAAsB,OAAO,CAC5B,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,GACV,OAAO,CAAC,cAAc,CAAC,CAqBzB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Ready } from '../session';
|
|
2
|
+
import type { ClientSocket } from '../socket';
|
|
3
|
+
import type { Identity, PoolLimits } from './options';
|
|
4
|
+
/** A connection past its opening, between messages or lent. */
|
|
5
|
+
export interface Entry {
|
|
6
|
+
readonly socket: ClientSocket;
|
|
7
|
+
readonly ready: Ready;
|
|
8
|
+
readonly identity: Identity;
|
|
9
|
+
readonly born: number;
|
|
10
|
+
messages: number;
|
|
11
|
+
timer?: Timer;
|
|
12
|
+
}
|
|
13
|
+
/** What a pool holds; the functions of this folder act on it. */
|
|
14
|
+
export interface PoolState {
|
|
15
|
+
readonly limits: PoolLimits;
|
|
16
|
+
readonly idle: Entry[];
|
|
17
|
+
/** Messages in flight. */
|
|
18
|
+
readonly busy: Set<Promise<unknown>>;
|
|
19
|
+
/** Connections of aborted messages being reset in the background. */
|
|
20
|
+
readonly resetting: Set<Promise<void>>;
|
|
21
|
+
closed: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare const newState: (limits: PoolLimits) => PoolState;
|
|
24
|
+
/** Closes a connection, saying QUIT first unless `polite` is false (it is dead or out of step). */
|
|
25
|
+
export declare function hangUp(entry: Entry, polite?: boolean): void;
|
|
26
|
+
/** Whether the connection outlived `maxAgeSeconds`. */
|
|
27
|
+
export declare const aged: (state: PoolState, entry: Entry) => boolean;
|
|
28
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/client/pool/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEtD,+DAA+D;AAC/D,MAAM,WAAW,KAAK;IACrB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;CACd;AAED,iEAAiE;AACjE,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;IACvB,0BAA0B;IAC1B,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IACrC,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACvC,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,eAAO,MAAM,QAAQ,GAAI,QAAQ,UAAU,KAAG,SAM5C,CAAC;AAEH,mGAAmG;AACnG,wBAAgB,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,UAAO,GAAG,IAAI,CAIxD;AAED,uDAAuD;AACvD,eAAO,MAAM,IAAI,GAAI,OAAO,SAAS,EAAE,OAAO,KAAK,KAAG,OACc,CAAC"}
|
package/dist/client/send.d.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type Content } from './content';
|
|
2
|
+
import type { MessageSource, MxDestination, SendMailOptions, SendMailResult } from './options';
|
|
3
|
+
import { type ClientSettings } from './settings';
|
|
4
|
+
import { Clock, type Endpoint } from './target';
|
|
2
5
|
/** The most addresses one delivery tries, every MX host together; and the most MX hosts it looks up. */
|
|
3
6
|
export declare const MAX_ADDRESSES = 10;
|
|
7
|
+
/** One delivery to one server address: a fresh session here, a pooled one in `pool.ts`. */
|
|
8
|
+
export type Attempt = (endpoint: Endpoint, settings: ClientSettings, content: Content, clock: Clock) => Promise<SendMailResult>;
|
|
9
|
+
/** Direct delivery, by `try_`: each MX host's addresses in turn, while the failures are temporary and before MAIL FROM. */
|
|
10
|
+
export declare function viaMx(try_: Attempt, options: MxDestination, settings: ClientSettings, content: Content, clock: Clock): Promise<SendMailResult>;
|
|
11
|
+
/** Where the options send the message: a host, or a domain's mail hosts by MX; each address is tried by `try_`. */
|
|
12
|
+
export declare function deliver(try_: Attempt, options: SendMailOptions, settings: ClientSettings, content: Content, clock: Clock): Promise<SendMailResult>;
|
|
4
13
|
/**
|
|
5
14
|
* Delivers one message to one destination: a host (`{ host, port }`, a
|
|
6
15
|
* smarthost or a submission server) or a domain's mail hosts (`{ domain,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../../src/client/send.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../../src/client/send.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,OAAO,EAAa,MAAM,WAAW,CAAC;AAEpD,OAAO,KAAK,EACX,aAAa,EACb,aAAa,EACb,eAAe,EACf,cAAc,EACd,MAAM,WAAW,CAAC;AAEnB,OAAO,EAEN,KAAK,cAAc,EAInB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,EAAE,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGhD,wGAAwG;AACxG,eAAO,MAAM,aAAa,KAAK,CAAC;AAKhC,2FAA2F;AAC3F,MAAM,MAAM,OAAO,GAAG,CACrB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,KACR,OAAO,CAAC,cAAc,CAAC,CAAC;AAmC7B,2HAA2H;AAC3H,wBAAsB,KAAK,CAC1B,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,GACV,OAAO,CAAC,cAAc,CAAC,CAgDzB;AAED,mHAAmH;AACnH,wBAAgB,OAAO,CACtB,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,GACV,OAAO,CAAC,cAAc,CAAC,CASzB;AAED;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAC7B,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,eAAe,GACtB,OAAO,CAAC,cAAc,CAAC,CAUzB"}
|
package/dist/client/session.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ConnectionSettings } from './settings';
|
|
2
2
|
import type { ClientSocket } from './socket';
|
|
3
3
|
import type { TlsTarget } from './target';
|
|
4
4
|
/** A session past its greeting, EHLO, STARTTLS and AUTH: what the server offers. */
|
|
@@ -7,5 +7,5 @@ export interface Ready {
|
|
|
7
7
|
readonly authenticated: boolean;
|
|
8
8
|
}
|
|
9
9
|
/** The greeting, EHLO, STARTTLS and AUTH: a session ready for MAIL FROM. */
|
|
10
|
-
export declare function open(socket: ClientSocket, settings:
|
|
10
|
+
export declare function open(socket: ClientSocket, settings: ConnectionSettings, tls: TlsTarget): Promise<Ready>;
|
|
11
11
|
//# sourceMappingURL=session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/client/session.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/client/session.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,oFAAoF;AACpF,MAAM,WAAW,KAAK;IACrB,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjD,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CAChC;AAqGD,4EAA4E;AAC5E,wBAAsB,IAAI,CACzB,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,kBAAkB,EAC5B,GAAG,EAAE,SAAS,GACZ,OAAO,CAAC,KAAK,CAAC,CAchB"}
|
|
@@ -3,22 +3,28 @@ import { type DsnSettings } from './dsn';
|
|
|
3
3
|
import type { MxDestination, SendMailAuth, SendMailEnvelope, SendMailOptions, SendMailTimeouts, TlsMode } from './options';
|
|
4
4
|
/** The longest delay `setTimeout` takes, in seconds: past 2^31 − 1 ms it fires after 1 ms. */
|
|
5
5
|
export declare const MAX_SECONDS: number;
|
|
6
|
-
/**
|
|
7
|
-
export interface
|
|
8
|
-
readonly from: string;
|
|
9
|
-
readonly to: readonly string[];
|
|
6
|
+
/** What a connection is: where it greets from, how it secures and authenticates, how long each wait lasts (seconds). Shared by every message sent over it. */
|
|
7
|
+
export interface ConnectionSettings {
|
|
10
8
|
readonly helo: string;
|
|
11
9
|
readonly tls: TlsMode;
|
|
12
10
|
readonly secure: boolean;
|
|
13
11
|
readonly ca?: readonly string[];
|
|
14
12
|
readonly auth?: SendMailAuth;
|
|
15
13
|
readonly allowPlaintextAuth: boolean;
|
|
14
|
+
readonly timeouts: Required<SendMailTimeouts>;
|
|
15
|
+
}
|
|
16
|
+
/** What one message is: its envelope, its size, its DSN request, how its lines end. */
|
|
17
|
+
export interface EnvelopeSettings {
|
|
18
|
+
readonly from: string;
|
|
19
|
+
readonly to: readonly string[];
|
|
16
20
|
readonly size?: number;
|
|
17
21
|
readonly dsn: DsnSettings;
|
|
18
22
|
/** An address is not ASCII, or `smtputf8` asked for it. */
|
|
19
23
|
readonly smtputf8: boolean;
|
|
20
24
|
readonly normalizeLineEnds: boolean;
|
|
21
|
-
|
|
25
|
+
}
|
|
26
|
+
/** The options, checked, with their defaults: the connection's, the envelope's, and the transaction's `deadline` in seconds. */
|
|
27
|
+
export interface ClientSettings extends ConnectionSettings, EnvelopeSettings {
|
|
22
28
|
readonly deadline: number;
|
|
23
29
|
}
|
|
24
30
|
/** `INVALID_OPTION`, for an option `sendMail` cannot take. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/client/settings.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,OAAO,EAAE,KAAK,WAAW,EAAS,MAAM,OAAO,CAAC;AAChD,OAAO,KAAK,EACX,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,OAAO,EACP,MAAM,WAAW,CAAC;AAEnB,8FAA8F;AAC9F,eAAO,MAAM,WAAW,QAAmC,CAAC;AAc5D,
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/client/settings.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,OAAO,EAAE,KAAK,WAAW,EAAS,MAAM,OAAO,CAAC;AAChD,OAAO,KAAK,EACX,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,OAAO,EACP,MAAM,WAAW,CAAC;AAEnB,8FAA8F;AAC9F,eAAO,MAAM,WAAW,QAAmC,CAAC;AAc5D,8JAA8J;AAC9J,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;CAC9C;AAED,uFAAuF;AACvF,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;CACpC;AAED,gIAAgI;AAChI,MAAM,WAAW,cAAe,SAAQ,kBAAkB,EAAE,gBAAgB;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC1B;AAED,8DAA8D;AAC9D,eAAO,MAAM,OAAO,GAAI,SAAS,MAAM,cACmB,CAAC;AA4G3D,4FAA4F;AAC5F,wBAAgB,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,cAAc,CAgCnE;AAED,yEAAyE;AACzE,eAAO,MAAM,IAAI,GAChB,SAAS,eAAe,KACtB,OAAO,IAAI,gBAAgB,GAAG,aACkB,CAAC;AAEpD,0CAA0C;AAC1C,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKzE"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SocketHandler } from 'bun';
|
|
2
|
+
import { SmtpError } from '../errors';
|
|
3
|
+
import { type Endpoint } from './target';
|
|
4
|
+
/** What a socket's events do to its `ClientSocket`. */
|
|
5
|
+
export interface SocketHooks {
|
|
6
|
+
readonly receive: (chunk: Uint8Array) => void;
|
|
7
|
+
readonly drain: () => void;
|
|
8
|
+
readonly lost: () => void;
|
|
9
|
+
readonly handshake: (ok: boolean, error: Error | null) => void;
|
|
10
|
+
}
|
|
11
|
+
/** Bun's events for one socket: only the live one's count (`live`), the clear one's or, after STARTTLS, the TLS one's. */
|
|
12
|
+
export declare function socketHandlers(live: () => boolean, hooks: SocketHooks): SocketHandler<undefined>;
|
|
13
|
+
/** `error` as the connect step failed it: an `SmtpError` as it is, else `CONNECTION_FAILED`. */
|
|
14
|
+
export declare const connectFailure: (endpoint: Endpoint, error: unknown) => SmtpError;
|
|
15
|
+
//# sourceMappingURL=socket-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-events.d.ts","sourceRoot":"","sources":["../../src/client/socket-events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,KAAK,QAAQ,EAAU,MAAM,UAAU,CAAC;AAEjD,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IAC9C,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,CAAC;CAC/D;AAED,0HAA0H;AAC1H,wBAAgB,cAAc,CAC7B,IAAI,EAAE,MAAM,OAAO,EACnB,KAAK,EAAE,WAAW,GAChB,aAAa,CAAC,SAAS,CAAC,CAe1B;AAED,gGAAgG;AAChG,eAAO,MAAM,cAAc,GAC1B,UAAU,QAAQ,EAClB,OAAO,OAAO,KACZ,SAMC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { SmtpError } from '../errors';
|
|
2
|
+
import type { Reply } from '../protocol/reply';
|
|
3
|
+
import type { Inbox } from './inbox';
|
|
4
|
+
import type { Clock } from './target';
|
|
5
|
+
/** What the waits read from their `ClientSocket`, and how they fail it. */
|
|
6
|
+
export interface WaitsHooks {
|
|
7
|
+
readonly host: string;
|
|
8
|
+
readonly clock: () => Clock;
|
|
9
|
+
readonly inbox: Inbox;
|
|
10
|
+
readonly failure: () => SmtpError | undefined;
|
|
11
|
+
readonly closed: () => boolean;
|
|
12
|
+
readonly fail: (error: SmtpError) => SmtpError;
|
|
13
|
+
readonly hungUp: (otherwise: SmtpError) => SmtpError;
|
|
14
|
+
}
|
|
15
|
+
/** The waits of a `ClientSocket`: the reply loop and the caller's promises, each bounded by its timeout and the deadline. */
|
|
16
|
+
export declare class Waits {
|
|
17
|
+
#private;
|
|
18
|
+
private readonly hooks;
|
|
19
|
+
/** Replies read so far. */
|
|
20
|
+
get replies(): number;
|
|
21
|
+
constructor(hooks: WaitsHooks);
|
|
22
|
+
/** Wakes the reply loop: something arrived, or the connection went. */
|
|
23
|
+
wakeUp(): void;
|
|
24
|
+
/** Cuts short every `within` pending: the connection is gone. */
|
|
25
|
+
release(): void;
|
|
26
|
+
/**
|
|
27
|
+
* `promise`, or a `TIMEOUT` once `seconds` passed since `start` (now by
|
|
28
|
+
* default), or the deadline did, whichever comes first.
|
|
29
|
+
*/
|
|
30
|
+
race<T>(promise: Promise<T>, seconds: number, what: string, start?: number): Promise<T>;
|
|
31
|
+
/** The next reply, within `seconds` all told; `what` names it in errors: `the greeting`, `the reply to MAIL FROM`. */
|
|
32
|
+
reply(what: string, seconds: number): Promise<Reply>;
|
|
33
|
+
/** One of the caller's promises — the message stream's next part — bounded as a reply is, and cut short if the connection goes (a hang-up after it settled is none of its business). */
|
|
34
|
+
within<T>(promise: Promise<T>, seconds: number, what: string): Promise<T>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=socket-waits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-waits.d.ts","sourceRoot":"","sources":["../../src/client/socket-waits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAEtC,2EAA2E;AAC3E,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,KAAK,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,SAAS,GAAG,SAAS,CAAC;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,SAAS,CAAC;IAC/C,QAAQ,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,SAAS,CAAC;CACrD;AAED,6HAA6H;AAC7H,qBAAa,KAAK;;IAUL,OAAO,CAAC,QAAQ,CAAC,KAAK;IALlC,2BAA2B;IAC3B,IAAI,OAAO,IAAI,MAAM,CAEpB;gBAE4B,KAAK,EAAE,UAAU;IAE9C,uEAAuE;IACvE,MAAM,IAAI,IAAI;IAMd,iEAAiE;IACjE,OAAO,IAAI,IAAI;IAIf;;;OAGG;IACH,IAAI,CAAC,CAAC,EACL,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,KAAK,SAAoB,GACvB,OAAO,CAAC,CAAC,CAAC;IASb,sHAAsH;IAChH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAuB1D,wLAAwL;IACxL,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;CAmBzE"}
|
package/dist/client/socket.d.ts
CHANGED
|
@@ -9,23 +9,33 @@ import { type Clock, type Endpoint, type TlsTarget } from './target';
|
|
|
9
9
|
export declare class ClientSocket {
|
|
10
10
|
#private;
|
|
11
11
|
readonly host: string;
|
|
12
|
+
/** Times the waits; only the sender or pool sets it (a new `Clock` per transaction), never while a wait is pending. */
|
|
13
|
+
clock: Clock;
|
|
12
14
|
secure: boolean;
|
|
15
|
+
/** Replies read so far: a failure with the same count as before a command got no reply to it. */
|
|
16
|
+
get replies(): number;
|
|
13
17
|
/** The certificate checked out against the host name. */
|
|
14
18
|
verified: boolean;
|
|
15
19
|
private constructor();
|
|
16
|
-
/** Connects, with TLS from the first byte when `tls` is given. */
|
|
17
|
-
static open(endpoint: Endpoint, tls: TlsTarget | undefined, seconds: number, clock: Clock): Promise<ClientSocket>;
|
|
18
20
|
/** The next reply, within `seconds` all told; `what` names it in errors: `the greeting`, `the reply to MAIL FROM`. */
|
|
19
21
|
reply(what: string, seconds: number): Promise<Reply>;
|
|
20
22
|
/** One of the caller's promises — the message stream's next part — bounded as a reply is, and cut short if the connection goes (a hang-up after it settled is none of its business). */
|
|
21
23
|
within<T>(promise: Promise<T>, seconds: number, what: string): Promise<T>;
|
|
22
|
-
/**
|
|
24
|
+
/** Connects, with TLS from the first byte when `tls` is given. */
|
|
25
|
+
static open(endpoint: Endpoint, tls: TlsTarget | undefined, seconds: number, clock: Clock): Promise<ClientSocket>;
|
|
26
|
+
/** Queues bytes; false once the connection failed. */
|
|
23
27
|
write(data: Uint8Array | string): boolean;
|
|
24
28
|
/** Waits until the server took everything written. */
|
|
25
29
|
drained(seconds: number): Promise<void>;
|
|
26
30
|
/** After the 220 to STARTTLS: TLS on this connection. Anything the server sent past the 220 is an attack. */
|
|
27
31
|
startTls(tls: TlsTarget, seconds: number): Promise<void>;
|
|
28
|
-
/**
|
|
32
|
+
/** Alive and silent: nothing failed, closed, or arrived unasked. A pool checks it before it lends the connection. */
|
|
33
|
+
get quiet(): boolean;
|
|
34
|
+
/** Whether the open connection keeps the process alive: a pool unrefs an idle one. */
|
|
35
|
+
ref(keep: boolean): void;
|
|
36
|
+
/** A new message: the server's reply budget starts over. */
|
|
37
|
+
newMessage(): void;
|
|
38
|
+
/** Hangs up; `now` at once, so a cut message is not delivered. */
|
|
29
39
|
close(now?: boolean): void;
|
|
30
40
|
}
|
|
31
41
|
//# sourceMappingURL=socket.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../../src/client/socket.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../../src/client/socket.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAK/C,OAAO,EACN,KAAK,KAAK,EACV,KAAK,QAAQ,EAEb,KAAK,SAAS,EAEd,MAAM,UAAU,CAAC;AAElB;;;;;GAKG;AACH,qBAAa,YAAY;;IAkBvB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,uHAAuH;IAChH,KAAK,EAAE,KAAK;IAXpB,MAAM,UAAS;IACf,iGAAiG;IACjG,IAAI,OAAO,IAAI,MAAM,CAEpB;IACD,yDAAyD;IACzD,QAAQ,UAAS;IAEjB,OAAO;IAgBP,sHAAsH;IACtH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAIpD,wLAAwL;IACxL,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAIzE,kEAAkE;WACrD,IAAI,CAChB,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,SAAS,GAAG,SAAS,EAC1B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,KAAK,GACV,OAAO,CAAC,YAAY,CAAC;IA2FxB,sDAAsD;IACtD,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO;IAQzC,sDAAsD;IAChD,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB7C,6GAA6G;IACvG,QAAQ,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB9D,qHAAqH;IACrH,IAAI,KAAK,IAAI,OAAO,CAEnB;IAED,sFAAsF;IACtF,GAAG,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAKxB,4DAA4D;IAC5D,UAAU,IAAI,IAAI;IAIlB,kEAAkE;IAClE,KAAK,CAAC,GAAG,UAAQ,GAAG,IAAI;CAKxB"}
|
|
@@ -2,7 +2,7 @@ import { type RecipientReply } from '../errors';
|
|
|
2
2
|
import type { Reply } from '../protocol/reply';
|
|
3
3
|
import { type Content } from './content';
|
|
4
4
|
import type { Ready } from './session';
|
|
5
|
-
import type {
|
|
5
|
+
import type { ConnectionSettings, EnvelopeSettings } from './settings';
|
|
6
6
|
import type { ClientSocket } from './socket';
|
|
7
7
|
/** What the server said to the envelope and the message. */
|
|
8
8
|
export interface Delivered {
|
|
@@ -12,6 +12,12 @@ export interface Delivered {
|
|
|
12
12
|
/** The server lists DSN. */
|
|
13
13
|
readonly dsnOffered: boolean;
|
|
14
14
|
}
|
|
15
|
-
/**
|
|
16
|
-
export
|
|
15
|
+
/** What `transact` returns: the server's answers. */
|
|
16
|
+
export interface Transacted {
|
|
17
|
+
readonly delivered: Delivered;
|
|
18
|
+
}
|
|
19
|
+
/** Whether `error` came from `transact` with replies in order and the connection alive (a clean 4xx/5xx refusal, not a 421, a timeout, a bad reply or a lost connection); `RSET` before reuse. */
|
|
20
|
+
export declare const inStep: (error: unknown) => boolean;
|
|
21
|
+
/** One message: the envelope, then the content. A refusal throws, `inStep(error)` telling whether the session can go on. */
|
|
22
|
+
export declare function transact(socket: ClientSocket, settings: ConnectionSettings & EnvelopeSettings, content: Content, ready: Ready): Promise<Transacted>;
|
|
17
23
|
//# sourceMappingURL=transaction.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../../src/client/transaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAa,MAAM,WAAW,CAAC;AAE3D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAGxD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../../src/client/transaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAa,MAAM,WAAW,CAAC;AAE3D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAGxD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,4DAA4D;AAC5D,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IAC7C,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,4BAA4B;IAC5B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC7B;AAED,qDAAqD;AACrD,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;CAC9B;AAQD,kMAAkM;AAClM,eAAO,MAAM,MAAM,GAAI,OAAO,OAAO,KAAG,OAC0B,CAAC;AA2KnE,4HAA4H;AAC5H,wBAAsB,QAAQ,CAC7B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,kBAAkB,GAAG,gBAAgB,EAC/C,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,GACV,OAAO,CAAC,UAAU,CAAC,CAgCrB"}
|
package/docs/README.md
CHANGED
|
@@ -5,6 +5,6 @@ long one.
|
|
|
5
5
|
|
|
6
6
|
| Page | Read it when |
|
|
7
7
|
| --- | --- |
|
|
8
|
-
| [Guide](guide.md) | running an MX, a submission server or implicit TLS; reading what a reply means; writing hooks and keeping state in `Session.data`; handling what `onData` receives; delivering into `@bumail/store`; setting TLS, limits and `greetingDelay`; running behind a TCP proxy such as Traefik with the PROXY protocol; sending mail with `@bumail/smtp/client` and trying it with Mailpit; or checking which RFC a behaviour follows |
|
|
9
|
-
| [Troubleshooting](troubleshooting.md) | `createSmtpServer` threw, the server will not listen, a client reports a reply you did not expect, connections through a proxy close or show the proxy's address,
|
|
10
|
-
| [Roadmap](roadmap.md) | wondering what is coming —
|
|
8
|
+
| [Guide](guide.md) | running an MX, a submission server or implicit TLS; reading what a reply means; writing hooks and keeping state in `Session.data`; handling what `onData` receives; delivering into `@bumail/store`; setting TLS, limits and `greetingDelay`; running behind a TCP proxy such as Traefik with the PROXY protocol; sending mail with `@bumail/smtp/client`, reusing connections with `createSmtpPool`, and trying it with Mailpit; or checking which RFC a behaviour follows |
|
|
9
|
+
| [Troubleshooting](troubleshooting.md) | `createSmtpServer` threw, the server will not listen, a client reports a reply you did not expect, connections through a proxy close or show the proxy's address, `sendMail` rejected, or `createSmtpPool` threw or reported that the pool is closed |
|
|
10
|
+
| [Roadmap](roadmap.md) | wondering what is coming — DSN, a ready-made delivery into the store — and what is not planned |
|
package/docs/guide.md
CHANGED
|
@@ -1818,6 +1818,28 @@ try {
|
|
|
1818
1818
|
}
|
|
1819
1819
|
```
|
|
1820
1820
|
|
|
1821
|
+
### Reusing connections: `createSmtpPool`
|
|
1822
|
+
|
|
1823
|
+
`createSmtpPool(options?)` takes the pool's own options, below, all optional. It returns a pool whose `sendMail(message, options)` takes the options of `sendMail`. A delivery borrows an idle connection to the same server when there is one, and opens one otherwise; by MX, each address is borrowed in turn, in MX order.
|
|
1824
|
+
|
|
1825
|
+
| option | default | |
|
|
1826
|
+
| --- | --- | --- |
|
|
1827
|
+
| `maxMessages` | 100 | messages one connection carries before QUIT |
|
|
1828
|
+
| `idleSeconds` | 5 | an idle connection is QUITted after this |
|
|
1829
|
+
| `maxAgeSeconds` | 300 | a connection older than this is not lent |
|
|
1830
|
+
| `maxIdlePerHost` | 4 | idle connections kept per mail host |
|
|
1831
|
+
|
|
1832
|
+
What the pool guarantees:
|
|
1833
|
+
|
|
1834
|
+
- **Same connection, same identity.** Server address, `helo`, `tls`, `secure`, `ca`, `allowPlaintextAuth` and the credentials are compared field by field; the password is never part of a string key. The envelope, `size`, DSN options and timeouts belong to the message.
|
|
1835
|
+
- **RSET on every borrow**, and after a message was aborted by a refusal. A connection is never reused after a 421, a timeout, a `BAD_REPLY`, a lost stream, or bytes the server sent while it was idle.
|
|
1836
|
+
- **A stale connection is invisible.** One that fails the RSET, or dies before the server answered MAIL FROM, is replaced once by a fresh connection; the caller gets that delivery's result, so a queue does not fall back to a smarthost for it.
|
|
1837
|
+
- **Extensions are per connection**: `SIZE`, `8BITMIME`, `SMTPUTF8` and `DSN` are checked for each message against the connection that carries it.
|
|
1838
|
+
- **`deadline` is per message.** Neither the idle timers nor the idle sockets keep the process alive; a pool left open does not hold a script up.
|
|
1839
|
+
- **`close()`** QUITs idle connections and waits for the busy ones; `sendMail` on a closed pool rejects with `STOPPED`.
|
|
1840
|
+
|
|
1841
|
+
`result.reused` is `true` when the connection had carried a message before (plain `sendMail` leaves it out).
|
|
1842
|
+
|
|
1821
1843
|
### Delivery by MX
|
|
1822
1844
|
|
|
1823
1845
|
With `{ domain, resolver }`, `resolveMx` gives the hosts in the order to
|
|
@@ -2036,9 +2058,9 @@ each one comes from.
|
|
|
2036
2058
|
unrecognized`; a client sends DATA instead.
|
|
2037
2059
|
- **A queue.** The client tries a destination once and reports;
|
|
2038
2060
|
retrying a temporary failure is the caller's job until `@bumail/queue`.
|
|
2039
|
-
- **Connection reuse
|
|
2040
|
-
sends no `RSET
|
|
2041
|
-
|
|
2061
|
+
- **Connection reuse in `sendMail`.** `sendMail` opens one connection per
|
|
2062
|
+
message and sends no `RSET`; for several messages to one destination over
|
|
2063
|
+
one session, use `createSmtpPool` (see "Reusing connections: `createSmtpPool`").
|
|
2042
2064
|
- **A non-ASCII `ORCPT` of type `rfc822` in the client**: RFC 6533 defines
|
|
2043
2065
|
`utf-8` for it, which both sides handle; the client leaves a non-ASCII
|
|
2044
2066
|
`rfc822` address out of `RCPT TO`. Any other type is relayed as xtext.
|
package/docs/roadmap.md
CHANGED
|
@@ -6,13 +6,10 @@ number on it.
|
|
|
6
6
|
|
|
7
7
|
## Now
|
|
8
8
|
|
|
9
|
-
Nothing is under way
|
|
9
|
+
Nothing is under way.
|
|
10
10
|
|
|
11
11
|
## Next
|
|
12
12
|
|
|
13
|
-
- **Connection reuse in the client** — several messages to one
|
|
14
|
-
destination over one session (`RSET` between them), for a queue that
|
|
15
|
-
delivers in batches. Today each `sendMail` opens its own connection.
|
|
16
13
|
- **Delivery into `@bumail/store`, ready-made** — an `onData` that puts
|
|
17
14
|
each message in its recipients' inboxes, instead of the few lines the
|
|
18
15
|
[guide](guide.md#delivering-into-bumailstore) shows today. The store
|
|
@@ -32,6 +29,15 @@ Nothing yet.
|
|
|
32
29
|
|
|
33
30
|
## Shipped
|
|
34
31
|
|
|
32
|
+
### 0.12.0
|
|
33
|
+
|
|
34
|
+
- **Connection reuse in the client** — `createSmtpPool(options?)` in
|
|
35
|
+
`@bumail/smtp/client` keeps connections to a server between messages, so
|
|
36
|
+
several messages to one destination go over one session (`RSET` between
|
|
37
|
+
them) instead of each `sendMail` opening its own. `SendMailResult` may
|
|
38
|
+
carry `reused`; plain `sendMail` is unchanged. `@bumail/queue` and
|
|
39
|
+
`@bumail/server` deliver through it by default.
|
|
40
|
+
|
|
35
41
|
### 0.11.0
|
|
36
42
|
|
|
37
43
|
- **UTF-8 `ORCPT` (RFC 6533)**: the server decodes `ORCPT=utf-8;…`
|
package/docs/troubleshooting.md
CHANGED
|
@@ -131,6 +131,13 @@ A reply carries its enhanced status code (`5.7.1`, RFC 3463) only after
|
|
|
131
131
|
- [`SmtpError: sendMail(): a message stream must give Uint8Array chunks`](#smtperror-sendmail-a-message-stream-must-give-uint8array-chunks)
|
|
132
132
|
- [`SmtpError: sendMail(): a message stream can be sent once only`](#smtperror-sendmail-a-message-stream-can-be-sent-once-only)
|
|
133
133
|
|
|
134
|
+
**Sending mail: the pool**
|
|
135
|
+
|
|
136
|
+
- [`SmtpError: The SMTP pool is closed`](#smtperror-the-smtp-pool-is-closed)
|
|
137
|
+
- [`SmtpError: createSmtpPool(): … must be an integer of at least 1, not …`](#smtperror-createsmtppool--must-be-an-integer-of-at-least-1-not-)
|
|
138
|
+
- [`SmtpError: createSmtpPool(): … must be a number of seconds above 0 and at most 2147483, not …`](#smtperror-createsmtppool--must-be-a-number-of-seconds-above-0-and-at-most-2147483-not-)
|
|
139
|
+
- [`SmtpError: createSmtpPool(): options must be an object`](#smtperror-createsmtppool-options-must-be-an-object)
|
|
140
|
+
|
|
134
141
|
**Sending mail: the message**
|
|
135
142
|
|
|
136
143
|
- [`SmtpError: sendMail(): the message holds a bare CR or LF, which a server must refuse (SMTP smuggling); end every line with CRLF, or pass normalizeLineEnds: true`](#smtperror-sendmail-the-message-holds-a-bare-cr-or-lf-which-a-server-must-refuse-smtp-smuggling-end-every-line-with-crlf-or-pass-normalizelineends-true)
|
|
@@ -2229,6 +2236,83 @@ the final dot.
|
|
|
2229
2236
|
|
|
2230
2237
|
**Fix**: make a new stream per `sendMail`, or pass bytes.
|
|
2231
2238
|
|
|
2239
|
+
## Sending mail: the pool
|
|
2240
|
+
|
|
2241
|
+
### `SmtpError: The SMTP pool is closed`
|
|
2242
|
+
|
|
2243
|
+
**When**: `pool.sendMail` is called after `pool.close()` (code `STOPPED`).
|
|
2244
|
+
|
|
2245
|
+
**Why**: `close()` sends QUIT on the idle connections and waits for the
|
|
2246
|
+
busy ones; after it the pool has none and opens none. A message in flight
|
|
2247
|
+
when `close()` was called still finishes.
|
|
2248
|
+
|
|
2249
|
+
**Fix**: close the pool only at shutdown, and stop sending first. (The pool
|
|
2250
|
+
of `@bumail/queue` and of `bumail serve` is closed by their own `stop()`,
|
|
2251
|
+
after their deliveries.) Make a
|
|
2252
|
+
new pool when sending must go on:
|
|
2253
|
+
|
|
2254
|
+
```ts
|
|
2255
|
+
import { createSmtpPool } from '@bumail/smtp/client';
|
|
2256
|
+
|
|
2257
|
+
let pool = createSmtpPool();
|
|
2258
|
+
// … at shutdown, after the last sendMail:
|
|
2259
|
+
await pool.close();
|
|
2260
|
+
// a later pool.sendMail rejects with STOPPED; make another pool to send again
|
|
2261
|
+
pool = createSmtpPool();
|
|
2262
|
+
```
|
|
2263
|
+
|
|
2264
|
+
### `SmtpError: createSmtpPool(): … must be an integer of at least 1, not …`
|
|
2265
|
+
|
|
2266
|
+
**When**: `maxMessages` or `maxIdlePerHost` is `0`, negative, fractional or
|
|
2267
|
+
not a number (code `INVALID_OPTION`, thrown by `createSmtpPool` itself).
|
|
2268
|
+
|
|
2269
|
+
**Why**: a connection that carries no message, or a host that keeps no idle
|
|
2270
|
+
connection, is no pool; to turn reuse off, use `sendMail` instead.
|
|
2271
|
+
|
|
2272
|
+
**Fix**: pass an integer of at least 1, or leave it out for its default
|
|
2273
|
+
(`maxMessages` 100, `maxIdlePerHost` 4):
|
|
2274
|
+
|
|
2275
|
+
```ts
|
|
2276
|
+
import { createSmtpPool } from '@bumail/smtp/client';
|
|
2277
|
+
|
|
2278
|
+
const pool = createSmtpPool({ maxMessages: 50, maxIdlePerHost: 2 });
|
|
2279
|
+
```
|
|
2280
|
+
|
|
2281
|
+
### `SmtpError: createSmtpPool(): … must be a number of seconds above 0 and at most 2147483, not …`
|
|
2282
|
+
|
|
2283
|
+
**When**: `idleSeconds` or `maxAgeSeconds` is `0`, negative, not a number, or
|
|
2284
|
+
past 2 147 483 seconds.
|
|
2285
|
+
|
|
2286
|
+
**Why**: each is a `setTimeout`, which fires after 1 ms for a delay past
|
|
2287
|
+
2^31 − 1 ms.
|
|
2288
|
+
|
|
2289
|
+
**Fix**: pass seconds, fractions allowed, or leave it out
|
|
2290
|
+
(`idleSeconds` 5, `maxAgeSeconds` 300):
|
|
2291
|
+
|
|
2292
|
+
```ts
|
|
2293
|
+
import { createSmtpPool } from '@bumail/smtp/client';
|
|
2294
|
+
|
|
2295
|
+
const pool = createSmtpPool({ idleSeconds: 10, maxAgeSeconds: 600 });
|
|
2296
|
+
```
|
|
2297
|
+
|
|
2298
|
+
### `SmtpError: createSmtpPool(): options must be an object`
|
|
2299
|
+
|
|
2300
|
+
**When**: `createSmtpPool` is given something that is not an object, such
|
|
2301
|
+
as `null` or a string.
|
|
2302
|
+
|
|
2303
|
+
**Why**: the pool options are `maxMessages`, `idleSeconds`,
|
|
2304
|
+
`maxAgeSeconds` and `maxIdlePerHost`. The options of a message (`host`,
|
|
2305
|
+
`from`, `to`, …) go to `pool.sendMail`, not here.
|
|
2306
|
+
|
|
2307
|
+
**Fix**: pass an object, or nothing:
|
|
2308
|
+
|
|
2309
|
+
```ts
|
|
2310
|
+
import { createSmtpPool } from '@bumail/smtp/client';
|
|
2311
|
+
|
|
2312
|
+
const pool = createSmtpPool();
|
|
2313
|
+
await pool.sendMail(message, { host: 'relay.example.net', from: 'a@example.com', to: 'b@example.org' });
|
|
2314
|
+
```
|
|
2315
|
+
|
|
2232
2316
|
## Sending mail: the message
|
|
2233
2317
|
|
|
2234
2318
|
### `SmtpError: sendMail(): the message holds a bare CR or LF, which a server must refuse (SMTP smuggling); end every line with CRLF, or pass normalizeLineEnds: true`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bumail/smtp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "An SMTP server on Bun.listen — RFC 5321 with PIPELINING, SIZE, 8BITMIME, SMTPUTF8, STARTTLS and AUTH after TLS, policy hooks, and no open relay — and a client that delivers a message to a smarthost or by MX",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@bumail/auth": "^0.5.0",
|
|
51
51
|
"@bumail/dns": "^0.2.2",
|
|
52
|
-
"@bumail/store": "^0.
|
|
52
|
+
"@bumail/store": "^0.7.1",
|
|
53
53
|
"@types/bun": "^1.4.2"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|