@geekmidas/emailkit 10.0.0-alpha.9 → 10.0.0-alpha.91

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 (62) hide show
  1. package/dist/client.cjs +124 -3
  2. package/dist/client.cjs.map +1 -0
  3. package/dist/client.d.cts +23 -3
  4. package/dist/client.d.cts.map +1 -0
  5. package/dist/client.d.mts +23 -3
  6. package/dist/client.d.mts.map +1 -0
  7. package/dist/client.mjs +99 -2
  8. package/dist/client.mjs.map +1 -0
  9. package/dist/errors.cjs +52 -4
  10. package/dist/errors.cjs.map +1 -0
  11. package/dist/errors.d.cts +39 -2
  12. package/dist/errors.d.cts.map +1 -0
  13. package/dist/errors.d.mts +39 -2
  14. package/dist/errors.d.mts.map +1 -0
  15. package/dist/errors.mjs +49 -2
  16. package/dist/errors.mjs.map +1 -0
  17. package/dist/index.cjs +5 -5
  18. package/dist/index.d.cts +5 -5
  19. package/dist/index.d.mts +5 -5
  20. package/dist/index.mjs +4 -5
  21. package/dist/types.d.cts +88 -2
  22. package/dist/types.d.cts.map +1 -0
  23. package/dist/types.d.mts +88 -2
  24. package/dist/types.d.mts.map +1 -0
  25. package/dist/types.mjs +1 -0
  26. package/dist/url.cjs +60 -3
  27. package/dist/url.cjs.map +1 -0
  28. package/dist/url.d.cts +16 -3
  29. package/dist/url.d.cts.map +1 -0
  30. package/dist/url.d.mts +16 -3
  31. package/dist/url.d.mts.map +1 -0
  32. package/dist/url.mjs +59 -3
  33. package/dist/url.mjs.map +1 -0
  34. package/package.json +9 -4
  35. package/dist/client-BkN5BGeJ.mjs +0 -58
  36. package/dist/client-BkN5BGeJ.mjs.map +0 -1
  37. package/dist/client-Dcgdc6HG.d.mts +0 -21
  38. package/dist/client-Dcgdc6HG.d.mts.map +0 -1
  39. package/dist/client-DyfZrCpo.d.cts +0 -21
  40. package/dist/client-DyfZrCpo.d.cts.map +0 -1
  41. package/dist/client-sZNHPYYg.cjs +0 -92
  42. package/dist/client-sZNHPYYg.cjs.map +0 -1
  43. package/dist/errors-6difZDw0.cjs +0 -68
  44. package/dist/errors-6difZDw0.cjs.map +0 -1
  45. package/dist/errors-Ba3cEHPO.mjs +0 -50
  46. package/dist/errors-Ba3cEHPO.mjs.map +0 -1
  47. package/dist/errors-ZllQgZ9Y.d.mts +0 -41
  48. package/dist/errors-ZllQgZ9Y.d.mts.map +0 -1
  49. package/dist/errors-xJ88z_5r.d.cts +0 -41
  50. package/dist/errors-xJ88z_5r.d.cts.map +0 -1
  51. package/dist/types-CoPG5az4.d.mts +0 -90
  52. package/dist/types-CoPG5az4.d.mts.map +0 -1
  53. package/dist/types-D5yAoaGX.d.cts +0 -90
  54. package/dist/types-D5yAoaGX.d.cts.map +0 -1
  55. package/dist/url-B2hKcFA3.d.mts +0 -20
  56. package/dist/url-B2hKcFA3.d.mts.map +0 -1
  57. package/dist/url-B8QqBrMt.mjs +0 -49
  58. package/dist/url-B8QqBrMt.mjs.map +0 -1
  59. package/dist/url-CVk3Cs_8.d.cts +0 -20
  60. package/dist/url-CVk3Cs_8.d.cts.map +0 -1
  61. package/dist/url-GT3jLGq-.cjs +0 -54
  62. package/dist/url-GT3jLGq-.cjs.map +0 -1
