agent-inbox 0.2.2 → 0.2.4

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.
Files changed (149) hide show
  1. package/AGENTS.md +18 -0
  2. package/CLAUDE.md +92 -1
  3. package/README.md +73 -6
  4. package/bench/inbox-growth.bench.ts +224 -0
  5. package/dist/federation/connection-manager.d.ts +8 -0
  6. package/dist/federation/connection-manager.d.ts.map +1 -1
  7. package/dist/federation/connection-manager.js +12 -0
  8. package/dist/federation/connection-manager.js.map +1 -1
  9. package/dist/federation/delivery-queue.d.ts +11 -3
  10. package/dist/federation/delivery-queue.d.ts.map +1 -1
  11. package/dist/federation/delivery-queue.js +38 -8
  12. package/dist/federation/delivery-queue.js.map +1 -1
  13. package/dist/federation/queue-store.d.ts +42 -0
  14. package/dist/federation/queue-store.d.ts.map +1 -0
  15. package/dist/federation/queue-store.js +87 -0
  16. package/dist/federation/queue-store.js.map +1 -0
  17. package/dist/index.d.mts +2 -0
  18. package/dist/index.d.ts +29 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +124 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.mjs +1 -0
  23. package/dist/index.mjs.map +1 -0
  24. package/dist/jsonrpc/mail-push-types.d.ts +9 -0
  25. package/dist/jsonrpc/mail-push-types.d.ts.map +1 -1
  26. package/dist/jsonrpc/mail-push-types.js +1 -0
  27. package/dist/jsonrpc/mail-push-types.js.map +1 -1
  28. package/dist/jsonrpc/mail-server.d.ts +8 -1
  29. package/dist/jsonrpc/mail-server.d.ts.map +1 -1
  30. package/dist/jsonrpc/mail-server.js +42 -1
  31. package/dist/jsonrpc/mail-server.js.map +1 -1
  32. package/dist/mail/address-book.d.ts +43 -0
  33. package/dist/mail/address-book.d.ts.map +1 -0
  34. package/dist/mail/address-book.js +95 -0
  35. package/dist/mail/address-book.js.map +1 -0
  36. package/dist/mail/attachment-store.d.ts +31 -0
  37. package/dist/mail/attachment-store.d.ts.map +1 -0
  38. package/dist/mail/attachment-store.js +74 -0
  39. package/dist/mail/attachment-store.js.map +1 -0
  40. package/dist/mail/email-mapper.d.ts +41 -0
  41. package/dist/mail/email-mapper.d.ts.map +1 -0
  42. package/dist/mail/email-mapper.js +216 -0
  43. package/dist/mail/email-mapper.js.map +1 -0
  44. package/dist/mail/fs-attachment-store.d.ts +38 -0
  45. package/dist/mail/fs-attachment-store.d.ts.map +1 -0
  46. package/dist/mail/fs-attachment-store.js +165 -0
  47. package/dist/mail/fs-attachment-store.js.map +1 -0
  48. package/dist/mail/mail-gateway.d.ts +114 -0
  49. package/dist/mail/mail-gateway.d.ts.map +1 -0
  50. package/dist/mail/mail-gateway.js +402 -0
  51. package/dist/mail/mail-gateway.js.map +1 -0
  52. package/dist/mail/provider-transport.d.ts +138 -0
  53. package/dist/mail/provider-transport.d.ts.map +1 -0
  54. package/dist/mail/provider-transport.js +434 -0
  55. package/dist/mail/provider-transport.js.map +1 -0
  56. package/dist/mail/rate-limiter.d.ts +20 -0
  57. package/dist/mail/rate-limiter.d.ts.map +1 -0
  58. package/dist/mail/rate-limiter.js +56 -0
  59. package/dist/mail/rate-limiter.js.map +1 -0
  60. package/dist/mail/smtp-transport.d.ts +141 -0
  61. package/dist/mail/smtp-transport.d.ts.map +1 -0
  62. package/dist/mail/smtp-transport.js +415 -0
  63. package/dist/mail/smtp-transport.js.map +1 -0
  64. package/dist/mail/types.d.ts +177 -0
  65. package/dist/mail/types.d.ts.map +1 -0
  66. package/dist/mail/types.js +11 -0
  67. package/dist/mail/types.js.map +1 -0
  68. package/dist/push/notifier.d.ts +21 -0
  69. package/dist/push/notifier.d.ts.map +1 -1
  70. package/dist/push/notifier.js +84 -2
  71. package/dist/push/notifier.js.map +1 -1
  72. package/dist/router/destination.d.ts +69 -0
  73. package/dist/router/destination.d.ts.map +1 -0
  74. package/dist/router/destination.js +106 -0
  75. package/dist/router/destination.js.map +1 -0
  76. package/dist/router/message-router.d.ts +15 -0
  77. package/dist/router/message-router.d.ts.map +1 -1
  78. package/dist/router/message-router.js +25 -3
  79. package/dist/router/message-router.js.map +1 -1
  80. package/dist/storage/interface.d.ts +21 -0
  81. package/dist/storage/interface.d.ts.map +1 -1
  82. package/dist/storage/memory.d.ts +12 -0
  83. package/dist/storage/memory.d.ts.map +1 -1
  84. package/dist/storage/memory.js +50 -0
  85. package/dist/storage/memory.js.map +1 -1
  86. package/dist/storage/sqlite.d.ts +14 -0
  87. package/dist/storage/sqlite.d.ts.map +1 -1
  88. package/dist/storage/sqlite.js +79 -1
  89. package/dist/storage/sqlite.js.map +1 -1
  90. package/dist/traceability/traceability.d.ts.map +1 -1
  91. package/dist/traceability/traceability.js +7 -17
  92. package/dist/traceability/traceability.js.map +1 -1
  93. package/dist/types.d.ts +80 -0
  94. package/dist/types.d.ts.map +1 -1
  95. package/docs/DESIGN.md +15 -0
  96. package/docs/MAIL-INTEROP-PLAN.md +660 -0
  97. package/package.json +29 -3
  98. package/renovate.json5 +6 -0
  99. package/rules/agent-inbox.md +1 -0
  100. package/src/federation/connection-manager.ts +12 -0
  101. package/src/federation/delivery-queue.ts +38 -8
  102. package/src/federation/queue-store.ts +124 -0
  103. package/src/index.ts +186 -1
  104. package/src/jsonrpc/mail-push-types.ts +10 -0
  105. package/src/jsonrpc/mail-server.ts +48 -1
  106. package/src/mail/address-book.ts +111 -0
  107. package/src/mail/attachment-store.ts +90 -0
  108. package/src/mail/email-mapper.ts +288 -0
  109. package/src/mail/fs-attachment-store.ts +163 -0
  110. package/src/mail/mail-gateway.ts +505 -0
  111. package/src/mail/provider-transport.ts +577 -0
  112. package/src/mail/rate-limiter.ts +51 -0
  113. package/src/mail/smtp-transport.ts +589 -0
  114. package/src/mail/types.ts +221 -0
  115. package/src/push/notifier.ts +98 -2
  116. package/src/router/destination.ts +140 -0
  117. package/src/router/message-router.ts +41 -4
  118. package/src/storage/interface.ts +22 -0
  119. package/src/storage/memory.ts +59 -0
  120. package/src/storage/sqlite.ts +114 -1
  121. package/src/traceability/traceability.ts +7 -16
  122. package/src/types.ts +74 -0
  123. package/test/federation/delivery-queue-sqlite.test.ts +158 -0
  124. package/test/load.test.ts +288 -0
  125. package/test/mail/address-book.test.ts +111 -0
  126. package/test/mail/attachment-store-contract.test.ts +92 -0
  127. package/test/mail/attachment-store.test.ts +69 -0
  128. package/test/mail/destination.test.ts +115 -0
  129. package/test/mail/dsn-parse.test.ts +239 -0
  130. package/test/mail/email-mapper.test.ts +341 -0
  131. package/test/mail/external-id.test.ts +43 -0
  132. package/test/mail/fs-attachment-store.test.ts +134 -0
  133. package/test/mail/full-flow-e2e.test.ts +200 -0
  134. package/test/mail/mail-gateway.test.ts +419 -0
  135. package/test/mail/mail-transport-contract.test.ts +134 -0
  136. package/test/mail/mock-mail.ts +161 -0
  137. package/test/mail/mock-postmark.ts +66 -0
  138. package/test/mail/provider-transport.test.ts +381 -0
  139. package/test/mail/rate-limiter.test.ts +48 -0
  140. package/test/mail/router-mail-integration.test.ts +138 -0
  141. package/test/mail/smtp-e2e.test.ts +98 -0
  142. package/test/mail/smtp-transport.test.ts +138 -0
  143. package/test/mail-presence.test.ts +149 -0
  144. package/test/mail-push.test.ts +44 -0
  145. package/test/mail-server.test.ts +25 -0
  146. package/test/push-notifier.test.ts +81 -0
  147. package/test/sqlite-storage.test.ts +106 -0
  148. package/test/storage.test.ts +92 -0
  149. package/vitest.bench.config.ts +8 -0
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Self-hosted SMTP MailTransport — the lightweight default backend.
3
+ *
4
+ * Inbound: `smtp-server` listener. On DATA end the message is parsed, auth-
5
+ * verified, attachments are stored, and the inbound handler is awaited
6
+ * BEFORE the SMTP 250 is returned (commit-before-ACK). A handler throw
7
+ * returns a 4xx/5xx so the peer MTA retries.
8
+ * Outbound: `nodemailer` (smarthost relay; direct-MX is future). SMTP responses
9
+ * map to delivered/transient/permanent.
10
+ *
11
+ * smtp-server / nodemailer / mailparser / mailauth are OPTIONAL peer deps,
12
+ * loaded via dynamic import (mirrors src/map/map-client.ts). The transport
13
+ * throws a clear error from start()/send() if they are not installed.
14
+ *
15
+ * The pure classification/parsing helpers are exported for unit testing without
16
+ * the libraries or real sockets.
17
+ */
18
+ import type { MailTransport, MailCapabilities, MailTransportState, MailHealth, OutboundMail, MailSendResult, InboundMail, InboundHandler, InboundAuthResults, InboundBounce, AttachmentStore } from "./types.js";
19
+ export interface SmtpTransportOptions {
20
+ /** Inbound listener port (default 25). */
21
+ listenPort?: number;
22
+ /** Inbound bind host (default "0.0.0.0"). */
23
+ listenHost?: string;
24
+ /** Outbound smarthost relay. Required for sending in this phase. */
25
+ relay?: {
26
+ host: string;
27
+ port: number;
28
+ secure?: boolean;
29
+ auth?: {
30
+ user: string;
31
+ pass: string;
32
+ };
33
+ };
34
+ /** DKIM signing for outbound. */
35
+ dkim?: {
36
+ domainName: string;
37
+ keySelector: string;
38
+ privateKey: string;
39
+ };
40
+ /** Max accepted inbound size, bytes (default 25 MiB). */
41
+ maxMessageBytes?: number;
42
+ /** Where to persist inbound attachment bytes. */
43
+ attachmentStore?: AttachmentStore;
44
+ }
45
+ export declare class SmtpTransport implements MailTransport {
46
+ private opts;
47
+ readonly capabilities: MailCapabilities;
48
+ private _state;
49
+ private handler?;
50
+ private server?;
51
+ private mailer?;
52
+ /** Best-effort in-process idempotency for delivered sends (bounded). */
53
+ private delivered;
54
+ private static readonly MAX_DELIVERED;
55
+ constructor(opts: SmtpTransportOptions);
56
+ get state(): MailTransportState;
57
+ onReceive(handler: InboundHandler): void;
58
+ health(): Promise<MailHealth>;
59
+ start(): Promise<void>;
60
+ stop(): Promise<void>;
61
+ send(envelope: OutboundMail): Promise<MailSendResult>;
62
+ /** Record a delivered idempotency key, evicting the oldest when over cap. */
63
+ private rememberDelivered;
64
+ private onData;
65
+ private verifyAuth;
66
+ private storeAttachments;
67
+ private getMailer;
68
+ }
69
+ interface ParsedAddress {
70
+ value: Array<{
71
+ address?: string;
72
+ name?: string;
73
+ }>;
74
+ }
75
+ interface ParsedMail {
76
+ from?: ParsedAddress;
77
+ to?: ParsedAddress;
78
+ cc?: ParsedAddress;
79
+ subject?: string;
80
+ text?: string;
81
+ html?: string | false;
82
+ messageId?: string;
83
+ inReplyTo?: string;
84
+ references?: string | string[];
85
+ headers?: Map<string, unknown>;
86
+ date?: Date;
87
+ attachments?: Array<{
88
+ content: Buffer;
89
+ contentType: string;
90
+ filename?: string;
91
+ cid?: string;
92
+ size: number;
93
+ }>;
94
+ }
95
+ /** Map a nodemailer/SMTP send error to a classified result. */
96
+ export declare function smtpErrorToResult(err: unknown): MailSendResult;
97
+ /** Build an InboundMail from mailparser output + session/transport context. */
98
+ export declare function parsedToInboundMail(parsed: ParsedMail, ctx: {
99
+ envelopeFrom: string;
100
+ envelopeTo: string[];
101
+ remote?: {
102
+ ip?: string;
103
+ reverseDns?: string;
104
+ };
105
+ authResults?: InboundAuthResults;
106
+ attachments?: InboundMail["attachments"];
107
+ sizeBytes: number;
108
+ }): InboundMail;
109
+ /**
110
+ * Detect and parse an RFC 3464 delivery-status notification (bounce) from
111
+ * mailparser output. A DSN is `multipart/report; report-type=delivery-status`,
112
+ * with a `message/delivery-status` part carrying per-recipient Action/Status/
113
+ * Diagnostic-Code, and usually a returned-message part bearing the original
114
+ * Message-ID. Returns undefined for ordinary mail.
115
+ */
116
+ export declare function parseDsnFromParsed(parsed: ParsedMail): InboundBounce | undefined;
117
+ /** Convert an OutboundMail to a nodemailer message object. */
118
+ export declare function toNodemailerMessage(env: OutboundMail, dkim?: SmtpTransportOptions["dkim"]): Record<string, unknown>;
119
+ interface MailauthResult {
120
+ spf?: {
121
+ status?: {
122
+ result?: string;
123
+ };
124
+ };
125
+ dkim?: {
126
+ results?: Array<{
127
+ status?: {
128
+ result?: string;
129
+ };
130
+ }>;
131
+ };
132
+ dmarc?: {
133
+ status?: {
134
+ result?: string;
135
+ };
136
+ };
137
+ }
138
+ /** Normalize a mailauth authenticate() result into InboundAuthResults. */
139
+ export declare function mailauthToResults(res: MailauthResult): InboundAuthResults;
140
+ export {};
141
+ //# sourceMappingURL=smtp-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smtp-transport.d.ts","sourceRoot":"","sources":["../../src/mail/smtp-transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,415 @@
1
+ /**
2
+ * Self-hosted SMTP MailTransport — the lightweight default backend.
3
+ *
4
+ * Inbound: `smtp-server` listener. On DATA end the message is parsed, auth-
5
+ * verified, attachments are stored, and the inbound handler is awaited
6
+ * BEFORE the SMTP 250 is returned (commit-before-ACK). A handler throw
7
+ * returns a 4xx/5xx so the peer MTA retries.
8
+ * Outbound: `nodemailer` (smarthost relay; direct-MX is future). SMTP responses
9
+ * map to delivered/transient/permanent.
10
+ *
11
+ * smtp-server / nodemailer / mailparser / mailauth are OPTIONAL peer deps,
12
+ * loaded via dynamic import (mirrors src/map/map-client.ts). The transport
13
+ * throws a clear error from start()/send() if they are not installed.
14
+ *
15
+ * The pure classification/parsing helpers are exported for unit testing without
16
+ * the libraries or real sockets.
17
+ */
18
+ const DEFAULT_MAX_BYTES = 25 * 1024 * 1024;
19
+ export class SmtpTransport {
20
+ opts;
21
+ capabilities;
22
+ _state = "stopped";
23
+ handler;
24
+ server;
25
+ mailer;
26
+ /** Best-effort in-process idempotency for delivered sends (bounded). */
27
+ delivered = new Set();
28
+ static MAX_DELIVERED = 50_000;
29
+ constructor(opts) {
30
+ this.opts = opts;
31
+ this.capabilities = {
32
+ outbound: opts.relay ? "relay" : "mx",
33
+ signsDkim: !!opts.dkim,
34
+ verifiesInboundAuth: true,
35
+ inbound: "listener",
36
+ maxMessageBytes: opts.maxMessageBytes ?? DEFAULT_MAX_BYTES,
37
+ };
38
+ }
39
+ get state() {
40
+ return this._state;
41
+ }
42
+ onReceive(handler) {
43
+ this.handler = handler;
44
+ }
45
+ async health() {
46
+ return { state: this._state };
47
+ }
48
+ async start() {
49
+ if (this._state === "ready")
50
+ return;
51
+ this._state = "starting";
52
+ const { SMTPServer } = await loadSmtpServer();
53
+ const maxBytes = this.capabilities.maxMessageBytes;
54
+ const server = new SMTPServer({
55
+ authOptional: true,
56
+ // TLS is expected to be terminated upstream (tunnel/relay) for the
57
+ // lightweight default; don't advertise the built-in self-signed cert.
58
+ hideSTARTTLS: true,
59
+ size: maxBytes,
60
+ onData: (stream, session, callback) => {
61
+ // Guard the SMTP callback so it can fire at most once — smtp-server
62
+ // corrupts its protocol state if the data callback is invoked twice.
63
+ let acked = false;
64
+ const ack = (err) => {
65
+ if (acked)
66
+ return;
67
+ acked = true;
68
+ callback(err);
69
+ };
70
+ this.onData(stream, session, ack).catch((err) => ack(err));
71
+ },
72
+ });
73
+ await new Promise((resolve) => {
74
+ this.server = server;
75
+ this.server.listen(this.opts.listenPort ?? 25, this.opts.listenHost ?? "0.0.0.0", () => resolve());
76
+ });
77
+ this._state = "ready";
78
+ }
79
+ async stop() {
80
+ this._state = "stopping";
81
+ if (this.server) {
82
+ await new Promise((resolve) => this.server.close(() => resolve()));
83
+ this.server = undefined;
84
+ }
85
+ this._state = "stopped";
86
+ }
87
+ async send(envelope) {
88
+ if (this._state !== "ready") {
89
+ throw new Error(`send() called while transport state is "${this._state}"`);
90
+ }
91
+ if (this.delivered.has(envelope.idempotencyKey)) {
92
+ return {
93
+ disposition: "delivered",
94
+ remoteMessageId: envelope.headers.messageId,
95
+ detail: "idempotent replay",
96
+ };
97
+ }
98
+ if (!this.opts.relay) {
99
+ throw new Error("SmtpTransport.send requires a relay; direct-MX is not yet implemented");
100
+ }
101
+ const mailer = await this.getMailer();
102
+ try {
103
+ const info = await mailer.sendMail(toNodemailerMessage(envelope, this.opts.dkim));
104
+ this.rememberDelivered(envelope.idempotencyKey);
105
+ return {
106
+ disposition: "delivered",
107
+ remoteMessageId: info.messageId ?? envelope.headers.messageId,
108
+ detail: info.response,
109
+ };
110
+ }
111
+ catch (err) {
112
+ return smtpErrorToResult(err);
113
+ }
114
+ }
115
+ /** Record a delivered idempotency key, evicting the oldest when over cap. */
116
+ rememberDelivered(key) {
117
+ if (this.delivered.size >= SmtpTransport.MAX_DELIVERED) {
118
+ const oldest = this.delivered.values().next().value;
119
+ if (oldest !== undefined)
120
+ this.delivered.delete(oldest);
121
+ }
122
+ this.delivered.add(key);
123
+ }
124
+ // -- inbound DATA handling ----------------------------------------------
125
+ async onData(stream, session, callback) {
126
+ const { simpleParser } = await loadMailparser();
127
+ const chunks = [];
128
+ for await (const chunk of stream) {
129
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
130
+ }
131
+ const raw = Buffer.concat(chunks);
132
+ if (stream.sizeExceeded) {
133
+ // Permanent: message too large.
134
+ callback(Object.assign(new Error("Message exceeds size limit"), { responseCode: 552 }));
135
+ return;
136
+ }
137
+ const parsed = await simpleParser(raw);
138
+ const auth = await this.verifyAuth(raw, session).catch(() => undefined);
139
+ const attachments = await this.storeAttachments(parsed);
140
+ const mailFrom = session.envelope?.mailFrom;
141
+ const mail = parsedToInboundMail(parsed, {
142
+ envelopeFrom: mailFrom ? mailFrom.address : "",
143
+ envelopeTo: (session.envelope?.rcptTo ?? []).map((r) => r.address),
144
+ remote: { ip: session.remoteAddress, reverseDns: session.clientHostname },
145
+ authResults: auth,
146
+ attachments,
147
+ sizeBytes: raw.length,
148
+ });
149
+ if (!this.handler) {
150
+ // No handler registered — refuse so the peer retries later.
151
+ callback(Object.assign(new Error("Not ready"), { responseCode: 451 }));
152
+ return;
153
+ }
154
+ try {
155
+ await this.handler(mail); // commit happens here
156
+ callback(); // ACK 250 only after the handler resolves
157
+ }
158
+ catch (err) {
159
+ // NACK — transient by default so the sender retries.
160
+ callback(Object.assign(err instanceof Error ? err : new Error(String(err)), {
161
+ responseCode: 451,
162
+ }));
163
+ }
164
+ }
165
+ async verifyAuth(raw, session) {
166
+ const mailauth = await loadMailauth().catch(() => undefined);
167
+ if (!mailauth)
168
+ return undefined;
169
+ const mailFrom = session.envelope?.mailFrom;
170
+ const res = await mailauth.authenticate(raw, {
171
+ ip: session.remoteAddress,
172
+ helo: session.hostNameAppearsAs,
173
+ sender: mailFrom ? mailFrom.address : undefined,
174
+ });
175
+ return mailauthToResults(res);
176
+ }
177
+ async storeAttachments(parsed) {
178
+ const store = this.opts.attachmentStore;
179
+ if (!store || !parsed.attachments?.length)
180
+ return undefined;
181
+ const out = [];
182
+ for (const a of parsed.attachments) {
183
+ const ref = await store.put(a.content, {
184
+ contentType: a.contentType,
185
+ filename: a.filename,
186
+ });
187
+ out.push({
188
+ filename: a.filename,
189
+ contentType: a.contentType,
190
+ contentId: a.cid,
191
+ contentRef: ref,
192
+ sizeBytes: a.size,
193
+ });
194
+ }
195
+ return out;
196
+ }
197
+ async getMailer() {
198
+ if (this.mailer)
199
+ return this.mailer;
200
+ const nodemailer = await loadNodemailer();
201
+ const secure = this.opts.relay.secure ?? false;
202
+ this.mailer = nodemailer.createTransport({
203
+ host: this.opts.relay.host,
204
+ port: this.opts.relay.port,
205
+ secure,
206
+ // Plaintext relay (TLS terminated upstream): don't attempt a STARTTLS
207
+ // upgrade against a server that isn't offering a trusted cert.
208
+ ignoreTLS: !secure,
209
+ auth: this.opts.relay.auth,
210
+ });
211
+ return this.mailer;
212
+ }
213
+ }
214
+ /** Map a nodemailer/SMTP send error to a classified result. */
215
+ export function smtpErrorToResult(err) {
216
+ const e = err;
217
+ const code = e?.responseCode;
218
+ if (typeof code === "number") {
219
+ if (code >= 500) {
220
+ return { disposition: "permanent", code, detail: e.response ?? e.message };
221
+ }
222
+ if (code >= 400) {
223
+ return { disposition: "transient", code, detail: e.response ?? e.message };
224
+ }
225
+ }
226
+ // No SMTP code (DNS/connection error) — transient, worth retrying.
227
+ return { disposition: "transient", detail: e?.message ?? "send failed" };
228
+ }
229
+ /** Build an InboundMail from mailparser output + session/transport context. */
230
+ export function parsedToInboundMail(parsed, ctx) {
231
+ const addrs = (a) => (a?.value ?? [])
232
+ .filter((v) => v.address)
233
+ .map((v) => ({ address: v.address, name: v.name }));
234
+ const refs = parsed.references
235
+ ? Array.isArray(parsed.references)
236
+ ? parsed.references
237
+ : parsed.references.split(/\s+/).filter(Boolean)
238
+ : undefined;
239
+ const raw = {};
240
+ if (parsed.headers) {
241
+ for (const [k, v] of parsed.headers.entries()) {
242
+ raw[k.toLowerCase()] = typeof v === "string" ? v : String(v);
243
+ }
244
+ }
245
+ return {
246
+ envelopeFrom: ctx.envelopeFrom,
247
+ envelopeTo: ctx.envelopeTo,
248
+ from: addrs(parsed.from)[0] ?? { address: ctx.envelopeFrom },
249
+ to: addrs(parsed.to),
250
+ cc: addrs(parsed.cc),
251
+ subject: parsed.subject,
252
+ text: parsed.text,
253
+ html: typeof parsed.html === "string" ? parsed.html : undefined,
254
+ headers: {
255
+ messageId: parsed.messageId,
256
+ inReplyTo: parsed.inReplyTo,
257
+ references: refs,
258
+ raw,
259
+ },
260
+ attachments: ctx.attachments,
261
+ authResults: ctx.authResults,
262
+ remote: ctx.remote,
263
+ bounce: parseDsnFromParsed(parsed),
264
+ sizeBytes: ctx.sizeBytes,
265
+ receivedAt: (parsed.date ?? new Date()).toISOString(),
266
+ };
267
+ }
268
+ /**
269
+ * Detect and parse an RFC 3464 delivery-status notification (bounce) from
270
+ * mailparser output. A DSN is `multipart/report; report-type=delivery-status`,
271
+ * with a `message/delivery-status` part carrying per-recipient Action/Status/
272
+ * Diagnostic-Code, and usually a returned-message part bearing the original
273
+ * Message-ID. Returns undefined for ordinary mail.
274
+ */
275
+ export function parseDsnFromParsed(parsed) {
276
+ const contentType = headerString(parsed.headers, "content-type").toLowerCase();
277
+ // A real RFC 3464 DSN is multipart/report at the TOP level. Detecting on a
278
+ // delivery-status part alone would misclassify an ordinary email that merely
279
+ // *carries* a forwarded/attached bounce (top-level multipart/mixed) — a very
280
+ // common support-inbox scenario. Require the top-level report content-type.
281
+ const isReport = contentType.includes("multipart/report") ||
282
+ contentType.includes("report-type=delivery-status");
283
+ if (!isReport)
284
+ return undefined;
285
+ // The per-recipient delivery-status fields live in a `message/delivery-status`
286
+ // MIME part — but mailparser only surfaces that as an attachment when it
287
+ // carries `Content-Disposition: attachment`. The common Postfix/Gmail form
288
+ // omits that, and mailparser folds the block into the text body instead. So
289
+ // read from the attachment when present, else fall back to the parsed text.
290
+ const statusPart = (parsed.attachments ?? []).find((a) => a.contentType?.toLowerCase().includes("delivery-status"));
291
+ const statusText = statusPart
292
+ ? statusPart.content.toString("utf8")
293
+ : parsed.text ?? "";
294
+ const field = (re, text) => {
295
+ const m = text.match(re);
296
+ return m ? m[1].trim() : undefined;
297
+ };
298
+ const actionRaw = field(/^Action:\s*([^\r\n]+)/im, statusText)?.toLowerCase();
299
+ const status = field(/^Status:\s*([0-9.]+)/im, statusText);
300
+ const diagnostic = field(/^Diagnostic-Code:\s*([^\r\n]+)/im, statusText);
301
+ const recipient = addrFromField(field(/^Final-Recipient:\s*([^\r\n]+)/im, statusText)) ??
302
+ addrFromField(field(/^Original-Recipient:\s*([^\r\n]+)/im, statusText));
303
+ // The original Message-ID may appear in the returned message part (surfaced as
304
+ // an attachment) or be folded into the text (text/rfc822-headers form), or as
305
+ // an Original-Envelope-Id / X-Original-Message-ID field.
306
+ const rfc822 = (parsed.attachments ?? []).find((a) => a.contentType?.toLowerCase().includes("rfc822"));
307
+ const originalMessageId = (rfc822 &&
308
+ field(/^Message-ID:\s*(<[^>\r\n]+>)/im, rfc822.content.toString("utf8"))) ??
309
+ field(/^(?:X-Original-Message-ID|Original-Envelope-Id):\s*(<[^>\r\n]+>)/im, statusText) ??
310
+ field(/^Message-ID:\s*(<[^>\r\n]+>)/im, statusText) ??
311
+ undefined;
312
+ // If nothing actionable parsed out, this isn't a usable DSN — don't synthesize
313
+ // a meaningless bounce.
314
+ if (!actionRaw && !status && !recipient && !originalMessageId && !diagnostic) {
315
+ return undefined;
316
+ }
317
+ // Map Action / Status to our coarse disposition.
318
+ let action = "failed";
319
+ if (actionRaw === "delayed" || status?.startsWith("4"))
320
+ action = "delayed";
321
+ else if (actionRaw === "delivered" || actionRaw === "relayed" || actionRaw === "expanded")
322
+ action = "delivered";
323
+ return { action, recipient: recipient ?? "", status, originalMessageId, diagnostic };
324
+ }
325
+ function headerString(headers, key) {
326
+ if (!headers)
327
+ return "";
328
+ const v = headers.get(key);
329
+ if (typeof v === "string")
330
+ return v;
331
+ if (v && typeof v === "object" && "value" in v) {
332
+ return String(v.value);
333
+ }
334
+ return v ? String(v) : "";
335
+ }
336
+ /** Extract the address from a DSN recipient field like "rfc822; user@host". */
337
+ function addrFromField(value) {
338
+ if (!value)
339
+ return undefined;
340
+ const semi = value.indexOf(";");
341
+ return (semi === -1 ? value : value.slice(semi + 1)).trim() || undefined;
342
+ }
343
+ /** Convert an OutboundMail to a nodemailer message object. */
344
+ export function toNodemailerMessage(env, dkim) {
345
+ const addr = (a) => a.name ? { name: a.name, address: a.address } : a.address;
346
+ return {
347
+ messageId: env.headers.messageId,
348
+ inReplyTo: env.headers.inReplyTo,
349
+ references: env.headers.references,
350
+ from: addr(env.from),
351
+ to: env.to.map(addr),
352
+ cc: env.cc?.map(addr),
353
+ bcc: env.bcc?.map(addr),
354
+ subject: env.subject,
355
+ text: env.text,
356
+ html: env.html,
357
+ attachments: env.attachments?.map((a) => ({
358
+ filename: a.filename,
359
+ contentType: a.contentType,
360
+ cid: a.contentId,
361
+ })),
362
+ ...(dkim ? { dkim } : {}),
363
+ };
364
+ }
365
+ /** Normalize a mailauth authenticate() result into InboundAuthResults. */
366
+ export function mailauthToResults(res) {
367
+ const spf = (res.spf?.status?.result ?? "none").toLowerCase();
368
+ const dkim = (res.dkim?.results?.[0]?.status?.result ?? "none").toLowerCase();
369
+ const dmarc = (res.dmarc?.status?.result ?? "none").toLowerCase();
370
+ const spfVal = spf === "pass" || spf === "fail" || spf === "softfail" || spf === "neutral"
371
+ ? spf
372
+ : "none";
373
+ return {
374
+ spf: spfVal,
375
+ dkim: dkim === "pass" ? "pass" : dkim === "fail" ? "fail" : "none",
376
+ dmarc: dmarc === "pass" ? "pass" : dmarc === "fail" ? "fail" : "none",
377
+ };
378
+ }
379
+ // ---------------------------------------------------------------------------
380
+ // Optional dynamic imports
381
+ // ---------------------------------------------------------------------------
382
+ async function dynImport(spec) {
383
+ // Variable specifier keeps TypeScript from resolving the optional peer dep at
384
+ // compile time, while Node/the bundler resolves it at runtime.
385
+ return (await import(/* @vite-ignore */ spec));
386
+ }
387
+ async function loadSmtpServer() {
388
+ try {
389
+ return await dynImport("smtp-server");
390
+ }
391
+ catch {
392
+ throw new Error('SmtpTransport requires the optional "smtp-server" package. Install it to enable inbound mail.');
393
+ }
394
+ }
395
+ async function loadNodemailer() {
396
+ try {
397
+ const mod = await dynImport("nodemailer");
398
+ return mod.default ?? mod;
399
+ }
400
+ catch {
401
+ throw new Error('SmtpTransport requires the optional "nodemailer" package. Install it to enable outbound mail.');
402
+ }
403
+ }
404
+ async function loadMailparser() {
405
+ try {
406
+ return await dynImport("mailparser");
407
+ }
408
+ catch {
409
+ throw new Error('SmtpTransport requires the optional "mailparser" package. Install it to parse inbound mail.');
410
+ }
411
+ }
412
+ async function loadMailauth() {
413
+ return await dynImport("mailauth");
414
+ }
415
+ //# sourceMappingURL=smtp-transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smtp-transport.js","sourceRoot":"","sources":["../../src/mail/smtp-transport.ts"],"names":[],"mappings":"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"}