@tyravel/mail 0.1.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.
Files changed (53) hide show
  1. package/dist/index.d.ts +11 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +8 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/mail-manager.d.ts +37 -0
  6. package/dist/mail-manager.d.ts.map +1 -0
  7. package/dist/mail-manager.js +119 -0
  8. package/dist/mail-manager.js.map +1 -0
  9. package/dist/mail.test.d.ts +2 -0
  10. package/dist/mail.test.d.ts.map +1 -0
  11. package/dist/mail.test.js +26 -0
  12. package/dist/mail.test.js.map +1 -0
  13. package/dist/mailable.d.ts +11 -0
  14. package/dist/mailable.d.ts.map +1 -0
  15. package/dist/mailable.js +9 -0
  16. package/dist/mailable.js.map +1 -0
  17. package/dist/mime.d.ts +6 -0
  18. package/dist/mime.d.ts.map +1 -0
  19. package/dist/mime.js +62 -0
  20. package/dist/mime.js.map +1 -0
  21. package/dist/mime.test.d.ts +2 -0
  22. package/dist/mime.test.d.ts.map +1 -0
  23. package/dist/mime.test.js +18 -0
  24. package/dist/mime.test.js.map +1 -0
  25. package/dist/queue-bridge.d.ts +9 -0
  26. package/dist/queue-bridge.d.ts.map +1 -0
  27. package/dist/queue-bridge.js +2 -0
  28. package/dist/queue-bridge.js.map +1 -0
  29. package/dist/queued-mail-context.d.ts +7 -0
  30. package/dist/queued-mail-context.d.ts.map +1 -0
  31. package/dist/queued-mail-context.js +11 -0
  32. package/dist/queued-mail-context.js.map +1 -0
  33. package/dist/queued-mailable.test.d.ts +2 -0
  34. package/dist/queued-mailable.test.d.ts.map +1 -0
  35. package/dist/queued-mailable.test.js +40 -0
  36. package/dist/queued-mailable.test.js.map +1 -0
  37. package/dist/send-mailable.d.ts +10 -0
  38. package/dist/send-mailable.d.ts.map +1 -0
  39. package/dist/send-mailable.js +9 -0
  40. package/dist/send-mailable.js.map +1 -0
  41. package/dist/smtp-transport.d.ts +8 -0
  42. package/dist/smtp-transport.d.ts.map +1 -0
  43. package/dist/smtp-transport.js +169 -0
  44. package/dist/smtp-transport.js.map +1 -0
  45. package/dist/transport.d.ts +14 -0
  46. package/dist/transport.d.ts.map +1 -0
  47. package/dist/transport.js +22 -0
  48. package/dist/transport.js.map +1 -0
  49. package/dist/types.d.ts +40 -0
  50. package/dist/types.d.ts.map +1 -0
  51. package/dist/types.js +2 -0
  52. package/dist/types.js.map +1 -0
  53. package/package.json +39 -0