package/dist/types.d.cts CHANGED
@@ -1,2 +1,88 @@
1
- import { Attachment, EmailClient, EmailClientConfig, EmailOptions, EmailTemplate, PlainEmailOptions, SMTPConfig, SendOptions, SendResult, TemplateNames, TemplateProps, TemplatePropsFor, TemplateRecord } from "./types-D5yAoaGX.cjs";
2
- export { Attachment, EmailClient, EmailClientConfig, EmailOptions, EmailTemplate, PlainEmailOptions, SMTPConfig, SendOptions, SendResult, TemplateNames, TemplateProps, TemplatePropsFor, TemplateRecord };
1
+ import Mail from "nodemailer/lib/mailer";
2
+ import { ReactElement } from "react";
3
+ //#region src/types.d.ts
4
+ type Address = Mail.Address;
5
+ export interface EmailOptions {
6
+ from: string | Address;
7
+ to: string | string[] | Address[];
8
+ cc?: string | string[] | Address[];
9
+ bcc?: string | string[] | Address[];
10
+ subject: string;
11
+ replyTo?: string | Address;
12
+ attachments?: Attachment[];
13
+ }
14
+ export interface Attachment {
15
+ filename: string;
16
+ content?: string | Buffer;
17
+ path?: string;
18
+ contentType?: string;
19
+ cid?: string;
20
+ }
21
+ export interface PlainEmailOptions extends EmailOptions {
22
+ text?: string;
23
+ html?: string;
24
+ }
25
+ export type SendOptions = Omit<PlainEmailOptions, 'from'> & {
26
+ from?: string | Address;
27
+ };
28
+ export interface SMTPConfig {
29
+ host: string;
30
+ port: number;
31
+ secure?: boolean;
32
+ auth?: {
33
+ user: string;
34
+ pass: string;
35
+ };
36
+ tls?: {
37
+ rejectUnauthorized?: boolean;
38
+ servername?: string;
39
+ };
40
+ pool?: boolean;
41
+ maxConnections?: number;
42
+ maxMessages?: number;
43
+ rateLimit?: number;
44
+ logger?: boolean;
45
+ debug?: boolean;
46
+ }
47
+ export interface SendResult {
48
+ messageId: string;
49
+ accepted: string[];
50
+ rejected: string[];
51
+ /**
52
+ * The transport's reply, where it made one.
53
+ *
54
+ * Optional because nodemailer types it that way: not every transport answers
55
+ * with a response line, and the previous `string` was a promise this could
56
+ * not keep. Defaulting it to `''` would have kept the shape by inventing a
57
+ * reply that never arrived.
58
+ */
59
+ response?: string;
60
+ }
61
+ export type EmailTemplate<T = any> = (props: T) => ReactElement;
62
+ export type TemplateProps<T> = T extends EmailTemplate<infer P> ? P : never;
63
+ export type TemplateRecord = Record<string, EmailTemplate<any>>;
64
+ export type TemplateNames<T extends TemplateRecord> = keyof T;
65
+ export type TemplatePropsFor<T extends TemplateRecord, K extends TemplateNames<T>> = TemplateProps<T[K]>;
66
+ export interface EmailClientConfig<T extends TemplateRecord = TemplateRecord> {
67
+ smtp: SMTPConfig;
68
+ templates: T;
69
+ dkim?: {
70
+ domainName: string;
71
+ keySelector: string;
72
+ privateKey: string;
73
+ };
74
+ defaults: {
75
+ from?: string | Address;
76
+ replyTo?: string | Address;
77
+ };
78
+ }
79
+ export interface EmailClient<T extends TemplateRecord = TemplateRecord> {
80
+ send(options: SendOptions): Promise<SendResult>;
81
+ sendTemplate<K extends TemplateNames<T>>(template: K, options: SendOptions & {
82
+ props: TemplatePropsFor<T, K>;
83
+ }): Promise<SendResult>;
84
+ verify(): Promise<boolean>;
85
+ close(): Promise<void>;
86
+ }
87
+ //#endregion
88
+ //# sourceMappingURL=types.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.cts","names":[],"sources":["../src/types.ts"],"mappings":";;;KAMK,UAAU,KAAK;iBAEH;EAChB,eAAe;EACf,wBAAwB;EACxB,yBAAyB;EACzB,0BAA0B;EAC1B;EACA,mBAAmB;EACnB,cAAc;;iBAGE;EAChB;EACA,mBAAmB;EACnB;EACA;EACA;;iBAGgB,0BAA0B;EAC1C;EACA;;YAGW,cAAc,KAAK;EAC9B,gBAAgB;;iBAGA;EAChB;EACA;EACA;EACA;IACC;IACA;;EAED;IACC;IACA;;EAED;EACA;EACA;EACA;EACA;EACA;;iBAGgB;EAChB;EACA;EACA;;;;;;;;;EASA;;YAGW,cAAc,YAAY,OAAO,MAAM;YAGvC,cAAc,KAAK,UAAU,oBAAoB,KAAK;YAGtD,iBAAiB,eAAe;YAGhC,cAAc,UAAU,wBAAwB;YAGhD,iBACX,UAAU,gBACV,UAAU,cAAc,MACrB,cAAc,EAAE;iBAEH,kBAAkB,UAAU,iBAAiB;EAC7D,MAAM;EACN,WAAW;EACX;IACC;IACA;IACA;;EAED;IACC,gBAAgB;IAChB,mBAAmB;;;iBAIJ,YAAY,UAAU,iBAAiB;EACvD,KAAK,SAAS,cAAc,QAAQ;EACpC,aAAa,UAAU,cAAc,IACpC,UAAU,GACV,SAAS;IACR,OAAO,iBAAiB,GAAG;MAE1B,QAAQ;EACX,UAAU;EACV,SAAS"}
package/dist/types.d.mts CHANGED
@@ -1,2 +1,88 @@
1
- import { Attachment, EmailClient, EmailClientConfig, EmailOptions, EmailTemplate, PlainEmailOptions, SMTPConfig, SendOptions, SendResult, TemplateNames, TemplateProps, TemplatePropsFor, TemplateRecord } from "./types-CoPG5az4.mjs";
2
- export { Attachment, EmailClient, EmailClientConfig, EmailOptions, EmailTemplate, PlainEmailOptions, SMTPConfig, SendOptions, SendResult, TemplateNames, TemplateProps, TemplatePropsFor, TemplateRecord };
1
+ import Mail from "nodemailer/lib/mailer";
2
+ import { ReactElement } from "react";
3
+ //#region src/types.d.ts
4
+ type Address = Mail.Address;
5
+ export interface EmailOptions {
6
+ from: string | Address;
7
+ to: string | string[] | Address[];
8
+ cc?: string | string[] | Address[];
9
+ bcc?: string | string[] | Address[];
10
+ subject: string;
11
+ replyTo?: string | Address;
12
+ attachments?: Attachment[];
13
+ }
14
+ export interface Attachment {
15
+ filename: string;
16
+ content?: string | Buffer;
17
+ path?: string;
18
+ contentType?: string;
19
+ cid?: string;
20
+ }
21
+ export interface PlainEmailOptions extends EmailOptions {
22
+ text?: string;
23
+ html?: string;
24
+ }
25
+ export type SendOptions = Omit<PlainEmailOptions, 'from'> & {
26
+ from?: string | Address;
27
+ };
28
+ export interface SMTPConfig {
29
+ host: string;
30
+ port: number;
31
+ secure?: boolean;
32
+ auth?: {
33
+ user: string;
34
+ pass: string;
35
+ };
36
+ tls?: {
37
+ rejectUnauthorized?: boolean;
38
+ servername?: string;
39
+ };
40
+ pool?: boolean;
41
+ maxConnections?: number;
42
+ maxMessages?: number;
43
+ rateLimit?: number;
44
+ logger?: boolean;
45
+ debug?: boolean;
46
+ }
47
+ export interface SendResult {
48
+ messageId: string;
49
+ accepted: string[];
50
+ rejected: string[];
51
+ /**
52
+ * The transport's reply, where it made one.
53
+ *
54
+ * Optional because nodemailer types it that way: not every transport answers
55
+ * with a response line, and the previous `string` was a promise this could
56
+ * not keep. Defaulting it to `''` would have kept the shape by inventing a
57
+ * reply that never arrived.
58
+ */
59
+ response?: string;
60
+ }
61
+ export type EmailTemplate<T = any> = (props: T) => ReactElement;
62
+ export type TemplateProps<T> = T extends EmailTemplate<infer P> ? P : never;
63
+ export type TemplateRecord = Record<string, EmailTemplate<any>>;
64
+ export type TemplateNames<T extends TemplateRecord> = keyof T;
65
+ export type TemplatePropsFor<T extends TemplateRecord, K extends TemplateNames<T>> = TemplateProps<T[K]>;
66
+ export interface EmailClientConfig<T extends TemplateRecord = TemplateRecord> {
67
+ smtp: SMTPConfig;
68
+ templates: T;
69
+ dkim?: {
70
+ domainName: string;
71
+ keySelector: string;
72
+ privateKey: string;
73
+ };
74
+ defaults: {
75
+ from?: string | Address;
76
+ replyTo?: string | Address;
77
+ };
78
+ }
79
+ export interface EmailClient<T extends TemplateRecord = TemplateRecord> {
80
+ send(options: SendOptions): Promise<SendResult>;
81
+ sendTemplate<K extends TemplateNames<T>>(template: K, options: SendOptions & {
82
+ props: TemplatePropsFor<T, K>;
83
+ }): Promise<SendResult>;
84
+ verify(): Promise<boolean>;
85
+ close(): Promise<void>;
86
+ }
87
+ //#endregion
88
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;;KAMK,UAAU,KAAK;iBAEH;EAChB,eAAe;EACf,wBAAwB;EACxB,yBAAyB;EACzB,0BAA0B;EAC1B;EACA,mBAAmB;EACnB,cAAc;;iBAGE;EAChB;EACA,mBAAmB;EACnB;EACA;EACA;;iBAGgB,0BAA0B;EAC1C;EACA;;YAGW,cAAc,KAAK;EAC9B,gBAAgB;;iBAGA;EAChB;EACA;EACA;EACA;IACC;IACA;;EAED;IACC;IACA;;EAED;EACA;EACA;EACA;EACA;EACA;;iBAGgB;EAChB;EACA;EACA;;;;;;;;;EASA;;YAGW,cAAc,YAAY,OAAO,MAAM;YAGvC,cAAc,KAAK,UAAU,oBAAoB,KAAK;YAGtD,iBAAiB,eAAe;YAGhC,cAAc,UAAU,wBAAwB;YAGhD,iBACX,UAAU,gBACV,UAAU,cAAc,MACrB,cAAc,EAAE;iBAEH,kBAAkB,UAAU,iBAAiB;EAC7D,MAAM;EACN,WAAW;EACX;IACC;IACA;IACA;;EAED;IACC,gBAAgB;IAChB,mBAAmB;;;iBAIJ,YAAY,UAAU,iBAAiB;EACvD,KAAK,SAAS,cAAc,QAAQ;EACpC,aAAa,UAAU,cAAc,IACpC,UAAU,GACV,SAAS;IACR,OAAO,iBAAiB,GAAG;MAE1B,QAAQ;EACX,UAAU;EACV,SAAS"}
package/dist/types.mjs CHANGED
@@ -0,0 +1 @@
1
+ export {};
package/dist/url.cjs CHANGED
@@ -1,4 +1,61 @@
1
- require('./errors-6difZDw0.cjs');
2
- const require_url = require('./url-GT3jLGq-.cjs');
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_errors = require("./errors.cjs");
3
+ //#region src/url.ts
4
+ /**
5
+ * `smtp://` — the one URL shape an email construct ever provides.
6
+ *
7
+ * Every provider is reached the same way: Mailpit locally, SES through its SMTP
8
+ * interface, Resend and Postmark through theirs. So there is no scheme-keyed
9
+ * driver registry here as there is in `@geekmidas/storage` — one transport, one
10
+ * parser, and the client never branches on who is delivering the mail.
11
+ *
12
+ * What varies between providers is confined to the URL: host, port, and
13
+ * credentials. That is what lets the same handler source run against Mailpit and
14
+ * against SES without a conditional.
15
+ */
16
+ /**
17
+ * The port to use when the URL names none.
18
+ *
19
+ * 587 is submission-with-STARTTLS, which is what SES, Resend, and Postmark all
20
+ * document; 465 is implicit TLS, which is what `smtps://` means.
21
+ */
22
+ const DEFAULT_PORT = {
23
+ "smtp:": 587,
24
+ "smtps:": 465
25
+ };
26
+ /**
27
+ * Build an SMTP transport config from a `<NAME>_URL`.
28
+ *
29
+ * `smtps://` sets `secure`, which is nodemailer's flag for implicit TLS on
30
+ * connect rather than STARTTLS after `EHLO` — the distinction 465 and 587 carry,
31
+ * and the reason the scheme is worth having rather than inferring from the port.
32
+ *
33
+ * @example parseEmailUrl('smtp://mailpit:1025')
34
+ * @example parseEmailUrl('smtps://AKIA…:…@email-smtp.eu-west-1.amazonaws.com:465')
35
+ * @example parseEmailUrl('smtp://resend:re_123@smtp.resend.com:587')
36
+ */
37
+ function parseEmailUrl(url) {
38
+ let parsed;
39
+ try {
40
+ parsed = new URL(url);
41
+ } catch {
42
+ throw new require_errors.MalformedEmailUrl(url);
43
+ }
44
+ if (parsed.protocol !== "smtp:" && parsed.protocol !== "smtps:") throw new require_errors.UnsupportedEmailScheme(url, parsed.protocol);
45
+ const secure = parsed.protocol === "smtps:";
46
+ const config = {
47
+ host: parsed.hostname,
48
+ port: parsed.port ? Number(parsed.port) : DEFAULT_PORT[parsed.protocol],
49
+ secure
50
+ };
51
+ if (parsed.username) config.auth = {
52
+ user: decodeURIComponent(parsed.username),
53
+ pass: decodeURIComponent(parsed.password)
54
+ };
55
+ if (parsed.searchParams.get("insecure") === "true") config.tls = { rejectUnauthorized: false };
56
+ return config;
57
+ }
58
+ //#endregion
59
+ exports.parseEmailUrl = parseEmailUrl;
3
60
 
4
- exports.parseEmailUrl = require_url.parseEmailUrl;
61
+ //# sourceMappingURL=url.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.cjs","names":["MalformedEmailUrl","UnsupportedEmailScheme"],"sources":["../src/url.ts"],"sourcesContent":["/**\n * `smtp://` — the one URL shape an email construct ever provides.\n *\n * Every provider is reached the same way: Mailpit locally, SES through its SMTP\n * interface, Resend and Postmark through theirs. So there is no scheme-keyed\n * driver registry here as there is in `@geekmidas/storage` — one transport, one\n * parser, and the client never branches on who is delivering the mail.\n *\n * What varies between providers is confined to the URL: host, port, and\n * credentials. That is what lets the same handler source run against Mailpit and\n * against SES without a conditional.\n */\n\nimport { MalformedEmailUrl, UnsupportedEmailScheme } from './errors';\nimport type { SMTPConfig } from './types';\n\n/**\n * The port to use when the URL names none.\n *\n * 587 is submission-with-STARTTLS, which is what SES, Resend, and Postmark all\n * document; 465 is implicit TLS, which is what `smtps://` means.\n */\nconst DEFAULT_PORT = { 'smtp:': 587, 'smtps:': 465 } as const;\n\n/**\n * Build an SMTP transport config from a `<NAME>_URL`.\n *\n * `smtps://` sets `secure`, which is nodemailer's flag for implicit TLS on\n * connect rather than STARTTLS after `EHLO` — the distinction 465 and 587 carry,\n * and the reason the scheme is worth having rather than inferring from the port.\n *\n * @example parseEmailUrl('smtp://mailpit:1025')\n * @example parseEmailUrl('smtps://AKIA…:…@email-smtp.eu-west-1.amazonaws.com:465')\n * @example parseEmailUrl('smtp://resend:re_123@smtp.resend.com:587')\n */\nexport function parseEmailUrl(url: string): SMTPConfig {\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(url);\n\t} catch {\n\t\tthrow new MalformedEmailUrl(url);\n\t}\n\n\tif (parsed.protocol !== 'smtp:' && parsed.protocol !== 'smtps:') {\n\t\tthrow new UnsupportedEmailScheme(url, parsed.protocol);\n\t}\n\n\tconst secure = parsed.protocol === 'smtps:';\n\tconst config: SMTPConfig = {\n\t\thost: parsed.hostname,\n\t\tport: parsed.port ? Number(parsed.port) : DEFAULT_PORT[parsed.protocol],\n\t\tsecure,\n\t};\n\n\t// Mailpit accepts mail unauthenticated, so credentials are optional rather\n\t// than required — omitting `auth` entirely is what nodemailer expects, where\n\t// empty strings would be sent as a real (failing) AUTH exchange.\n\tif (parsed.username) {\n\t\tconfig.auth = {\n\t\t\tuser: decodeURIComponent(parsed.username),\n\t\t\tpass: decodeURIComponent(parsed.password),\n\t\t};\n\t}\n\n\t// Local containers present a self-signed certificate. Rather than have every\n\t// project set this, read it off the URL — the adapter that knows it is\n\t// pointing at a container is the one that can say so.\n\tif (parsed.searchParams.get('insecure') === 'true') {\n\t\tconfig.tls = { rejectUnauthorized: false };\n\t}\n\n\treturn config;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsBA,MAAM,eAAe;CAAE,SAAS;CAAK,UAAU;AAAI;;;;;;;;;;;;AAanD,SAAgB,cAAc,KAAyB;CACtD,IAAI;CACJ,IAAI;EACH,SAAS,IAAI,IAAI,GAAG;CACrB,QAAQ;EACP,MAAM,IAAIA,eAAAA,kBAAkB,GAAG;CAChC;CAEA,IAAI,OAAO,aAAa,WAAW,OAAO,aAAa,UACtD,MAAM,IAAIC,eAAAA,uBAAuB,KAAK,OAAO,QAAQ;CAGtD,MAAM,SAAS,OAAO,aAAa;CACnC,MAAM,SAAqB;EAC1B,MAAM,OAAO;EACb,MAAM,OAAO,OAAO,OAAO,OAAO,IAAI,IAAI,aAAa,OAAO;EAC9D;CACD;CAKA,IAAI,OAAO,UACV,OAAO,OAAO;EACb,MAAM,mBAAmB,OAAO,QAAQ;EACxC,MAAM,mBAAmB,OAAO,QAAQ;CACzC;CAMD,IAAI,OAAO,aAAa,IAAI,UAAU,MAAM,QAC3C,OAAO,MAAM,EAAE,oBAAoB,MAAM;CAG1C,OAAO;AACR"}
package/dist/url.d.cts CHANGED
@@ -1,3 +1,16 @@
1
- import "./types-D5yAoaGX.cjs";
2
- import { parseEmailUrl } from "./url-CVk3Cs_8.cjs";
3
- export { parseEmailUrl };
1
+ import { SMTPConfig } from "./types.cjs";
2
+ //#region src/url.d.ts
3
+ /**
4
+ * Build an SMTP transport config from a `<NAME>_URL`.
5
+ *
6
+ * `smtps://` sets `secure`, which is nodemailer's flag for implicit TLS on
7
+ * connect rather than STARTTLS after `EHLO` — the distinction 465 and 587 carry,
8
+ * and the reason the scheme is worth having rather than inferring from the port.
9
+ *
10
+ * @example parseEmailUrl('smtp://mailpit:1025')
11
+ * @example parseEmailUrl('smtps://AKIA…:…@email-smtp.eu-west-1.amazonaws.com:465')
12
+ * @example parseEmailUrl('smtp://resend:re_123@smtp.resend.com:587')
13
+ */
14
+ export declare function parseEmailUrl(url: string): SMTPConfig;
15
+ //#endregion
16
+ //# sourceMappingURL=url.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.d.cts","names":[],"sources":["../src/url.ts"],"mappings":";;;;;;;;;;;;;wBAmCgB,cAAc,cAAc"}
package/dist/url.d.mts CHANGED
@@ -1,3 +1,16 @@
1
- import "./types-CoPG5az4.mjs";
2
- import { parseEmailUrl } from "./url-B2hKcFA3.mjs";
3
- export { parseEmailUrl };
1
+ import { SMTPConfig } from "./types.mjs";
2
+ //#region src/url.d.ts
3
+ /**
4
+ * Build an SMTP transport config from a `<NAME>_URL`.
5
+ *
6
+ * `smtps://` sets `secure`, which is nodemailer's flag for implicit TLS on
7
+ * connect rather than STARTTLS after `EHLO` — the distinction 465 and 587 carry,
8
+ * and the reason the scheme is worth having rather than inferring from the port.
9
+ *
10
+ * @example parseEmailUrl('smtp://mailpit:1025')
11
+ * @example parseEmailUrl('smtps://AKIA…:…@email-smtp.eu-west-1.amazonaws.com:465')
12
+ * @example parseEmailUrl('smtp://resend:re_123@smtp.resend.com:587')
13
+ */
14
+ export declare function parseEmailUrl(url: string): SMTPConfig;
15
+ //#endregion
16
+ //# sourceMappingURL=url.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.d.mts","names":[],"sources":["../src/url.ts"],"mappings":";;;;;;;;;;;;;wBAmCgB,cAAc,cAAc"}
package/dist/url.mjs CHANGED
@@ -1,4 +1,60 @@
1
- import "./errors-Ba3cEHPO.mjs";
2
- import { parseEmailUrl } from "./url-B8QqBrMt.mjs";
1
+ import { MalformedEmailUrl, UnsupportedEmailScheme } from "./errors.mjs";
2
+ //#region src/url.ts
3
+ /**
4
+ * `smtp://` — the one URL shape an email construct ever provides.
5
+ *
6
+ * Every provider is reached the same way: Mailpit locally, SES through its SMTP
7
+ * interface, Resend and Postmark through theirs. So there is no scheme-keyed
8
+ * driver registry here as there is in `@geekmidas/storage` — one transport, one
9
+ * parser, and the client never branches on who is delivering the mail.
10
+ *
11
+ * What varies between providers is confined to the URL: host, port, and
12
+ * credentials. That is what lets the same handler source run against Mailpit and
13
+ * against SES without a conditional.
14
+ */
15
+ /**
16
+ * The port to use when the URL names none.
17
+ *
18
+ * 587 is submission-with-STARTTLS, which is what SES, Resend, and Postmark all
19
+ * document; 465 is implicit TLS, which is what `smtps://` means.
20
+ */
21
+ const DEFAULT_PORT = {
22
+ "smtp:": 587,
23
+ "smtps:": 465
24
+ };
25
+ /**
26
+ * Build an SMTP transport config from a `<NAME>_URL`.
27
+ *
28
+ * `smtps://` sets `secure`, which is nodemailer's flag for implicit TLS on
29
+ * connect rather than STARTTLS after `EHLO` — the distinction 465 and 587 carry,
30
+ * and the reason the scheme is worth having rather than inferring from the port.
31
+ *
32
+ * @example parseEmailUrl('smtp://mailpit:1025')
33
+ * @example parseEmailUrl('smtps://AKIA…:…@email-smtp.eu-west-1.amazonaws.com:465')
34
+ * @example parseEmailUrl('smtp://resend:re_123@smtp.resend.com:587')
35
+ */
36
+ function parseEmailUrl(url) {
37
+ let parsed;
38
+ try {
39
+ parsed = new URL(url);
40
+ } catch {
41
+ throw new MalformedEmailUrl(url);
42
+ }
43
+ if (parsed.protocol !== "smtp:" && parsed.protocol !== "smtps:") throw new UnsupportedEmailScheme(url, parsed.protocol);
44
+ const secure = parsed.protocol === "smtps:";
45
+ const config = {
46
+ host: parsed.hostname,
47
+ port: parsed.port ? Number(parsed.port) : DEFAULT_PORT[parsed.protocol],
48
+ secure
49
+ };
50
+ if (parsed.username) config.auth = {
51
+ user: decodeURIComponent(parsed.username),
52
+ pass: decodeURIComponent(parsed.password)
53
+ };
54
+ if (parsed.searchParams.get("insecure") === "true") config.tls = { rejectUnauthorized: false };
55
+ return config;
56
+ }
57
+ //#endregion
58
+ export { parseEmailUrl };
3
59
 