@@ -0,0 +1,11 @@
1
+ export type { MailAddress, MailConfig, MailConnectionConfig, MailMessage, SmtpMailConfig } from './types.js';
2
+ export { SmtpMailTransport } from './smtp-transport.js';
3
+ export { buildMimeMessage } from './mime.js';
4
+ export { Mailable } from './mailable.js';
5
+ export { MailManager, Mailer, shouldQueueMailable } from './mail-manager.js';
6
+ export { ArrayMailTransport, LogMailTransport, type MailTransport, } from './transport.js';
7
+ export { SendMailable } from './send-mailable.js';
8
+ export type { SendMailableData } from './send-mailable.js';
9
+ export { setQueuedMailContext } from './queued-mail-context.js';
10
+ export type { MailQueueBridge } from './queue-bridge.js';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,oBAAoB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC7G,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,aAAa,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { SmtpMailTransport } from './smtp-transport.js';
2
+ export { buildMimeMessage } from './mime.js';
3
+ export { Mailable } from './mailable.js';
4
+ export { MailManager, Mailer, shouldQueueMailable } from './mail-manager.js';
5
+ export { ArrayMailTransport, LogMailTransport, } from './transport.js';
6
+ export { SendMailable } from './send-mailable.js';
7
+ export { setQueuedMailContext } from './queued-mail-context.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EACL,kBAAkB,EAClB,gBAAgB,GAEjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { type MailTransport } from './transport.js';
2
+ import type { MailAddress, MailConfig, MailMessage } from './types.js';
3
+ import { Mailable } from './mailable.js';
4
+ import type { MailQueueBridge } from './queue-bridge.js';
5
+ export declare class MailManager {
6
+ private readonly config;
7
+ private readonly queue?;
8
+ private readonly transports;
9
+ private queueDefaults;
10
+ constructor(config: MailConfig, queue?: MailQueueBridge | undefined);
11
+ setQueueDefaults(options: {
12
+ connection?: string;
13
+ queue?: string;
14
+ }): void;
15
+ mailer(name?: string): Mailer;
16
+ transport(name?: string): MailTransport;
17
+ private resolveTransport;
18
+ private buildTransport;
19
+ }
20
+ export declare class Mailer {
21
+ private readonly transport;
22
+ private readonly defaultFrom;
23
+ private readonly mailConnection;
24
+ private readonly queue?;
25
+ private readonly queueDefaults;
26
+ private recipients;
27
+ constructor(transport: MailTransport, defaultFrom: MailAddress, mailConnection: string, queue?: MailQueueBridge | undefined, queueDefaults?: {
28
+ connection?: string;
29
+ queue?: string;
30
+ });
31
+ to(address: string | MailAddress | Array<string | MailAddress>): this;
32
+ send(mailable: Mailable | MailMessage): Promise<void>;
33
+ private mergeMessage;
34
+ private resolveQueueOptions;
35
+ }
36
+ export declare function shouldQueueMailable(mailable: Mailable): boolean;
37
+ //# sourceMappingURL=mail-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail-manager.d.ts","sourceRoot":"","sources":["../src/mail-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwC,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE1F,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAwB,WAAW,EAAE,MAAM,YAAY,CAAC;AAC7F,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGzD,qBAAa,WAAW;IAKpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IALzB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoC;IAC/D,OAAO,CAAC,aAAa,CAA+C;gBAGjD,MAAM,EAAE,UAAU,EAClB,KAAK,CAAC,EAAE,eAAe,YAAA;IAG1C,gBAAgB,CAAC,OAAO,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAIxE,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;IAW7B,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,aAAa;IAIvC,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,cAAc;CAYvB;AAED,qBAAa,MAAM;IAIf,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IACvB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAPhC,OAAO,CAAC,UAAU,CAAqB;gBAGpB,SAAS,EAAE,aAAa,EACxB,WAAW,EAAE,WAAW,EACxB,cAAc,EAAE,MAAM,EACtB,KAAK,CAAC,EAAE,eAAe,YAAA,EACvB,aAAa,GAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO;IAG9E,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,KAAK,CAAC,MAAM,GAAG,WAAW,CAAC,GAAG,IAAI;IAQ/D,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;YAgB7C,YAAY;IAoB1B,OAAO,CAAC,mBAAmB;CAW5B;AASD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAE/D"}
@@ -0,0 +1,119 @@
1
+ import { ArrayMailTransport, LogMailTransport } from './transport.js';
2
+ import { SmtpMailTransport } from './smtp-transport.js';
3
+ import { Mailable } from './mailable.js';
4
+ import { SendMailable } from './send-mailable.js';
5
+ export class MailManager {
6
+ config;
7
+ queue;
8
+ transports = new Map();
9
+ queueDefaults = {};
10
+ constructor(config, queue) {
11
+ this.config = config;
12
+ this.queue = queue;
13
+ }
14
+ setQueueDefaults(options) {
15
+ this.queueDefaults = options;
16
+ }
17
+ mailer(name) {
18
+ const connection = name ?? this.config.default;
19
+ return new Mailer(this.resolveTransport(connection), this.config.from, connection, this.queue, this.queueDefaults);
20
+ }
21
+ transport(name) {
22
+ return this.resolveTransport(name ?? this.config.default);
23
+ }
24
+ resolveTransport(connection) {
25
+ const existing = this.transports.get(connection);
26
+ if (existing) {
27
+ return existing;
28
+ }
29
+ const config = this.config.connections[connection];
30
+ if (!config) {
31
+ throw new Error(`Mail connection [${connection}] is not configured.`);
32
+ }
33
+ const transport = this.buildTransport(config);
34
+ this.transports.set(connection, transport);
35
+ return transport;
36
+ }
37
+ buildTransport(config) {
38
+ switch (config.driver) {
39
+ case 'array':
40
+ return new ArrayMailTransport();
41
+ case 'log':
42
+ return new LogMailTransport(config.channel ?? 'stdout');
43
+ case 'smtp':
44
+ return new SmtpMailTransport(config);
45
+ default:
46
+ throw new Error(`Unsupported mail driver: ${config.driver}`);
47
+ }
48
+ }
49
+ }
50
+ export class Mailer {
51
+ transport;
52
+ defaultFrom;
53
+ mailConnection;
54
+ queue;
55
+ queueDefaults;
56
+ recipients = [];
57
+ constructor(transport, defaultFrom, mailConnection, queue, queueDefaults = {}) {
58
+ this.transport = transport;
59
+ this.defaultFrom = defaultFrom;
60
+ this.mailConnection = mailConnection;
61
+ this.queue = queue;
62
+ this.queueDefaults = queueDefaults;
63
+ }
64
+ to(address) {
65
+ const list = Array.isArray(address) ? address : [address];
66
+ for (const entry of list) {
67
+ this.recipients.push(normalizeAddress(entry));
68
+ }
69
+ return this;
70
+ }
71
+ async send(mailable) {
72
+ const merged = await this.mergeMessage(mailable);
73
+ if (mailable instanceof Mailable && mailable.shouldQueue?.() === true && this.queue) {
74
+ const job = new SendMailable({
75
+ mailConnection: this.mailConnection,
76
+ message: merged,
77
+ });
78
+ const options = this.resolveQueueOptions(mailable);
79
+ await this.queue.dispatch(job, options);
80
+ return;
81
+ }
82
+ await this.transport.send(merged);
83
+ }
84
+ async mergeMessage(mailable) {
85
+ const resolved = mailable instanceof Mailable ? await mailable.toMessage() : mailable;
86
+ const merged = {
87
+ subject: resolved.subject,
88
+ from: resolved.from ?? this.defaultFrom,
89
+ to: resolved.to.length > 0 ? resolved.to : this.recipients,
90
+ cc: resolved.cc,
91
+ bcc: resolved.bcc,
92
+ replyTo: resolved.replyTo,
93
+ text: resolved.text,
94
+ html: resolved.html,
95
+ tags: resolved.tags,
96
+ };
97
+ if (merged.to.length === 0) {
98
+ throw new Error('Mail message requires at least one recipient.');
99
+ }
100
+ return merged;
101
+ }
102
+ resolveQueueOptions(mailable) {
103
+ return {
104
+ connection: mailable.connection ?? this.queueDefaults.connection,
105
+ queue: mailable.queue ?? this.queueDefaults.queue,
106
+ delaySeconds: mailable.delaySeconds,
107
+ };
108
+ }
109
+ }
110
+ function normalizeAddress(entry) {
111
+ if (typeof entry === 'string') {
112
+ return { address: entry };
113
+ }
114
+ return entry;
115
+ }
116
+ export function shouldQueueMailable(mailable) {
117
+ return mailable.shouldQueue?.() === true;
118
+ }
119
+ //# sourceMappingURL=mail-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail-manager.js","sourceRoot":"","sources":["../src/mail-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mail.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail.test.d.ts","sourceRoot":"","sources":["../src/mail.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,26 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { MailManager, Mailable } from './index.js';
3
+ class WelcomeMail extends Mailable {
4
+ build() {
5
+ return {
6
+ subject: 'Welcome',
7
+ to: [],
8
+ text: 'Hello',
9
+ };
10
+ }
11
+ }
12
+ describe('MailManager', () => {
13
+ it('sends mailables through array transport', async () => {
14
+ const manager = new MailManager({
15
+ default: 'array',
16
+ from: { address: 'app@example.com', name: 'App' },
17
+ connections: { array: { driver: 'array' } },
18
+ });
19
+ await manager.mailer().to('user@example.com').send(new WelcomeMail());
20
+ const transport = manager.transport('array');
21
+ expect(transport.messages).toHaveLength(1);
22
+ expect(transport.messages[0]?.subject).toBe('Welcome');
23
+ expect(transport.messages[0]?.to[0]?.address).toBe('user@example.com');
24
+ });
25
+ });
26
+ //# sourceMappingURL=mail.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mail.test.js","sourceRoot":"","sources":["../src/mail.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAsB,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGvE,MAAM,WAAY,SAAQ,QAAQ;IAChC,KAAK;QACH,OAAO;YACL,OAAO,EAAE,SAAS;YAClB,EAAE,EAAE,EAAE;YACN,IAAI,EAAE,OAAO;SACd,CAAC;IACJ,CAAC;CACF;AAED,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QACvD,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC;YAC9B,OAAO,EAAE,OAAO;YAChB,IAAI,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,KAAK,EAAE;YACjD,WAAW,EAAE,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE;SAC5C,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC,CAAC;QACtE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAuB,CAAC;QACnE,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACvD,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { MailMessage } from './types.js';
2
+ export declare abstract class Mailable {
3
+ abstract build(): MailMessage | Promise<MailMessage>;
4
+ /** When true, the mailable is pushed to the queue instead of sent immediately. */
5
+ shouldQueue?(): boolean;
6
+ connection?: string;
7
+ queue?: string;
8
+ delaySeconds?: number;
9
+ toMessage(): Promise<MailMessage>;
10
+ }
11
+ //# sourceMappingURL=mailable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mailable.d.ts","sourceRoot":"","sources":["../src/mailable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,8BAAsB,QAAQ;IAC5B,QAAQ,CAAC,KAAK,IAAI,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAEpD,kFAAkF;IAClF,WAAW,CAAC,IAAI,OAAO;IAEvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IAEhB,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC;CAGxC"}
@@ -0,0 +1,9 @@
1
+ export class Mailable {
2
+ connection;
3
+ queue;
4
+ delaySeconds;
5
+ async toMessage() {
6
+ return this.build();
7
+ }
8
+ }
9
+ //# sourceMappingURL=mailable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mailable.js","sourceRoot":"","sources":["../src/mailable.ts"],"names":[],"mappings":"AAEA,MAAM,OAAgB,QAAQ;IAM5B,UAAU,CAAU;IACpB,KAAK,CAAU;IACf,YAAY,CAAU;IAEtB,KAAK,CAAC,SAAS;QACb,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;CACF"}
package/dist/mime.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { MailAddress, MailMessage } from './types.js';
2
+ export declare function formatMailbox(address: MailAddress): string;
3
+ export declare function buildMimeMessage(message: MailMessage): string;
4
+ export declare function encodeSubject(subject: string): string;
5
+ export declare function dotStuff(body: string): string;
6
+ //# sourceMappingURL=mime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mime.d.ts","sourceRoot":"","sources":["../src/mime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE3D,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAM1D;AAMD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAqC7D;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMrD;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK7C"}
package/dist/mime.js ADDED
@@ -0,0 +1,62 @@
1
+ export function formatMailbox(address) {
2
+ if (address.name) {
3
+ const encoded = encodeHeaderValue(address.name);
4
+ return `"${encoded}" <${address.address}>`;
5
+ }
6
+ return address.address;
7
+ }
8
+ function encodeHeaderValue(value) {
9
+ return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
10
+ }
11
+ export function buildMimeMessage(message) {
12
+ const lines = [];
13
+ lines.push(`From: ${formatMailbox(message.from)}`);
14
+ lines.push(`To: ${message.to.map(formatMailbox).join(', ')}`);
15
+ if (message.cc?.length) {
16
+ lines.push(`Cc: ${message.cc.map(formatMailbox).join(', ')}`);
17
+ }
18
+ if (message.replyTo) {
19
+ lines.push(`Reply-To: ${formatMailbox(message.replyTo)}`);
20
+ }
21
+ lines.push(`Subject: ${encodeSubject(message.subject)}`);
22
+ lines.push('MIME-Version: 1.0');
23
+ if (message.html && message.text) {
24
+ const boundary = `----=_Tyravel_${Date.now()}`;
25
+ lines.push(`Content-Type: multipart/alternative; boundary="${boundary}"`);
26
+ lines.push('');
27
+ lines.push(`--${boundary}`);
28
+ lines.push('Content-Type: text/plain; charset=utf-8');
29
+ lines.push('');
30
+ lines.push(message.text);
31
+ lines.push(`--${boundary}`);
32
+ lines.push('Content-Type: text/html; charset=utf-8');
33
+ lines.push('');
34
+ lines.push(message.html);
35
+ lines.push(`--${boundary}--`);
36
+ }
37
+ else if (message.html) {
38
+ lines.push('Content-Type: text/html; charset=utf-8');
39
+ lines.push('');
40
+ lines.push(message.html);
41
+ }
42
+ else {
43
+ lines.push('Content-Type: text/plain; charset=utf-8');
44
+ lines.push('');
45
+ lines.push(message.text ?? '');
46
+ }
47
+ return lines.join('\r\n');
48
+ }
49
+ export function encodeSubject(subject) {
50
+ if (/^[\x20-\x7E]*$/.test(subject)) {
51
+ return subject;
52
+ }
53
+ const encoded = Buffer.from(subject, 'utf8').toString('base64');
54
+ return `=?UTF-8?B?${encoded}?=`;
55
+ }
56
+ export function dotStuff(body) {
57
+ return body
58
+ .split('\r\n')
59
+ .map((line) => (line.startsWith('.') ? `.${line}` : line))
60
+ .join('\r\n');
61
+ }
62
+ //# sourceMappingURL=mime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mime.js","sourceRoot":"","sources":["../src/mime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mime.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mime.test.d.ts","sourceRoot":"","sources":["../src/mime.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { buildMimeMessage, dotStuff, formatMailbox } from './mime.js';
3
+ describe('mime', () => {
4
+ it('formats multipart messages', () => {
5
+ const raw = buildMimeMessage({
6
+ subject: 'Hello',
7
+ from: { address: 'app@example.com', name: 'App' },
8
+ to: [{ address: 'user@example.com' }],
9
+ text: 'plain',
10
+ html: '<p>html</p>',
11
+ });
12
+ expect(raw).toContain('multipart/alternative');
13
+ expect(raw).toContain('plain');
14
+ expect(formatMailbox({ address: 'a@b.com', name: 'Test' })).toContain('a@b.com');
15
+ expect(dotStuff('.hidden')).toBe('..hidden');
16
+ });
17
+ });
18
+ //# sourceMappingURL=mime.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mime.test.js","sourceRoot":"","sources":["../src/mime.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAEtE,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;IACpB,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,GAAG,GAAG,gBAAgB,CAAC;YAC3B,OAAO,EAAE,OAAO;YAChB,IAAI,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,KAAK,EAAE;YACjD,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC;YACrC,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,aAAa;SACpB,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;QAC/C,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC/B,MAAM,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACjF,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Job } from '@tyravel/queue';
2
+ export interface MailQueueBridge {
3
+ dispatch(job: Job, options?: {
4
+ connection?: string;
5
+ queue?: string;
6
+ delaySeconds?: number;
7
+ }): Promise<void>;
8
+ }
9
+ //# sourceMappingURL=queue-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-bridge.d.ts","sourceRoot":"","sources":["../src/queue-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CACN,GAAG,EAAE,GAAG,EACR,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,GACvE,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=queue-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-bridge.js","sourceRoot":"","sources":["../src/queue-bridge.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ import type { MailManager } from './mail-manager.js';
2
+ export interface QueuedMailContext {
3
+ manager: MailManager;
4
+ }
5
+ export declare function setQueuedMailContext(value: QueuedMailContext): void;
6
+ export declare function getQueuedMailContext(): QueuedMailContext;
7
+ //# sourceMappingURL=queued-mail-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queued-mail-context.d.ts","sourceRoot":"","sources":["../src/queued-mail-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,WAAW,CAAC;CACtB;AAID,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAEnE;AAED,wBAAgB,oBAAoB,IAAI,iBAAiB,CAKxD"}
@@ -0,0 +1,11 @@
1
+ let context;
2
+ export function setQueuedMailContext(value) {
3
+ context = value;
4
+ }
5
+ export function getQueuedMailContext() {
6
+ if (!context) {
7
+ throw new Error('Queued mail context is not configured.');
8
+ }
9
+ return context;
10
+ }
11
+ //# sourceMappingURL=queued-mail-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queued-mail-context.js","sourceRoot":"","sources":["../src/queued-mail-context.ts"],"names":[],"mappings":"AAMA,IAAI,OAAsC,CAAC;AAE3C,MAAM,UAAU,oBAAoB,CAAC,KAAwB;IAC3D,OAAO,GAAG,KAAK,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=queued-mailable.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queued-mailable.test.d.ts","sourceRoot":"","sources":["../src/queued-mailable.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,40 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { MailManager, Mailable } from './index.js';
3
+ import { JobRegistry, QueueManager, QueueWorker } from '@tyravel/queue';
4
+ import { SendMailable } from './send-mailable.js';
5
+ import { setQueuedMailContext } from './queued-mail-context.js';
6
+ class QueuedWelcome extends Mailable {
7
+ shouldQueue() {
8
+ return true;
9
+ }
10
+ build() {
11
+ return {
12
+ subject: 'Queued welcome',
13
+ to: [],
14
+ text: 'Later',
15
+ };
16
+ }
17
+ }
18
+ describe('queued mailables', () => {
19
+ it('dispatches SendMailable on sync queue', async () => {
20
+ const manager = new MailManager({
21
+ default: 'array',
22
+ from: { address: 'app@example.com' },
23
+ connections: { array: { driver: 'array' } },
24
+ }, {
25
+ dispatch: async (job) => {
26
+ await queueManager.connection('sync').push(job);
27
+ },
28
+ });
29
+ const jobs = new JobRegistry();
30
+ jobs.register(SendMailable);
31
+ const worker = new QueueWorker(jobs);
32
+ const queueManager = new QueueManager({ default: 'sync', connections: { sync: { driver: 'sync' } } }, worker);
33
+ setQueuedMailContext({ manager });
34
+ await manager.mailer().to('queued@example.com').send(new QueuedWelcome());
35
+ const transport = manager.transport('array');
36
+ expect(transport.messages[0]?.subject).toBe('Queued welcome');
37
+ expect(transport.messages[0]?.to[0]?.address).toBe('queued@example.com');
38
+ });
39
+ });
40
+ //# sourceMappingURL=queued-mailable.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queued-mailable.test.js","sourceRoot":"","sources":["../src/queued-mailable.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAsB,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAGhE,MAAM,aAAc,SAAQ,QAAQ;IACzB,WAAW;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK;QACH,OAAO;YACL,OAAO,EAAE,gBAAgB;YACzB,EAAE,EAAE,EAAE;YACN,IAAI,EAAE,OAAO;SACd,CAAC;IACJ,CAAC;CACF;AAED,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,OAAO,GAAG,IAAI,WAAW,CAC7B;YACE,OAAO,EAAE,OAAO;YAChB,IAAI,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE;YACpC,WAAW,EAAE,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE;SAC5C,EACD;YACE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;gBACtB,MAAM,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAClD,CAAC;SACF,CACF,CAAC;QAEF,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC;QAC/B,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;QAC5B,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,YAAY,GAAG,IAAI,YAAY,CACnC,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,EAC9D,MAAM,CACP,CAAC;QAEF,oBAAoB,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QAElC,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,IAAI,aAAa,EAAE,CAAC,CAAC;QAE1E,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAuB,CAAC;QACnE,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC9D,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { Job } from '@tyravel/queue';
2
+ import type { MailMessage } from './types.js';
3
+ export interface SendMailableData extends Record<string, unknown> {
4
+ mailConnection: string;
5
+ message: MailMessage;
6
+ }
7
+ export declare class SendMailable extends Job<SendMailableData> {
8
+ handle(): Promise<void>;
9
+ }
10
+ //# sourceMappingURL=send-mailable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-mailable.d.ts","sourceRoot":"","sources":["../src/send-mailable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,MAAM,WAAW,gBAAiB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/D,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,qBAAa,YAAa,SAAQ,GAAG,CAAC,gBAAgB,CAAC;IACtC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAIvC"}
@@ -0,0 +1,9 @@
1
+ import { Job } from '@tyravel/queue';
2
+ import { getQueuedMailContext } from './queued-mail-context.js';
3
+ export class SendMailable extends Job {
4
+ async handle() {
5
+ const { manager } = getQueuedMailContext();
6
+ await manager.mailer(this.data.mailConnection).send(this.data.message);
7
+ }
8
+ }
9
+ //# sourceMappingURL=send-mailable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-mailable.js","sourceRoot":"","sources":["../src/send-mailable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAErC,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAOhE,MAAM,OAAO,YAAa,SAAQ,GAAqB;IAC5C,KAAK,CAAC,MAAM;QACnB,MAAM,EAAE,OAAO,EAAE,GAAG,oBAAoB,EAAE,CAAC;QAC3C,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzE,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ import type { MailMessage, SmtpMailConfig } from './types.js';
2
+ import type { MailTransport } from './transport.js';
3
+ export declare class SmtpMailTransport implements MailTransport {
4
+ private readonly config;
5
+ constructor(config: SmtpMailConfig);
6
+ send(message: MailMessage): Promise<void>;
7
+ }
8
+ //# sourceMappingURL=smtp-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smtp-transport.d.ts","sourceRoot":"","sources":["../src/smtp-transport.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGpD,qBAAa,iBAAkB,YAAW,aAAa;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,cAAc;IAE7C,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAmChD"}
@@ -0,0 +1,169 @@
1
+ import { connect as tlsConnect } from 'node:tls';
2
+ import { connect as netConnect } from 'node:net';
3
+ import { buildMimeMessage, dotStuff } from './mime.js';
4
+ export class SmtpMailTransport {
5
+ config;
6
+ constructor(config) {
7
+ this.config = config;
8
+ }
9
+ async send(message) {
10
+ const encryption = this.config.encryption ?? 'tls';
11
+ const port = this.config.port ?? (encryption === 'ssl' ? 465 : 587);
12
+ const timeout = this.config.timeout ?? 30_000;
13
+ const socket = encryption === 'ssl'
14
+ ? await openSslSocket(this.config.host, port, timeout)
15
+ : await openPlainSocket(this.config.host, port, timeout);
16
+ try {
17
+ await readGreeting(socket, timeout);
18
+ await sendCommand(socket, `EHLO tyravel.local`, timeout);
19
+ if (encryption === 'tls') {
20
+ const reply = await sendCommand(socket, 'STARTTLS', timeout);
21
+ if (!reply.startsWith('220')) {
22
+ throw new Error(`SMTP STARTTLS failed: ${reply}`);
23
+ }
24
+ const upgraded = await upgradeTls(socket, this.config.host, timeout);
25
+ await sendCommand(upgraded, `EHLO tyravel.local`, timeout);
26
+ await authenticate(upgraded, this.config, timeout);
27
+ await transmit(upgraded, message, timeout);
28
+ await sendCommand(upgraded, 'QUIT', timeout);
29
+ upgraded.end();
30
+ return;
31
+ }
32
+ await authenticate(socket, this.config, timeout);
33
+ await transmit(socket, message, timeout);
34
+ await sendCommand(socket, 'QUIT', timeout);
35
+ }
36
+ finally {
37
+ socket.end();
38
+ }
39
+ }
40
+ }
41
+ async function transmit(socket, message, timeout) {
42
+ const from = message.from.address;
43
+ await sendCommand(socket, `MAIL FROM:<${from}>`, timeout);
44
+ for (const recipient of allRecipients(message)) {
45
+ await sendCommand(socket, `RCPT TO:<${recipient}>`, timeout);
46
+ }
47
+ const body = dotStuff(buildMimeMessage(message));
48
+ const dataReply = await sendCommand(socket, 'DATA', timeout);
49
+ if (!dataReply.startsWith('354')) {
50
+ throw new Error(`SMTP DATA rejected: ${dataReply}`);
51
+ }
52
+ socket.write(`${body}\r\n.\r\n`);
53
+ const final = await readReply(socket, timeout);
54
+ if (!final.startsWith('250')) {
55
+ throw new Error(`SMTP message not accepted: ${final}`);
56
+ }
57
+ }
58
+ function allRecipients(message) {
59
+ const set = new Set();
60
+ for (const entry of [...message.to, ...(message.cc ?? []), ...(message.bcc ?? [])]) {
61
+ set.add(entry.address);
62
+ }
63
+ return [...set];
64
+ }
65
+ async function authenticate(socket, config, timeout) {
66
+ if (!config.username || !config.password) {
67
+ return;
68
+ }
69
+ const modes = await sendCommand(socket, 'AUTH LOGIN', timeout);
70
+ if (!modes.startsWith('334')) {
71
+ throw new Error(`SMTP AUTH not supported: ${modes}`);
72
+ }
73
+ await sendCommand(socket, Buffer.from(config.username).toString('base64'), timeout);
74
+ const passReply = await sendCommand(socket, Buffer.from(config.password).toString('base64'), timeout);
75
+ if (!passReply.startsWith('235')) {
76
+ throw new Error(`SMTP authentication failed: ${passReply}`);
77
+ }
78
+ }
79
+ function openPlainSocket(host, port, timeout) {
80
+ return new Promise((resolve, reject) => {
81
+ const socket = netConnect({ host, port });
82
+ const timer = setTimeout(() => {
83
+ socket.destroy();
84
+ reject(new Error(`SMTP connection timed out after ${timeout}ms`));
85
+ }, timeout);
86
+ socket.once('error', (error) => {
87
+ clearTimeout(timer);
88
+ reject(error);
89
+ });
90
+ socket.once('connect', () => {
91
+ clearTimeout(timer);
92
+ resolve(socket);
93
+ });
94
+ });
95
+ }
96
+ function openSslSocket(host, port, timeout) {
97
+ return new Promise((resolve, reject) => {
98
+ const socket = tlsConnect({ host, port, servername: host });
99
+ const timer = setTimeout(() => {
100
+ socket.destroy();
101
+ reject(new Error(`SMTP SSL connection timed out after ${timeout}ms`));
102
+ }, timeout);
103
+ socket.once('error', (error) => {
104
+ clearTimeout(timer);
105
+ reject(error);
106
+ });
107
+ socket.once('secureConnect', () => {
108
+ clearTimeout(timer);
109
+ resolve(socket);
110
+ });
111
+ });
112
+ }
113
+ function upgradeTls(socket, host, timeout) {
114
+ return new Promise((resolve, reject) => {
115
+ const upgraded = tlsConnect({
116
+ socket,
117
+ servername: host,
118
+ });
119
+ const timer = setTimeout(() => {
120
+ upgraded.destroy();
121
+ reject(new Error(`SMTP TLS upgrade timed out after ${timeout}ms`));
122
+ }, timeout);
123
+ upgraded.once('error', (error) => {
124
+ clearTimeout(timer);
125
+ reject(error);
126
+ });
127
+ upgraded.once('secureConnect', () => {
128
+ clearTimeout(timer);
129
+ resolve(upgraded);
130
+ });
131
+ });
132
+ }
133
+ async function readGreeting(socket, timeout) {
134
+ const reply = await readReply(socket, timeout);
135
+ if (!reply.startsWith('220')) {
136
+ throw new Error(`SMTP greeting failed: ${reply}`);
137
+ }
138
+ }
139
+ async function sendCommand(socket, command, timeout) {
140
+ socket.write(`${command}\r\n`);
141
+ return readReply(socket, timeout);
142
+ }
143
+ function readReply(socket, timeout) {
144
+ return new Promise((resolve, reject) => {
145
+ let buffer = '';
146
+ const timer = setTimeout(() => {
147
+ socket.off('data', onData);
148
+ reject(new Error(`SMTP read timed out after ${timeout}ms`));
149
+ }, timeout);
150
+ const onData = (chunk) => {
151
+ buffer += chunk.toString('utf8');
152
+ const lines = buffer.split('\r\n').filter((line) => line.length > 0);
153
+ const last = lines.at(-1);
154
+ if (!last || last.length < 4) {
155
+ return;
156
+ }
157
+ const code = last.slice(0, 3);
158
+ const cont = last[3];
159
+ if (cont === '-') {
160
+ return;
161
+ }
162
+ clearTimeout(timer);
163
+ socket.off('data', onData);
164
+ resolve(lines.join('\r\n'));
165
+ };
166
+ socket.on('data', onData);
167
+ });
168
+ }
169
+ //# sourceMappingURL=smtp-transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smtp-transport.js","sourceRoot":"","sources":["../src/smtp-transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { MailMessage } from './types.js';
2
+ export interface MailTransport {
3
+ send(message: MailMessage): Promise<void>;
4
+ }
5
+ export declare class ArrayMailTransport implements MailTransport {
6
+ readonly messages: MailMessage[];
7
+ send(message: MailMessage): Promise<void>;
8
+ }
9
+ export declare class LogMailTransport implements MailTransport {
10
+ private readonly channel;
11
+ constructor(channel?: 'stdout' | 'stderr');
12
+ send(message: MailMessage): Promise<void>;
13
+ }
14
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAED,qBAAa,kBAAmB,YAAW,aAAa;IACtD,QAAQ,CAAC,QAAQ,EAAE,WAAW,EAAE,CAAM;IAEhC,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAGhD;AAED,qBAAa,gBAAiB,YAAW,aAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE,QAAQ,GAAG,QAAmB;IAE9D,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAQhD"}
@@ -0,0 +1,22 @@
1
+ export class ArrayMailTransport {
2
+ messages = [];
3
+ async send(message) {
4
+ this.messages.push(structuredClone(message));
5
+ }
6
+ }
7
+ export class LogMailTransport {
8
+ channel;
9
+ constructor(channel = 'stdout') {
10
+ this.channel = channel;
11
+ }
12
+ async send(message) {
13
+ const line = `[mail] ${message.subject} → ${message.to.map((t) => t.address).join(', ')}`;
14
+ if (this.channel === 'stderr') {
15
+ console.error(line);
16
+ }
17
+ else {
18
+ console.log(line);
19
+ }
20
+ }
21
+ }
22
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,kBAAkB;IACpB,QAAQ,GAAkB,EAAE,CAAC;IAEtC,KAAK,CAAC,IAAI,CAAC,OAAoB;QAC7B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/C,CAAC;CACF;AAED,MAAM,OAAO,gBAAgB;IACE;IAA7B,YAA6B,UAA+B,QAAQ;QAAvC,YAAO,GAAP,OAAO,CAAgC;IAAG,CAAC;IAExE,KAAK,CAAC,IAAI,CAAC,OAAoB;QAC7B,MAAM,IAAI,GAAG,UAAU,OAAO,CAAC,OAAO,MAAM,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1F,IAAI,IAAI,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,40 @@
1
+ export interface MailAddress {
2
+ address: string;
3
+ name?: string;
4
+ }
5
+ export interface MailMessage {
6
+ subject: string;
7
+ from?: MailAddress;
8
+ to: MailAddress[];
9
+ cc?: MailAddress[];
10
+ bcc?: MailAddress[];
11
+ replyTo?: MailAddress;
12
+ text?: string;
13
+ html?: string;
14
+ tags?: string[];
15
+ }
16
+ export interface ArrayMailConfig {
17
+ driver: 'array';
18
+ }
19
+ export interface LogMailConfig {
20
+ driver: 'log';
21
+ channel?: 'stdout' | 'stderr';
22
+ }
23
+ export interface SmtpMailConfig {
24
+ driver: 'smtp';
25
+ host: string;
26
+ port?: number;
27
+ username?: string;
28
+ password?: string;
29
+ encryption?: 'tls' | 'ssl' | null;
30
+ timeout?: number;
31
+ }
32
+ export type MailConnectionConfig = ArrayMailConfig | LogMailConfig | SmtpMailConfig;
33
+ export interface MailConfig {
34
+ default: string;
35
+ from: MailAddress;
36
+ connections: Record<string, MailConnectionConfig>;
37
+ queue?: string;
38
+ queueConnection?: string;
39
+ }
40
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,EAAE,EAAE,WAAW,EAAE,CAAC;IAClB,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC;IACnB,GAAG,CAAC,EAAE,WAAW,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,KAAK,CAAC;IACd,OAAO,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,IAAI,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,oBAAoB,GAAG,eAAe,GAAG,aAAa,GAAG,cAAc,CAAC;AAEpF,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@tyravel/mail",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "scripts": {
14
+ "build": "tsc -p tsconfig.json",
15
+ "typecheck": "tsc -p tsconfig.json --noEmit"
16
+ },
17
+ "dependencies": {
18
+ "@tyravel/queue": "0.1.0"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "engines": {
24
+ "node": ">=22"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "^22.15.30"
28
+ },
29
+ "description": "Mail manager, mailables, and SMTP for Tyravel",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/thesimonharms/tyravel.git",
34
+ "directory": "packages/mail"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ }
39
+ }