4
- export { parseEmailUrl };
60
+ //# sourceMappingURL=url.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.mjs","names":[],"sources":["../src/url.ts"],"sourcesContent":["/**\n * `smtp://` — the one URL shape an email construct ever provides.\n *\n * Every provider is reached the same way: Mailpit locally, SES through its SMTP\n * interface, Resend and Postmark through theirs. So there is no scheme-keyed\n * driver registry here as there is in `@geekmidas/storage` — one transport, one\n * parser, and the client never branches on who is delivering the mail.\n *\n * What varies between providers is confined to the URL: host, port, and\n * credentials. That is what lets the same handler source run against Mailpit and\n * against SES without a conditional.\n */\n\nimport { MalformedEmailUrl, UnsupportedEmailScheme } from './errors';\nimport type { SMTPConfig } from './types';\n\n/**\n * The port to use when the URL names none.\n *\n * 587 is submission-with-STARTTLS, which is what SES, Resend, and Postmark all\n * document; 465 is implicit TLS, which is what `smtps://` means.\n */\nconst DEFAULT_PORT = { 'smtp:': 587, 'smtps:': 465 } as const;\n\n/**\n * Build an SMTP transport config from a `<NAME>_URL`.\n *\n * `smtps://` sets `secure`, which is nodemailer's flag for implicit TLS on\n * connect rather than STARTTLS after `EHLO` — the distinction 465 and 587 carry,\n * and the reason the scheme is worth having rather than inferring from the port.\n *\n * @example parseEmailUrl('smtp://mailpit:1025')\n * @example parseEmailUrl('smtps://AKIA…:…@email-smtp.eu-west-1.amazonaws.com:465')\n * @example parseEmailUrl('smtp://resend:re_123@smtp.resend.com:587')\n */\nexport function parseEmailUrl(url: string): SMTPConfig {\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(url);\n\t} catch {\n\t\tthrow new MalformedEmailUrl(url);\n\t}\n\n\tif (parsed.protocol !== 'smtp:' && parsed.protocol !== 'smtps:') {\n\t\tthrow new UnsupportedEmailScheme(url, parsed.protocol);\n\t}\n\n\tconst secure = parsed.protocol === 'smtps:';\n\tconst config: SMTPConfig = {\n\t\thost: parsed.hostname,\n\t\tport: parsed.port ? Number(parsed.port) : DEFAULT_PORT[parsed.protocol],\n\t\tsecure,\n\t};\n\n\t// Mailpit accepts mail unauthenticated, so credentials are optional rather\n\t// than required — omitting `auth` entirely is what nodemailer expects, where\n\t// empty strings would be sent as a real (failing) AUTH exchange.\n\tif (parsed.username) {\n\t\tconfig.auth = {\n\t\t\tuser: decodeURIComponent(parsed.username),\n\t\t\tpass: decodeURIComponent(parsed.password),\n\t\t};\n\t}\n\n\t// Local containers present a self-signed certificate. Rather than have every\n\t// project set this, read it off the URL — the adapter that knows it is\n\t// pointing at a container is the one that can say so.\n\tif (parsed.searchParams.get('insecure') === 'true') {\n\t\tconfig.tls = { rejectUnauthorized: false };\n\t}\n\n\treturn config;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAsBA,MAAM,eAAe;CAAE,SAAS;CAAK,UAAU;AAAI;;;;;;;;;;;;AAanD,SAAgB,cAAc,KAAyB;CACtD,IAAI;CACJ,IAAI;EACH,SAAS,IAAI,IAAI,GAAG;CACrB,QAAQ;EACP,MAAM,IAAI,kBAAkB,GAAG;CAChC;CAEA,IAAI,OAAO,aAAa,WAAW,OAAO,aAAa,UACtD,MAAM,IAAI,uBAAuB,KAAK,OAAO,QAAQ;CAGtD,MAAM,SAAS,OAAO,aAAa;CACnC,MAAM,SAAqB;EAC1B,MAAM,OAAO;EACb,MAAM,OAAO,OAAO,OAAO,OAAO,IAAI,IAAI,aAAa,OAAO;EAC9D;CACD;CAKA,IAAI,OAAO,UACV,OAAO,OAAO;EACb,MAAM,mBAAmB,OAAO,QAAQ;EACxC,MAAM,mBAAmB,OAAO,QAAQ;CACzC;CAMD,IAAI,OAAO,aAAa,IAAI,UAAU,MAAM,QAC3C,OAAO,MAAM,EAAE,oBAAoB,MAAM;CAG1C,OAAO;AACR"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/emailkit",
3
- "version": "10.0.0-alpha.9",
3
+ "version": "10.0.0-alpha.91",
4
4
  "description": "Type-safe email client with SMTP support and React templates",
5
5
  "private": false,
6
6
  "type": "module",
@@ -38,13 +38,15 @@
38
38
  "access": "public"
39
39
  },
40
40
  "dependencies": {
41
+ "@opentelemetry/api": "~1.9.1",
41
42
  "nodemailer": "~10.0.10"
42
43
  },
43
44
  "devDependencies": {
45
+ "@opentelemetry/sdk-trace-node": "~2.11.0",
44
46
  "@types/nodemailer": "~8.0.2",
45
- "@types/react": "~19.1.8",
46
- "@types/react-dom": "~19.1.6",
47
- "typescript": "~5.8.2"
47
+ "@types/react": "~19.3.0",
48
+ "@types/react-dom": "~19.3.0",
49
+ "typescript": "~7.0.2"
48
50
  },
49
51
  "peerDependencies": {
50
52
  "react": ">=18.0.0",
@@ -53,5 +55,8 @@
53
55
  },
54
56
  "engines": {
55
57
  "node": ">=22.0.0"
58
+ },
59
+ "scripts": {
60
+ "build": "tsdown --config ../../tsdown.config.ts --filter ."
56
61
  }
57
62
  }
@@ -1,58 +0,0 @@
1
- import { render } from "@react-email/components";
2
- import nodemailer from "nodemailer";
3
-
4
- //#region src/client.ts
5
- var SMTPClient = class {
6
- transporter;
7
- config;
8
- constructor(config) {
9
- this.config = config;
10
- this.transporter = nodemailer.createTransport(config.smtp);
11
- }
12
- async send(options) {
13
- const mailOptions = {
14
- ...this.config.defaults,
15
- ...options
16
- };
17
- if (!mailOptions.from) throw new Error("The \"from\" field is required in email options or defaults");
18
- if (!mailOptions.text && !mailOptions.html) throw new Error("Either text or html content must be provided");
19
- const info = await this.transporter.sendMail(mailOptions);
20
- return {
21
- messageId: info.messageId,
22
- accepted: info.accepted || [],
23
- rejected: info.rejected || [],
24
- response: info.response
25
- };
26
- }
27
- async sendTemplate(template, options) {
28
- const Component = this.config.templates[template];
29
- if (!Component) throw new Error(`Template "${String(template)}" not found`);
30
- const element = Component(options.props);
31
- const html = await render(element);
32
- return this.send({
33
- ...options,
34
- html
35
- });
36
- }
37
- async verify() {
38
- try {
39
- await this.transporter.verify();
40
- return true;
41
- } catch {
42
- return false;
43
- }
44
- }
45
- async close() {
46
- this.transporter.close();
47
- }
48
- getTemplateNames() {
49
- return Object.keys(this.config.templates);
50
- }
51
- };
52
- function createEmailClient(config) {
53
- return new SMTPClient(config);
54
- }
55
-
56
- //#endregion
57
- export { SMTPClient, createEmailClient };
58
- //# sourceMappingURL=client-BkN5BGeJ.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-BkN5BGeJ.mjs","names":["config: EmailClientConfig<T>","options: SendOptions","template: K","options: Omit<SendOptions, 'template'> & {\n\t\t\tprops: TemplatePropsFor<T, K>;\n\t\t}"],"sources":["../src/client.ts"],"sourcesContent":["import { render } from '@react-email/components';\nimport nodemailer, { type Transporter } from 'nodemailer';\nimport type {\n\tEmailClient,\n\tEmailClientConfig,\n\tSendOptions,\n\tSendResult,\n\tTemplateNames,\n\tTemplatePropsFor,\n\tTemplateRecord,\n} from './types';\n\nexport class SMTPClient<T extends TemplateRecord> implements EmailClient<T> {\n\tprivate transporter: Transporter;\n\tprivate config: EmailClientConfig<T>;\n\n\tconstructor(config: EmailClientConfig<T>) {\n\t\tthis.config = config;\n\t\tthis.transporter = nodemailer.createTransport(config.smtp as any);\n\t}\n\n\tasync send(options: SendOptions): Promise<SendResult> {\n\t\tconst mailOptions = {\n\t\t\t...this.config.defaults,\n\t\t\t...options,\n\t\t};\n\n\t\tif (!mailOptions.from) {\n\t\t\tthrow new Error(\n\t\t\t\t'The \"from\" field is required in email options or defaults',\n\t\t\t);\n\t\t}\n\n\t\tif (!mailOptions.text && !mailOptions.html) {\n\t\t\tthrow new Error('Either text or html content must be provided');\n\t\t}\n\n\t\tconst info = await this.transporter.sendMail(mailOptions);\n\n\t\treturn {\n\t\t\tmessageId: info.messageId,\n\t\t\taccepted: info.accepted || [],\n\t\t\trejected: info.rejected || [],\n\t\t\tresponse: info.response,\n\t\t};\n\t}\n\n\tasync sendTemplate<K extends TemplateNames<T>>(\n\t\ttemplate: K,\n\t\toptions: Omit<SendOptions, 'template'> & {\n\t\t\tprops: TemplatePropsFor<T, K>;\n\t\t},\n\t): Promise<SendResult> {\n\t\tconst Component = this.config.templates[template];\n\t\tif (!Component) {\n\t\t\tthrow new Error(`Template \"${String(template)}\" not found`);\n\t\t}\n\n\t\tconst element = Component(options.props);\n\t\tconst html = await render(element);\n\n\t\treturn this.send({\n\t\t\t...options,\n\t\t\thtml,\n\t\t});\n\t}\n\n\tasync verify(): Promise<boolean> {\n\t\ttry {\n\t\t\tawait this.transporter.verify();\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.transporter.close();\n\t}\n\n\tgetTemplateNames(): TemplateNames<T>[] {\n\t\treturn Object.keys(this.config.templates) as TemplateNames<T>[];\n\t}\n}\n\nexport function createEmailClient<T extends TemplateRecord>(\n\tconfig: EmailClientConfig<T>,\n): SMTPClient<T> {\n\treturn new SMTPClient(config);\n}\n"],"mappings":";;;;AAYA,IAAa,aAAb,MAA4E;CAC3E,AAAQ;CACR,AAAQ;CAER,YAAYA,QAA8B;AACzC,OAAK,SAAS;AACd,OAAK,cAAc,WAAW,gBAAgB,OAAO,KAAY;CACjE;CAED,MAAM,KAAKC,SAA2C;EACrD,MAAM,cAAc;GACnB,GAAG,KAAK,OAAO;GACf,GAAG;EACH;AAED,OAAK,YAAY,KAChB,OAAM,IAAI,MACT;AAIF,OAAK,YAAY,SAAS,YAAY,KACrC,OAAM,IAAI,MAAM;EAGjB,MAAM,OAAO,MAAM,KAAK,YAAY,SAAS,YAAY;AAEzD,SAAO;GACN,WAAW,KAAK;GAChB,UAAU,KAAK,YAAY,CAAE;GAC7B,UAAU,KAAK,YAAY,CAAE;GAC7B,UAAU,KAAK;EACf;CACD;CAED,MAAM,aACLC,UACAC,SAGsB;EACtB,MAAM,YAAY,KAAK,OAAO,UAAU;AACxC,OAAK,UACJ,OAAM,IAAI,OAAO,YAAY,OAAO,SAAS,CAAC;EAG/C,MAAM,UAAU,UAAU,QAAQ,MAAM;EACxC,MAAM,OAAO,MAAM,OAAO,QAAQ;AAElC,SAAO,KAAK,KAAK;GAChB,GAAG;GACH;EACA,EAAC;CACF;CAED,MAAM,SAA2B;AAChC,MAAI;AACH,SAAM,KAAK,YAAY,QAAQ;AAC/B,UAAO;EACP,QAAO;AACP,UAAO;EACP;CACD;CAED,MAAM,QAAuB;AAC5B,OAAK,YAAY,OAAO;CACxB;CAED,mBAAuC;AACtC,SAAO,OAAO,KAAK,KAAK,OAAO,UAAU;CACzC;AACD;AAED,SAAgB,kBACfH,QACgB;AAChB,QAAO,IAAI,WAAW;AACtB"}
@@ -1,21 +0,0 @@
1
- import { EmailClient, EmailClientConfig, SendOptions, SendResult, TemplateNames, TemplatePropsFor, TemplateRecord } from "./types-CoPG5az4.mjs";
2
-
3
- //#region src/client.d.ts
4
- declare class SMTPClient<T extends TemplateRecord> implements EmailClient<T> {
5
- private transporter;
6
- private config;
7
- constructor(config: EmailClientConfig<T>);
8
- send(options: SendOptions): Promise<SendResult>;
9
- sendTemplate<K extends TemplateNames<T>>(template: K, options: Omit<SendOptions, 'template'> & {
10
- props: TemplatePropsFor<T, K>;
11
- }): Promise<SendResult>;
12
- verify(): Promise<boolean>;
13
- close(): Promise<void>;
14
- getTemplateNames(): TemplateNames<T>[];
15
- }
16
- declare function createEmailClient<T extends TemplateRecord>(config: EmailClientConfig<T>): SMTPClient<T>;
17
- //# sourceMappingURL=client.d.ts.map
18
-
19
- //#endregion
20
- export { SMTPClient, createEmailClient };
21
- //# sourceMappingURL=client-Dcgdc6HG.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-Dcgdc6HG.d.mts","names":[],"sources":["../src/client.ts"],"sourcesContent":[],"mappings":";;;cAYa,qBAAqB,2BAA2B,YAAY;;EAA5D,QAAA,MAAU;EAAA,WAAA,CAAA,MAAA,EAIF,iBAJE,CAIgB,CAJhB,CAAA;EAAA,IAAW,CAAA,OAAA,EASb,WATa,CAAA,EASC,OATD,CASS,UATT,CAAA;EAAc,YAAyB,CAAA,UAmC3C,aAnC2C,CAmC7B,CAnC6B,CAAA,CAAA,CAAA,QAAA,EAoC7D,CApC6D,EAAA,OAAA,EAqC9D,IArC8D,CAqCzD,WArCyD,EAAA,UAAA,CAAA,GAAA;IAIlC,KAAA,EAkC7B,gBAlC6B,CAkCZ,CAlCY,EAkCT,CAlCS,CAAA;EAAC,CAAA,CAAA,EAoCpC,OApCiB,CAoCT,UApCS,CAAA;EAAiB,MAKjB,CAAA,CAAA,EA8CJ,OA9CI,CAAA,OAAA,CAAA;EAAW,KAAW,CAAA,CAAA,EAuD3B,OAvD2B,CAAA,IAAA,CAAA;EAAU,gBAAlB,CAAA,CAAA,EA2Dd,aA3Dc,CA2DA,CA3DA,CAAA,EAAA;;AA0BL,iBAsCd,iBAtCc,CAAA,UAsCc,cAtCd,CAAA,CAAA,MAAA,EAuCrB,iBAvCqB,CAuCH,CAvCG,CAAA,CAAA,EAwC3B,UAxC2B,CAwChB,CAxCgB,CAAA"}
@@ -1,21 +0,0 @@
1
- import { EmailClient, EmailClientConfig, SendOptions, SendResult, TemplateNames, TemplatePropsFor, TemplateRecord } from "./types-D5yAoaGX.cjs";
2
-
3
- //#region src/client.d.ts
4
- declare class SMTPClient<T extends TemplateRecord> implements EmailClient<T> {
5
- private transporter;
6
- private config;
7
- constructor(config: EmailClientConfig<T>);
8
- send(options: SendOptions): Promise<SendResult>;
9
- sendTemplate<K extends TemplateNames<T>>(template: K, options: Omit<SendOptions, 'template'> & {
10
- props: TemplatePropsFor<T, K>;
11
- }): Promise<SendResult>;
12
- verify(): Promise<boolean>;
13
- close(): Promise<void>;
14
- getTemplateNames(): TemplateNames<T>[];
15
- }
16
- declare function createEmailClient<T extends TemplateRecord>(config: EmailClientConfig<T>): SMTPClient<T>;
17
- //# sourceMappingURL=client.d.ts.map
18
-
19
- //#endregion
20
- export { SMTPClient, createEmailClient };
21
- //# sourceMappingURL=client-DyfZrCpo.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-DyfZrCpo.d.cts","names":[],"sources":["../src/client.ts"],"sourcesContent":[],"mappings":";;;cAYa,qBAAqB,2BAA2B,YAAY;;EAA5D,QAAA,MAAU;EAAA,WAAA,CAAA,MAAA,EAIF,iBAJE,CAIgB,CAJhB,CAAA;EAAA,IAAW,CAAA,OAAA,EASb,WATa,CAAA,EASC,OATD,CASS,UATT,CAAA;EAAc,YAAyB,CAAA,UAmC3C,aAnC2C,CAmC7B,CAnC6B,CAAA,CAAA,CAAA,QAAA,EAoC7D,CApC6D,EAAA,OAAA,EAqC9D,IArC8D,CAqCzD,WArCyD,EAAA,UAAA,CAAA,GAAA;IAIlC,KAAA,EAkC7B,gBAlC6B,CAkCZ,CAlCY,EAkCT,CAlCS,CAAA;EAAC,CAAA,CAAA,EAoCpC,OApCiB,CAoCT,UApCS,CAAA;EAAiB,MAKjB,CAAA,CAAA,EA8CJ,OA9CI,CAAA,OAAA,CAAA;EAAW,KAAW,CAAA,CAAA,EAuD3B,OAvD2B,CAAA,IAAA,CAAA;EAAU,gBAAlB,CAAA,CAAA,EA2Dd,aA3Dc,CA2DA,CA3DA,CAAA,EAAA;;AA0BL,iBAsCd,iBAtCc,CAAA,UAsCc,cAtCd,CAAA,CAAA,MAAA,EAuCrB,iBAvCqB,CAuCH,CAvCG,CAAA,CAAA,EAwC3B,UAxC2B,CAwChB,CAxCgB,CAAA"}
@@ -1,92 +0,0 @@
1
- //#region rolldown:runtime
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
- key = keys[i];
11
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
- get: ((k) => from[k]).bind(null, key),
13
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
- });
15
- }
16
- return to;
17
- };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
- value: mod,
20
- enumerable: true
21
- }) : target, mod));
22
-
23
- //#endregion
24
- const __react_email_components = __toESM(require("@react-email/components"));
25
- const nodemailer = __toESM(require("nodemailer"));
26
-
27
- //#region src/client.ts
28
- var SMTPClient = class {
29
- transporter;
30
- config;
31
- constructor(config) {
32
- this.config = config;
33
- this.transporter = nodemailer.default.createTransport(config.smtp);
34
- }
35
- async send(options) {
36
- const mailOptions = {
37
- ...this.config.defaults,
38
- ...options
39
- };
40
- if (!mailOptions.from) throw new Error("The \"from\" field is required in email options or defaults");
41
- if (!mailOptions.text && !mailOptions.html) throw new Error("Either text or html content must be provided");
42
- const info = await this.transporter.sendMail(mailOptions);
43
- return {
44
- messageId: info.messageId,
45
- accepted: info.accepted || [],
46
- rejected: info.rejected || [],
47
- response: info.response
48
- };
49
- }
50
- async sendTemplate(template, options) {
51
- const Component = this.config.templates[template];
52
- if (!Component) throw new Error(`Template "${String(template)}" not found`);
53
- const element = Component(options.props);
54
- const html = await (0, __react_email_components.render)(element);
55
- return this.send({
56
- ...options,
57
- html
58
- });
59
- }
60
- async verify() {
61
- try {
62
- await this.transporter.verify();
63
- return true;
64
- } catch {
65
- return false;
66
- }
67
- }
68
- async close() {
69
- this.transporter.close();
70
- }
71
- getTemplateNames() {
72
- return Object.keys(this.config.templates);
73
- }
74
- };
75
- function createEmailClient(config) {
76
- return new SMTPClient(config);
77
- }
78
-
79
- //#endregion
80
- Object.defineProperty(exports, 'SMTPClient', {
81
- enumerable: true,
82
- get: function () {
83
- return SMTPClient;
84
- }
85
- });
86
- Object.defineProperty(exports, 'createEmailClient', {
87
- enumerable: true,
88
- get: function () {
89
- return createEmailClient;
90
- }
91
- });
92
- //# sourceMappingURL=client-sZNHPYYg.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-sZNHPYYg.cjs","names":["config: EmailClientConfig<T>","options: SendOptions","template: K","options: Omit<SendOptions, 'template'> & {\n\t\t\tprops: TemplatePropsFor<T, K>;\n\t\t}"],"sources":["../src/client.ts"],"sourcesContent":["import { render } from '@react-email/components';\nimport nodemailer, { type Transporter } from 'nodemailer';\nimport type {\n\tEmailClient,\n\tEmailClientConfig,\n\tSendOptions,\n\tSendResult,\n\tTemplateNames,\n\tTemplatePropsFor,\n\tTemplateRecord,\n} from './types';\n\nexport class SMTPClient<T extends TemplateRecord> implements EmailClient<T> {\n\tprivate transporter: Transporter;\n\tprivate config: EmailClientConfig<T>;\n\n\tconstructor(config: EmailClientConfig<T>) {\n\t\tthis.config = config;\n\t\tthis.transporter = nodemailer.createTransport(config.smtp as any);\n\t}\n\n\tasync send(options: SendOptions): Promise<SendResult> {\n\t\tconst mailOptions = {\n\t\t\t...this.config.defaults,\n\t\t\t...options,\n\t\t};\n\n\t\tif (!mailOptions.from) {\n\t\t\tthrow new Error(\n\t\t\t\t'The \"from\" field is required in email options or defaults',\n\t\t\t);\n\t\t}\n\n\t\tif (!mailOptions.text && !mailOptions.html) {\n\t\t\tthrow new Error('Either text or html content must be provided');\n\t\t}\n\n\t\tconst info = await this.transporter.sendMail(mailOptions);\n\n\t\treturn {\n\t\t\tmessageId: info.messageId,\n\t\t\taccepted: info.accepted || [],\n\t\t\trejected: info.rejected || [],\n\t\t\tresponse: info.response,\n\t\t};\n\t}\n\n\tasync sendTemplate<K extends TemplateNames<T>>(\n\t\ttemplate: K,\n\t\toptions: Omit<SendOptions, 'template'> & {\n\t\t\tprops: TemplatePropsFor<T, K>;\n\t\t},\n\t): Promise<SendResult> {\n\t\tconst Component = this.config.templates[template];\n\t\tif (!Component) {\n\t\t\tthrow new Error(`Template \"${String(template)}\" not found`);\n\t\t}\n\n\t\tconst element = Component(options.props);\n\t\tconst html = await render(element);\n\n\t\treturn this.send({\n\t\t\t...options,\n\t\t\thtml,\n\t\t});\n\t}\n\n\tasync verify(): Promise<boolean> {\n\t\ttry {\n\t\t\tawait this.transporter.verify();\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\tthis.transporter.close();\n\t}\n\n\tgetTemplateNames(): TemplateNames<T>[] {\n\t\treturn Object.keys(this.config.templates) as TemplateNames<T>[];\n\t}\n}\n\nexport function createEmailClient<T extends TemplateRecord>(\n\tconfig: EmailClientConfig<T>,\n): SMTPClient<T> {\n\treturn new SMTPClient(config);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAYA,IAAa,aAAb,MAA4E;CAC3E,AAAQ;CACR,AAAQ;CAER,YAAYA,QAA8B;AACzC,OAAK,SAAS;AACd,OAAK,cAAc,mBAAW,gBAAgB,OAAO,KAAY;CACjE;CAED,MAAM,KAAKC,SAA2C;EACrD,MAAM,cAAc;GACnB,GAAG,KAAK,OAAO;GACf,GAAG;EACH;AAED,OAAK,YAAY,KAChB,OAAM,IAAI,MACT;AAIF,OAAK,YAAY,SAAS,YAAY,KACrC,OAAM,IAAI,MAAM;EAGjB,MAAM,OAAO,MAAM,KAAK,YAAY,SAAS,YAAY;AAEzD,SAAO;GACN,WAAW,KAAK;GAChB,UAAU,KAAK,YAAY,CAAE;GAC7B,UAAU,KAAK,YAAY,CAAE;GAC7B,UAAU,KAAK;EACf;CACD;CAED,MAAM,aACLC,UACAC,SAGsB;EACtB,MAAM,YAAY,KAAK,OAAO,UAAU;AACxC,OAAK,UACJ,OAAM,IAAI,OAAO,YAAY,OAAO,SAAS,CAAC;EAG/C,MAAM,UAAU,UAAU,QAAQ,MAAM;EACxC,MAAM,OAAO,MAAM,qCAAO,QAAQ;AAElC,SAAO,KAAK,KAAK;GAChB,GAAG;GACH;EACA,EAAC;CACF;CAED,MAAM,SAA2B;AAChC,MAAI;AACH,SAAM,KAAK,YAAY,QAAQ;AAC/B,UAAO;EACP,QAAO;AACP,UAAO;EACP;CACD;CAED,MAAM,QAAuB;AAC5B,OAAK,YAAY,OAAO;CACxB;CAED,mBAAuC;AACtC,SAAO,OAAO,KAAK,KAAK,OAAO,UAAU;CACzC;AACD;AAED,SAAgB,kBACfH,QACgB;AAChB,QAAO,IAAI,WAAW;AACtB"}