@geekmidas/emailkit 10.0.0-alpha.9 → 10.0.0-alpha.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.cjs +124 -3
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +23 -3
- package/dist/client.d.cts.map +1 -0
- package/dist/client.d.mts +23 -3
- package/dist/client.d.mts.map +1 -0
- package/dist/client.mjs +99 -2
- package/dist/client.mjs.map +1 -0
- package/dist/errors.cjs +52 -4
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.cts +39 -2
- package/dist/errors.d.cts.map +1 -0
- package/dist/errors.d.mts +39 -2
- package/dist/errors.d.mts.map +1 -0
- package/dist/errors.mjs +49 -2
- package/dist/errors.mjs.map +1 -0
- package/dist/index.cjs +5 -5
- package/dist/index.d.cts +5 -5
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +4 -5
- package/dist/types.d.cts +88 -2
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.mts +88 -2
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +1 -0
- package/dist/url.cjs +60 -3
- package/dist/url.cjs.map +1 -0
- package/dist/url.d.cts +16 -3
- package/dist/url.d.cts.map +1 -0
- package/dist/url.d.mts +16 -3
- package/dist/url.d.mts.map +1 -0
- package/dist/url.mjs +59 -3
- package/dist/url.mjs.map +1 -0
- package/package.json +9 -4
- package/dist/client-BkN5BGeJ.mjs +0 -58
- package/dist/client-BkN5BGeJ.mjs.map +0 -1
- package/dist/client-Dcgdc6HG.d.mts +0 -21
- package/dist/client-Dcgdc6HG.d.mts.map +0 -1
- package/dist/client-DyfZrCpo.d.cts +0 -21
- package/dist/client-DyfZrCpo.d.cts.map +0 -1
- package/dist/client-sZNHPYYg.cjs +0 -92
- package/dist/client-sZNHPYYg.cjs.map +0 -1
- package/dist/errors-6difZDw0.cjs +0 -68
- package/dist/errors-6difZDw0.cjs.map +0 -1
- package/dist/errors-Ba3cEHPO.mjs +0 -50
- package/dist/errors-Ba3cEHPO.mjs.map +0 -1
- package/dist/errors-ZllQgZ9Y.d.mts +0 -41
- package/dist/errors-ZllQgZ9Y.d.mts.map +0 -1
- package/dist/errors-xJ88z_5r.d.cts +0 -41
- package/dist/errors-xJ88z_5r.d.cts.map +0 -1
- package/dist/types-CoPG5az4.d.mts +0 -90
- package/dist/types-CoPG5az4.d.mts.map +0 -1
- package/dist/types-D5yAoaGX.d.cts +0 -90
- package/dist/types-D5yAoaGX.d.cts.map +0 -1
- package/dist/url-B2hKcFA3.d.mts +0 -20
- package/dist/url-B2hKcFA3.d.mts.map +0 -1
- package/dist/url-B8QqBrMt.mjs +0 -49
- package/dist/url-B8QqBrMt.mjs.map +0 -1
- package/dist/url-CVk3Cs_8.d.cts +0 -20
- package/dist/url-CVk3Cs_8.d.cts.map +0 -1
- package/dist/url-GT3jLGq-.cjs +0 -54
- package/dist/url-GT3jLGq-.cjs.map +0 -1
package/dist/client.cjs
CHANGED
|
@@ -1,4 +1,125 @@
|
|
|
1
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
//#endregion
|
|
24
|
+
let _opentelemetry_api = require("@opentelemetry/api");
|
|
25
|
+
let _react_email_components = require("@react-email/components");
|
|
26
|
+
let nodemailer = require("nodemailer");
|
|
27
|
+
nodemailer = __toESM(nodemailer, 1);
|
|
28
|
+
//#region src/client.ts
|
|
29
|
+
var SMTPClient = class {
|
|
30
|
+
transporter;
|
|
31
|
+
config;
|
|
32
|
+
constructor(config) {
|
|
33
|
+
this.config = config;
|
|
34
|
+
this.transporter = nodemailer.default.createTransport(config.smtp);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Send one message. An `email.send` span covers it, through the global
|
|
38
|
+
* OpenTelemetry tracer: the transport and how many were accepted or
|
|
39
|
+
* rejected, never an address, subject or body.
|
|
40
|
+
*/
|
|
41
|
+
async send(options) {
|
|
42
|
+
return traceSend({ "email.recipients": recipients(options) }, () => this.deliver(options));
|
|
43
|
+
}
|
|
44
|
+
async deliver(options) {
|
|
45
|
+
const mailOptions = {
|
|
46
|
+
...this.config.defaults,
|
|
47
|
+
...options
|
|
48
|
+
};
|
|
49
|
+
if (!mailOptions.from) throw new Error("The \"from\" field is required in email options or defaults");
|
|
50
|
+
if (!mailOptions.text && !mailOptions.html) throw new Error("Either text or html content must be provided");
|
|
51
|
+
const info = await this.transporter.sendMail(mailOptions);
|
|
52
|
+
return {
|
|
53
|
+
messageId: info.messageId,
|
|
54
|
+
accepted: info.accepted || [],
|
|
55
|
+
rejected: info.rejected || [],
|
|
56
|
+
response: info.response
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async sendTemplate(template, options) {
|
|
60
|
+
const Component = this.config.templates[template];
|
|
61
|
+
if (!Component) throw new Error(`Template "${String(template)}" not found`);
|
|
62
|
+
const element = Component(options.props);
|
|
63
|
+
const html = await (0, _react_email_components.render)(element);
|
|
64
|
+
return traceSend({
|
|
65
|
+
"email.template": String(template),
|
|
66
|
+
"email.recipients": recipients(options)
|
|
67
|
+
}, () => this.deliver({
|
|
68
|
+
...options,
|
|
69
|
+
html
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
async verify() {
|
|
73
|
+
try {
|
|
74
|
+
await this.transporter.verify();
|
|
75
|
+
return true;
|
|
76
|
+
} catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async close() {
|
|
81
|
+
this.transporter.close();
|
|
82
|
+
}
|
|
83
|
+
getTemplateNames() {
|
|
84
|
+
return Object.keys(this.config.templates);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
function createEmailClient(config) {
|
|
88
|
+
return new SMTPClient(config);
|
|
89
|
+
}
|
|
90
|
+
/** How many addresses a message is for: to, cc and bcc. */
|
|
91
|
+
function recipients(options) {
|
|
92
|
+
return count(options.to) + count(options.cc) + count(options.bcc);
|
|
93
|
+
}
|
|
94
|
+
function count(value) {
|
|
95
|
+
if (!value) return 0;
|
|
96
|
+
return Array.isArray(value) ? value.length : 1;
|
|
97
|
+
}
|
|
98
|
+
async function traceSend(attributes, send) {
|
|
99
|
+
const span = _opentelemetry_api.trace.getTracer("@geekmidas/emailkit").startSpan("email.send", {
|
|
100
|
+
kind: _opentelemetry_api.SpanKind.CLIENT,
|
|
101
|
+
attributes: {
|
|
102
|
+
"email.transport": "smtp",
|
|
103
|
+
...attributes
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
try {
|
|
107
|
+
const result = await send();
|
|
108
|
+
if (result.accepted.length || result.rejected.length) span.setAttributes({
|
|
109
|
+
"email.accepted": count(result.accepted),
|
|
110
|
+
"email.rejected": count(result.rejected)
|
|
111
|
+
});
|
|
112
|
+
return result;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (error instanceof Error) span.recordException(error);
|
|
115
|
+
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
116
|
+
throw error;
|
|
117
|
+
} finally {
|
|
118
|
+
span.end();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
exports.SMTPClient = SMTPClient;
|
|
123
|
+
exports.createEmailClient = createEmailClient;
|
|
2
124
|
|
|
3
|
-
|
|
4
|
-
exports.createEmailClient = require_client.createEmailClient;
|
|
125
|
+
//# sourceMappingURL=client.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.cjs","names":["render","trace","SpanKind","SpanStatusCode"],"sources":["../src/client.ts"],"sourcesContent":["import {\n\ttype Attributes,\n\tSpanKind,\n\tSpanStatusCode,\n\ttrace,\n} from '@opentelemetry/api';\nimport { 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\t/**\n\t * Send one message. An `email.send` span covers it, through the global\n\t * OpenTelemetry tracer: the transport and how many were accepted or\n\t * rejected, never an address, subject or body.\n\t */\n\tasync send(options: SendOptions): Promise<SendResult> {\n\t\treturn traceSend({ 'email.recipients': recipients(options) }, () =>\n\t\t\tthis.deliver(options),\n\t\t);\n\t}\n\n\tprivate async deliver(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 traceSend(\n\t\t\t{\n\t\t\t\t'email.template': String(template),\n\t\t\t\t'email.recipients': recipients(options),\n\t\t\t},\n\t\t\t() => this.deliver({ ...options, html }),\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\n/** How many addresses a message is for: to, cc and bcc. */\nfunction recipients(options: {\n\tto?: unknown;\n\tcc?: unknown;\n\tbcc?: unknown;\n}): number {\n\treturn count(options.to) + count(options.cc) + count(options.bcc);\n}\n\nfunction count(value: unknown): number {\n\tif (!value) return 0;\n\treturn Array.isArray(value) ? value.length : 1;\n}\n\nasync function traceSend(\n\tattributes: Attributes,\n\tsend: () => Promise<SendResult>,\n): Promise<SendResult> {\n\tconst span = trace.getTracer('@geekmidas/emailkit').startSpan('email.send', {\n\t\tkind: SpanKind.CLIENT,\n\t\tattributes: { 'email.transport': 'smtp', ...attributes },\n\t});\n\ttry {\n\t\tconst result = await send();\n\t\t// What the server said, when the transport reports it.\n\t\tif (result.accepted.length || result.rejected.length) {\n\t\t\tspan.setAttributes({\n\t\t\t\t'email.accepted': count(result.accepted),\n\t\t\t\t'email.rejected': count(result.rejected),\n\t\t\t});\n\t\t}\n\t\treturn result;\n\t} catch (error) {\n\t\tif (error instanceof Error) span.recordException(error);\n\t\tspan.setStatus({ code: SpanStatusCode.ERROR });\n\t\tthrow error;\n\t} finally {\n\t\tspan.end();\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkBA,IAAa,aAAb,MAA4E;CAC3E;CACA;CAEA,YAAY,QAA8B;EACzC,KAAK,SAAS;EACd,KAAK,cAAc,WAAA,QAAW,gBAAgB,OAAO,IAAW;CACjE;;;;;;CAOA,MAAM,KAAK,SAA2C;EACrD,OAAO,UAAU,EAAE,oBAAoB,WAAW,OAAO,EAAE,SAC1D,KAAK,QAAQ,OAAO,CACrB;CACD;CAEA,MAAc,QAAQ,SAA2C;EAChE,MAAM,cAAc;GACnB,GAAG,KAAK,OAAO;GACf,GAAG;EACJ;EAEA,IAAI,CAAC,YAAY,MAChB,MAAM,IAAI,MACT,6DACD;EAGD,IAAI,CAAC,YAAY,QAAQ,CAAC,YAAY,MACrC,MAAM,IAAI,MAAM,8CAA8C;EAG/D,MAAM,OAAO,MAAM,KAAK,YAAY,SAAS,WAAW;EAExD,OAAO;GACN,WAAW,KAAK;GAChB,UAAU,KAAK,YAAY,CAAC;GAC5B,UAAU,KAAK,YAAY,CAAC;GAC5B,UAAU,KAAK;EAChB;CACD;CAEA,MAAM,aACL,UACA,SAGsB;EACtB,MAAM,YAAY,KAAK,OAAO,UAAU;EACxC,IAAI,CAAC,WACJ,MAAM,IAAI,MAAM,aAAa,OAAO,QAAQ,EAAE,YAAY;EAG3D,MAAM,UAAU,UAAU,QAAQ,KAAK;EACvC,MAAM,OAAO,OAAA,GAAMA,wBAAAA,OAAAA,CAAO,OAAO;EAEjC,OAAO,UACN;GACC,kBAAkB,OAAO,QAAQ;GACjC,oBAAoB,WAAW,OAAO;EACvC,SACM,KAAK,QAAQ;GAAE,GAAG;GAAS;EAAK,CAAC,CACxC;CACD;CAEA,MAAM,SAA2B;EAChC,IAAI;GACH,MAAM,KAAK,YAAY,OAAO;GAC9B,OAAO;EACR,QAAQ;GACP,OAAO;EACR;CACD;CAEA,MAAM,QAAuB;EAC5B,KAAK,YAAY,MAAM;CACxB;CAEA,mBAAuC;EACtC,OAAO,OAAO,KAAK,KAAK,OAAO,SAAS;CACzC;AACD;AAEA,SAAgB,kBACf,QACgB;CAChB,OAAO,IAAI,WAAW,MAAM;AAC7B;;AAGA,SAAS,WAAW,SAIT;CACV,OAAO,MAAM,QAAQ,EAAE,IAAI,MAAM,QAAQ,EAAE,IAAI,MAAM,QAAQ,GAAG;AACjE;AAEA,SAAS,MAAM,OAAwB;CACtC,IAAI,CAAC,OAAO,OAAO;CACnB,OAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,SAAS;AAC9C;AAEA,eAAe,UACd,YACA,MACsB;CACtB,MAAM,OAAOC,mBAAAA,MAAM,UAAU,qBAAqB,CAAC,CAAC,UAAU,cAAc;EAC3E,MAAMC,mBAAAA,SAAS;EACf,YAAY;GAAE,mBAAmB;GAAQ,GAAG;EAAW;CACxD,CAAC;CACD,IAAI;EACH,MAAM,SAAS,MAAM,KAAK;EAE1B,IAAI,OAAO,SAAS,UAAU,OAAO,SAAS,QAC7C,KAAK,cAAc;GAClB,kBAAkB,MAAM,OAAO,QAAQ;GACvC,kBAAkB,MAAM,OAAO,QAAQ;EACxC,CAAC;EAEF,OAAO;CACR,SAAS,OAAO;EACf,IAAI,iBAAiB,OAAO,KAAK,gBAAgB,KAAK;EACtD,KAAK,UAAU,EAAE,MAAMC,mBAAAA,eAAe,MAAM,CAAC;EAC7C,MAAM;CACP,UAAU;EACT,KAAK,IAAI;CACV;AACD"}
|
package/dist/client.d.cts
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
-
import "./types
|
|
2
|
-
|
|
3
|
-
export
|
|
1
|
+
import { EmailClient, EmailClientConfig, SendOptions, SendResult, TemplateNames, TemplatePropsFor, TemplateRecord } from "./types.cjs";
|
|
2
|
+
//#region src/client.d.ts
|
|
3
|
+
export declare class SMTPClient<T extends TemplateRecord> implements EmailClient<T> {
|
|
4
|
+
private transporter;
|
|
5
|
+
private config;
|
|
6
|
+
constructor(config: EmailClientConfig<T>);
|
|
7
|
+
/**
|
|
8
|
+
* Send one message. An `email.send` span covers it, through the global
|
|
9
|
+
* OpenTelemetry tracer: the transport and how many were accepted or
|
|
10
|
+
* rejected, never an address, subject or body.
|
|
11
|
+
*/
|
|
12
|
+
send(options: SendOptions): Promise<SendResult>;
|
|
13
|
+
private deliver;
|
|
14
|
+
sendTemplate<K extends TemplateNames<T>>(template: K, options: Omit<SendOptions, 'template'> & {
|
|
15
|
+
props: TemplatePropsFor<T, K>;
|
|
16
|
+
}): Promise<SendResult>;
|
|
17
|
+
verify(): Promise<boolean>;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
getTemplateNames(): TemplateNames<T>[];
|
|
20
|
+
}
|
|
21
|
+
export declare function createEmailClient<T extends TemplateRecord>(config: EmailClientConfig<T>): SMTPClient<T>;
|
|
22
|
+
//#endregion
|
|
23
|
+
//# sourceMappingURL=client.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.cts","names":[],"sources":["../src/client.ts"],"mappings":";;qBAkBa,WAAW,UAAU,2BAA2B,YAAY;UAChE;UACA;EAER,YAAY,QAAQ,kBAAkB;;;;;;EAUhC,KAAK,SAAS,cAAc,QAAQ;UAM5B;EA0BR,aAAa,UAAU,cAAc,IAC1C,UAAU,GACV,SAAS,KAAK;IACb,OAAO,iBAAiB,GAAG;MAE1B,QAAQ;EAkBL,UAAU;EASV,SAAS;EAIf,oBAAoB,cAAc;;wBAKnB,kBAAkB,UAAU,gBAC3C,QAAQ,kBAAkB,KACxB,WAAW"}
|
package/dist/client.d.mts
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
-
import "./types
|
|
2
|
-
|
|
3
|
-
export
|
|
1
|
+
import { EmailClient, EmailClientConfig, SendOptions, SendResult, TemplateNames, TemplatePropsFor, TemplateRecord } from "./types.mjs";
|
|
2
|
+
//#region src/client.d.ts
|
|
3
|
+
export declare class SMTPClient<T extends TemplateRecord> implements EmailClient<T> {
|
|
4
|
+
private transporter;
|
|
5
|
+
private config;
|
|
6
|
+
constructor(config: EmailClientConfig<T>);
|
|
7
|
+
/**
|
|
8
|
+
* Send one message. An `email.send` span covers it, through the global
|
|
9
|
+
* OpenTelemetry tracer: the transport and how many were accepted or
|
|
10
|
+
* rejected, never an address, subject or body.
|
|
11
|
+
*/
|
|
12
|
+
send(options: SendOptions): Promise<SendResult>;
|
|
13
|
+
private deliver;
|
|
14
|
+
sendTemplate<K extends TemplateNames<T>>(template: K, options: Omit<SendOptions, 'template'> & {
|
|
15
|
+
props: TemplatePropsFor<T, K>;
|
|
16
|
+
}): Promise<SendResult>;
|
|
17
|
+
verify(): Promise<boolean>;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
getTemplateNames(): TemplateNames<T>[];
|
|
20
|
+
}
|
|
21
|
+
export declare function createEmailClient<T extends TemplateRecord>(config: EmailClientConfig<T>): SMTPClient<T>;
|
|
22
|
+
//#endregion
|
|
23
|
+
//# sourceMappingURL=client.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.mts","names":[],"sources":["../src/client.ts"],"mappings":";;qBAkBa,WAAW,UAAU,2BAA2B,YAAY;UAChE;UACA;EAER,YAAY,QAAQ,kBAAkB;;;;;;EAUhC,KAAK,SAAS,cAAc,QAAQ;UAM5B;EA0BR,aAAa,UAAU,cAAc,IAC1C,UAAU,GACV,SAAS,KAAK;IACb,OAAO,iBAAiB,GAAG;MAE1B,QAAQ;EAkBL,UAAU;EASV,SAAS;EAIf,oBAAoB,cAAc;;wBAKnB,kBAAkB,UAAU,gBAC3C,QAAQ,kBAAkB,KACxB,WAAW"}
|
package/dist/client.mjs
CHANGED
|
@@ -1,3 +1,100 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
2
|
+
import { render } from "@react-email/components";
|
|
3
|
+
import nodemailer from "nodemailer";
|
|
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
|
+
/**
|
|
13
|
+
* Send one message. An `email.send` span covers it, through the global
|
|
14
|
+
* OpenTelemetry tracer: the transport and how many were accepted or
|
|
15
|
+
* rejected, never an address, subject or body.
|
|
16
|
+
*/
|
|
17
|
+
async send(options) {
|
|
18
|
+
return traceSend({ "email.recipients": recipients(options) }, () => this.deliver(options));
|
|
19
|
+
}
|
|
20
|
+
async deliver(options) {
|
|
21
|
+
const mailOptions = {
|
|
22
|
+
...this.config.defaults,
|
|
23
|
+
...options
|
|
24
|
+
};
|
|
25
|
+
if (!mailOptions.from) throw new Error("The \"from\" field is required in email options or defaults");
|
|
26
|
+
if (!mailOptions.text && !mailOptions.html) throw new Error("Either text or html content must be provided");
|
|
27
|
+
const info = await this.transporter.sendMail(mailOptions);
|
|
28
|
+
return {
|
|
29
|
+
messageId: info.messageId,
|
|
30
|
+
accepted: info.accepted || [],
|
|
31
|
+
rejected: info.rejected || [],
|
|
32
|
+
response: info.response
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
async sendTemplate(template, options) {
|
|
36
|
+
const Component = this.config.templates[template];
|
|
37
|
+
if (!Component) throw new Error(`Template "${String(template)}" not found`);
|
|
38
|
+
const element = Component(options.props);
|
|
39
|
+
const html = await render(element);
|
|
40
|
+
return traceSend({
|
|
41
|
+
"email.template": String(template),
|
|
42
|
+
"email.recipients": recipients(options)
|
|
43
|
+
}, () => this.deliver({
|
|
44
|
+
...options,
|
|
45
|
+
html
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
async verify() {
|
|
49
|
+
try {
|
|
50
|
+
await this.transporter.verify();
|
|
51
|
+
return true;
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async close() {
|
|
57
|
+
this.transporter.close();
|
|
58
|
+
}
|
|
59
|
+
getTemplateNames() {
|
|
60
|
+
return Object.keys(this.config.templates);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
function createEmailClient(config) {
|
|
64
|
+
return new SMTPClient(config);
|
|
65
|
+
}
|
|
66
|
+
/** How many addresses a message is for: to, cc and bcc. */
|
|
67
|
+
function recipients(options) {
|
|
68
|
+
return count(options.to) + count(options.cc) + count(options.bcc);
|
|
69
|
+
}
|
|
70
|
+
function count(value) {
|
|
71
|
+
if (!value) return 0;
|
|
72
|
+
return Array.isArray(value) ? value.length : 1;
|
|
73
|
+
}
|
|
74
|
+
async function traceSend(attributes, send) {
|
|
75
|
+
const span = trace.getTracer("@geekmidas/emailkit").startSpan("email.send", {
|
|
76
|
+
kind: SpanKind.CLIENT,
|
|
77
|
+
attributes: {
|
|
78
|
+
"email.transport": "smtp",
|
|
79
|
+
...attributes
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
try {
|
|
83
|
+
const result = await send();
|
|
84
|
+
if (result.accepted.length || result.rejected.length) span.setAttributes({
|
|
85
|
+
"email.accepted": count(result.accepted),
|
|
86
|
+
"email.rejected": count(result.rejected)
|
|
87
|
+
});
|
|
88
|
+
return result;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (error instanceof Error) span.recordException(error);
|
|
91
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
92
|
+
throw error;
|
|
93
|
+
} finally {
|
|
94
|
+
span.end();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
export { SMTPClient, createEmailClient };
|
|
2
99
|
|
|
3
|
-
|
|
100
|
+
//# sourceMappingURL=client.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import {\n\ttype Attributes,\n\tSpanKind,\n\tSpanStatusCode,\n\ttrace,\n} from '@opentelemetry/api';\nimport { 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\t/**\n\t * Send one message. An `email.send` span covers it, through the global\n\t * OpenTelemetry tracer: the transport and how many were accepted or\n\t * rejected, never an address, subject or body.\n\t */\n\tasync send(options: SendOptions): Promise<SendResult> {\n\t\treturn traceSend({ 'email.recipients': recipients(options) }, () =>\n\t\t\tthis.deliver(options),\n\t\t);\n\t}\n\n\tprivate async deliver(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 traceSend(\n\t\t\t{\n\t\t\t\t'email.template': String(template),\n\t\t\t\t'email.recipients': recipients(options),\n\t\t\t},\n\t\t\t() => this.deliver({ ...options, html }),\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\n/** How many addresses a message is for: to, cc and bcc. */\nfunction recipients(options: {\n\tto?: unknown;\n\tcc?: unknown;\n\tbcc?: unknown;\n}): number {\n\treturn count(options.to) + count(options.cc) + count(options.bcc);\n}\n\nfunction count(value: unknown): number {\n\tif (!value) return 0;\n\treturn Array.isArray(value) ? value.length : 1;\n}\n\nasync function traceSend(\n\tattributes: Attributes,\n\tsend: () => Promise<SendResult>,\n): Promise<SendResult> {\n\tconst span = trace.getTracer('@geekmidas/emailkit').startSpan('email.send', {\n\t\tkind: SpanKind.CLIENT,\n\t\tattributes: { 'email.transport': 'smtp', ...attributes },\n\t});\n\ttry {\n\t\tconst result = await send();\n\t\t// What the server said, when the transport reports it.\n\t\tif (result.accepted.length || result.rejected.length) {\n\t\t\tspan.setAttributes({\n\t\t\t\t'email.accepted': count(result.accepted),\n\t\t\t\t'email.rejected': count(result.rejected),\n\t\t\t});\n\t\t}\n\t\treturn result;\n\t} catch (error) {\n\t\tif (error instanceof Error) span.recordException(error);\n\t\tspan.setStatus({ code: SpanStatusCode.ERROR });\n\t\tthrow error;\n\t} finally {\n\t\tspan.end();\n\t}\n}\n"],"mappings":";;;;AAkBA,IAAa,aAAb,MAA4E;CAC3E;CACA;CAEA,YAAY,QAA8B;EACzC,KAAK,SAAS;EACd,KAAK,cAAc,WAAW,gBAAgB,OAAO,IAAW;CACjE;;;;;;CAOA,MAAM,KAAK,SAA2C;EACrD,OAAO,UAAU,EAAE,oBAAoB,WAAW,OAAO,EAAE,SAC1D,KAAK,QAAQ,OAAO,CACrB;CACD;CAEA,MAAc,QAAQ,SAA2C;EAChE,MAAM,cAAc;GACnB,GAAG,KAAK,OAAO;GACf,GAAG;EACJ;EAEA,IAAI,CAAC,YAAY,MAChB,MAAM,IAAI,MACT,6DACD;EAGD,IAAI,CAAC,YAAY,QAAQ,CAAC,YAAY,MACrC,MAAM,IAAI,MAAM,8CAA8C;EAG/D,MAAM,OAAO,MAAM,KAAK,YAAY,SAAS,WAAW;EAExD,OAAO;GACN,WAAW,KAAK;GAChB,UAAU,KAAK,YAAY,CAAC;GAC5B,UAAU,KAAK,YAAY,CAAC;GAC5B,UAAU,KAAK;EAChB;CACD;CAEA,MAAM,aACL,UACA,SAGsB;EACtB,MAAM,YAAY,KAAK,OAAO,UAAU;EACxC,IAAI,CAAC,WACJ,MAAM,IAAI,MAAM,aAAa,OAAO,QAAQ,EAAE,YAAY;EAG3D,MAAM,UAAU,UAAU,QAAQ,KAAK;EACvC,MAAM,OAAO,MAAM,OAAO,OAAO;EAEjC,OAAO,UACN;GACC,kBAAkB,OAAO,QAAQ;GACjC,oBAAoB,WAAW,OAAO;EACvC,SACM,KAAK,QAAQ;GAAE,GAAG;GAAS;EAAK,CAAC,CACxC;CACD;CAEA,MAAM,SAA2B;EAChC,IAAI;GACH,MAAM,KAAK,YAAY,OAAO;GAC9B,OAAO;EACR,QAAQ;GACP,OAAO;EACR;CACD;CAEA,MAAM,QAAuB;EAC5B,KAAK,YAAY,MAAM;CACxB;CAEA,mBAAuC;EACtC,OAAO,OAAO,KAAK,KAAK,OAAO,SAAS;CACzC;AACD;AAEA,SAAgB,kBACf,QACgB;CAChB,OAAO,IAAI,WAAW,MAAM;AAC7B;;AAGA,SAAS,WAAW,SAIT;CACV,OAAO,MAAM,QAAQ,EAAE,IAAI,MAAM,QAAQ,EAAE,IAAI,MAAM,QAAQ,GAAG;AACjE;AAEA,SAAS,MAAM,OAAwB;CACtC,IAAI,CAAC,OAAO,OAAO;CACnB,OAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,SAAS;AAC9C;AAEA,eAAe,UACd,YACA,MACsB;CACtB,MAAM,OAAO,MAAM,UAAU,qBAAqB,CAAC,CAAC,UAAU,cAAc;EAC3E,MAAM,SAAS;EACf,YAAY;GAAE,mBAAmB;GAAQ,GAAG;EAAW;CACxD,CAAC;CACD,IAAI;EACH,MAAM,SAAS,MAAM,KAAK;EAE1B,IAAI,OAAO,SAAS,UAAU,OAAO,SAAS,QAC7C,KAAK,cAAc;GAClB,kBAAkB,MAAM,OAAO,QAAQ;GACvC,kBAAkB,MAAM,OAAO,QAAQ;EACxC,CAAC;EAEF,OAAO;CACR,SAAS,OAAO;EACf,IAAI,iBAAiB,OAAO,KAAK,gBAAgB,KAAK;EACtD,KAAK,UAAU,EAAE,MAAM,eAAe,MAAM,CAAC;EAC7C,MAAM;CACP,UAAU;EACT,KAAK,IAAI;CACV;AACD"}
|
package/dist/errors.cjs
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/errors.ts
|
|
3
|
+
/**
|
|
4
|
+
* Email URL errors.
|
|
5
|
+
*
|
|
6
|
+
* Every provider is reached over SMTP, so there are only two problems a URL can
|
|
7
|
+
* have: it is not a URL, or it names a scheme this transport cannot speak. Both
|
|
8
|
+
* carry the offending value as a field.
|
|
9
|
+
*
|
|
10
|
+
* Messages state the rule, which is constant, and never interpolate the value.
|
|
11
|
+
* An interpolated message cannot be matched on, reads differently every time it
|
|
12
|
+
* is thrown, and carries user input into every log line that touches it — and a
|
|
13
|
+
* mail URL's userinfo is a credential, which makes that leak a real one here.
|
|
14
|
+
*/
|
|
15
|
+
/** Base for anything wrong with an email URL, so callers may catch broadly. */
|
|
16
|
+
var EmailUrlError = class extends Error {
|
|
17
|
+
/** The URL that could not be used. Carries credentials — do not log it. */
|
|
18
|
+
url;
|
|
19
|
+
constructor(url, message) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.url = url;
|
|
22
|
+
this.name = new.target.name;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
/** The string is not a URL at all. */
|
|
26
|
+
var MalformedEmailUrl = class extends EmailUrlError {
|
|
27
|
+
constructor(url) {
|
|
28
|
+
super(url, "Not a valid URL");
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* A URL, but naming a scheme that is not SMTP.
|
|
33
|
+
*
|
|
34
|
+
* Almost always `ses://` or `resend://` — a provider written where a protocol
|
|
35
|
+
* belongs. Mail is delivered over SMTP whoever is delivering it, so the provider
|
|
36
|
+
* shows up as the URL's host and credentials, never as its scheme.
|
|
37
|
+
*/
|
|
38
|
+
var UnsupportedEmailScheme = class extends EmailUrlError {
|
|
39
|
+
/** The scheme the URL carried, e.g. `'ses:'`. */
|
|
40
|
+
actual;
|
|
41
|
+
/** The schemes this transport speaks. */
|
|
42
|
+
expected = ["smtp:", "smtps:"];
|
|
43
|
+
constructor(url, actual) {
|
|
44
|
+
super(url, "Email URL scheme must be smtp: or smtps:");
|
|
45
|
+
this.actual = actual;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
49
|
+
exports.EmailUrlError = EmailUrlError;
|
|
50
|
+
exports.MalformedEmailUrl = MalformedEmailUrl;
|
|
51
|
+
exports.UnsupportedEmailScheme = UnsupportedEmailScheme;
|
|
2
52
|
|
|
3
|
-
|
|
4
|
-
exports.MalformedEmailUrl = require_errors.MalformedEmailUrl;
|
|
5
|
-
exports.UnsupportedEmailScheme = require_errors.UnsupportedEmailScheme;
|
|
53
|
+
//# sourceMappingURL=errors.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Email URL errors.\n *\n * Every provider is reached over SMTP, so there are only two problems a URL can\n * have: it is not a URL, or it names a scheme this transport cannot speak. Both\n * carry the offending value as a field.\n *\n * Messages state the rule, which is constant, and never interpolate the value.\n * An interpolated message cannot be matched on, reads differently every time it\n * is thrown, and carries user input into every log line that touches it — and a\n * mail URL's userinfo is a credential, which makes that leak a real one here.\n */\n\n/** Base for anything wrong with an email URL, so callers may catch broadly. */\nexport abstract class EmailUrlError extends Error {\n\t/** The URL that could not be used. Carries credentials — do not log it. */\n\treadonly url: string;\n\n\tconstructor(url: string, message: string) {\n\t\tsuper(message);\n\t\tthis.url = url;\n\t\tthis.name = new.target.name;\n\t}\n}\n\n/** The string is not a URL at all. */\nexport class MalformedEmailUrl extends EmailUrlError {\n\tconstructor(url: string) {\n\t\tsuper(url, 'Not a valid URL');\n\t}\n}\n\n/**\n * A URL, but naming a scheme that is not SMTP.\n *\n * Almost always `ses://` or `resend://` — a provider written where a protocol\n * belongs. Mail is delivered over SMTP whoever is delivering it, so the provider\n * shows up as the URL's host and credentials, never as its scheme.\n */\nexport class UnsupportedEmailScheme extends EmailUrlError {\n\t/** The scheme the URL carried, e.g. `'ses:'`. */\n\treadonly actual: string;\n\t/** The schemes this transport speaks. */\n\treadonly expected: readonly string[] = ['smtp:', 'smtps:'];\n\n\tconstructor(url: string, actual: string) {\n\t\tsuper(url, 'Email URL scheme must be smtp: or smtps:');\n\t\tthis.actual = actual;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;AAcA,IAAsB,gBAAtB,cAA4C,MAAM;;CAEjD;CAEA,YAAY,KAAa,SAAiB;EACzC,MAAM,OAAO;EACb,KAAK,MAAM;EACX,KAAK,OAAO,WAAW;CACxB;AACD;;AAGA,IAAa,oBAAb,cAAuC,cAAc;CACpD,YAAY,KAAa;EACxB,MAAM,KAAK,iBAAiB;CAC7B;AACD;;;;;;;;AASA,IAAa,yBAAb,cAA4C,cAAc;;CAEzD;;CAEA,WAAuC,CAAC,SAAS,QAAQ;CAEzD,YAAY,KAAa,QAAgB;EACxC,MAAM,KAAK,0CAA0C;EACrD,KAAK,SAAS;CACf;AACD"}
|
package/dist/errors.d.cts
CHANGED
|
@@ -1,2 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/errors.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Email URL errors.
|
|
4
|
+
*
|
|
5
|
+
* Every provider is reached over SMTP, so there are only two problems a URL can
|
|
6
|
+
* have: it is not a URL, or it names a scheme this transport cannot speak. Both
|
|
7
|
+
* carry the offending value as a field.
|
|
8
|
+
*
|
|
9
|
+
* Messages state the rule, which is constant, and never interpolate the value.
|
|
10
|
+
* An interpolated message cannot be matched on, reads differently every time it
|
|
11
|
+
* is thrown, and carries user input into every log line that touches it — and a
|
|
12
|
+
* mail URL's userinfo is a credential, which makes that leak a real one here.
|
|
13
|
+
*/
|
|
14
|
+
/** Base for anything wrong with an email URL, so callers may catch broadly. */
|
|
15
|
+
export declare abstract class EmailUrlError extends Error {
|
|
16
|
+
/** The URL that could not be used. Carries credentials — do not log it. */
|
|
17
|
+
readonly url: string;
|
|
18
|
+
constructor(url: string, message: string);
|
|
19
|
+
}
|
|
20
|
+
/** The string is not a URL at all. */
|
|
21
|
+
export declare class MalformedEmailUrl extends EmailUrlError {
|
|
22
|
+
constructor(url: string);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A URL, but naming a scheme that is not SMTP.
|
|
26
|
+
*
|
|
27
|
+
* Almost always `ses://` or `resend://` — a provider written where a protocol
|
|
28
|
+
* belongs. Mail is delivered over SMTP whoever is delivering it, so the provider
|
|
29
|
+
* shows up as the URL's host and credentials, never as its scheme.
|
|
30
|
+
*/
|
|
31
|
+
export declare class UnsupportedEmailScheme extends EmailUrlError {
|
|
32
|
+
/** The scheme the URL carried, e.g. `'ses:'`. */
|
|
33
|
+
readonly actual: string;
|
|
34
|
+
/** The schemes this transport speaks. */
|
|
35
|
+
readonly expected: readonly string[];
|
|
36
|
+
constructor(url: string, actual: string);
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
//# sourceMappingURL=errors.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.cts","names":[],"sources":["../src/errors.ts"],"mappings":";;;;;;;;;;;;;;8BAcsB,sBAAsB;;WAElC;EAET,YAAY,aAAa;;;qBAQb,0BAA0B;EACtC,YAAY;;;;;;;;;qBAYA,+BAA+B;;WAElC;;WAEA;EAET,YAAY,aAAa"}
|
package/dist/errors.d.mts
CHANGED
|
@@ -1,2 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region src/errors.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Email URL errors.
|
|
4
|
+
*
|
|
5
|
+
* Every provider is reached over SMTP, so there are only two problems a URL can
|
|
6
|
+
* have: it is not a URL, or it names a scheme this transport cannot speak. Both
|
|
7
|
+
* carry the offending value as a field.
|
|
8
|
+
*
|
|
9
|
+
* Messages state the rule, which is constant, and never interpolate the value.
|
|
10
|
+
* An interpolated message cannot be matched on, reads differently every time it
|
|
11
|
+
* is thrown, and carries user input into every log line that touches it — and a
|
|
12
|
+
* mail URL's userinfo is a credential, which makes that leak a real one here.
|
|
13
|
+
*/
|
|
14
|
+
/** Base for anything wrong with an email URL, so callers may catch broadly. */
|
|
15
|
+
export declare abstract class EmailUrlError extends Error {
|
|
16
|
+
/** The URL that could not be used. Carries credentials — do not log it. */
|
|
17
|
+
readonly url: string;
|
|
18
|
+
constructor(url: string, message: string);
|
|
19
|
+
}
|
|
20
|
+
/** The string is not a URL at all. */
|
|
21
|
+
export declare class MalformedEmailUrl extends EmailUrlError {
|
|
22
|
+
constructor(url: string);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A URL, but naming a scheme that is not SMTP.
|
|
26
|
+
*
|
|
27
|
+
* Almost always `ses://` or `resend://` — a provider written where a protocol
|
|
28
|
+
* belongs. Mail is delivered over SMTP whoever is delivering it, so the provider
|
|
29
|
+
* shows up as the URL's host and credentials, never as its scheme.
|
|
30
|
+
*/
|
|
31
|
+
export declare class UnsupportedEmailScheme extends EmailUrlError {
|
|
32
|
+
/** The scheme the URL carried, e.g. `'ses:'`. */
|
|
33
|
+
readonly actual: string;
|
|
34
|
+
/** The schemes this transport speaks. */
|
|
35
|
+
readonly expected: readonly string[];
|
|
36
|
+
constructor(url: string, actual: string);
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
//# sourceMappingURL=errors.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.mts","names":[],"sources":["../src/errors.ts"],"mappings":";;;;;;;;;;;;;;8BAcsB,sBAAsB;;WAElC;EAET,YAAY,aAAa;;;qBAQb,0BAA0B;EACtC,YAAY;;;;;;;;;qBAYA,+BAA+B;;WAElC;;WAEA;EAET,YAAY,aAAa"}
|
package/dist/errors.mjs
CHANGED
|
@@ -1,3 +1,50 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/errors.ts
|
|
2
|
+
/**
|
|
3
|
+
* Email URL errors.
|
|
4
|
+
*
|
|
5
|
+
* Every provider is reached over SMTP, so there are only two problems a URL can
|
|
6
|
+
* have: it is not a URL, or it names a scheme this transport cannot speak. Both
|
|
7
|
+
* carry the offending value as a field.
|
|
8
|
+
*
|
|
9
|
+
* Messages state the rule, which is constant, and never interpolate the value.
|
|
10
|
+
* An interpolated message cannot be matched on, reads differently every time it
|
|
11
|
+
* is thrown, and carries user input into every log line that touches it — and a
|
|
12
|
+
* mail URL's userinfo is a credential, which makes that leak a real one here.
|
|
13
|
+
*/
|
|
14
|
+
/** Base for anything wrong with an email URL, so callers may catch broadly. */
|
|
15
|
+
var EmailUrlError = class extends Error {
|
|
16
|
+
/** The URL that could not be used. Carries credentials — do not log it. */
|
|
17
|
+
url;
|
|
18
|
+
constructor(url, message) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.url = url;
|
|
21
|
+
this.name = new.target.name;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
/** The string is not a URL at all. */
|
|
25
|
+
var MalformedEmailUrl = class extends EmailUrlError {
|
|
26
|
+
constructor(url) {
|
|
27
|
+
super(url, "Not a valid URL");
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* A URL, but naming a scheme that is not SMTP.
|
|
32
|
+
*
|
|
33
|
+
* Almost always `ses://` or `resend://` — a provider written where a protocol
|
|
34
|
+
* belongs. Mail is delivered over SMTP whoever is delivering it, so the provider
|
|
35
|
+
* shows up as the URL's host and credentials, never as its scheme.
|
|
36
|
+
*/
|
|
37
|
+
var UnsupportedEmailScheme = class extends EmailUrlError {
|
|
38
|
+
/** The scheme the URL carried, e.g. `'ses:'`. */
|
|
39
|
+
actual;
|
|
40
|
+
/** The schemes this transport speaks. */
|
|
41
|
+
expected = ["smtp:", "smtps:"];
|
|
42
|
+
constructor(url, actual) {
|
|
43
|
+
super(url, "Email URL scheme must be smtp: or smtps:");
|
|
44
|
+
this.actual = actual;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
//#endregion
|
|
48
|
+
export { EmailUrlError, MalformedEmailUrl, UnsupportedEmailScheme };
|
|
2
49
|
|
|
3
|
-
|
|
50
|
+
//# sourceMappingURL=errors.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Email URL errors.\n *\n * Every provider is reached over SMTP, so there are only two problems a URL can\n * have: it is not a URL, or it names a scheme this transport cannot speak. Both\n * carry the offending value as a field.\n *\n * Messages state the rule, which is constant, and never interpolate the value.\n * An interpolated message cannot be matched on, reads differently every time it\n * is thrown, and carries user input into every log line that touches it — and a\n * mail URL's userinfo is a credential, which makes that leak a real one here.\n */\n\n/** Base for anything wrong with an email URL, so callers may catch broadly. */\nexport abstract class EmailUrlError extends Error {\n\t/** The URL that could not be used. Carries credentials — do not log it. */\n\treadonly url: string;\n\n\tconstructor(url: string, message: string) {\n\t\tsuper(message);\n\t\tthis.url = url;\n\t\tthis.name = new.target.name;\n\t}\n}\n\n/** The string is not a URL at all. */\nexport class MalformedEmailUrl extends EmailUrlError {\n\tconstructor(url: string) {\n\t\tsuper(url, 'Not a valid URL');\n\t}\n}\n\n/**\n * A URL, but naming a scheme that is not SMTP.\n *\n * Almost always `ses://` or `resend://` — a provider written where a protocol\n * belongs. Mail is delivered over SMTP whoever is delivering it, so the provider\n * shows up as the URL's host and credentials, never as its scheme.\n */\nexport class UnsupportedEmailScheme extends EmailUrlError {\n\t/** The scheme the URL carried, e.g. `'ses:'`. */\n\treadonly actual: string;\n\t/** The schemes this transport speaks. */\n\treadonly expected: readonly string[] = ['smtp:', 'smtps:'];\n\n\tconstructor(url: string, actual: string) {\n\t\tsuper(url, 'Email URL scheme must be smtp: or smtps:');\n\t\tthis.actual = actual;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;AAcA,IAAsB,gBAAtB,cAA4C,MAAM;;CAEjD;CAEA,YAAY,KAAa,SAAiB;EACzC,MAAM,OAAO;EACb,KAAK,MAAM;EACX,KAAK,OAAO,WAAW;CACxB;AACD;;AAGA,IAAa,oBAAb,cAAuC,cAAc;CACpD,YAAY,KAAa;EACxB,MAAM,KAAK,iBAAiB;CAC7B;AACD;;;;;;;;AASA,IAAa,yBAAb,cAA4C,cAAc;;CAEzD;;CAEA,WAAuC,CAAC,SAAS,QAAQ;CAEzD,YAAY,KAAa,QAAgB;EACxC,MAAM,KAAK,0CAA0C;EACrD,KAAK,SAAS;CACf;AACD"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_client = require("./client.cjs");
|
|
3
|
+
const require_errors = require("./errors.cjs");
|
|
4
|
+
const require_url = require("./url.cjs");
|
|
5
5
|
exports.EmailUrlError = require_errors.EmailUrlError;
|
|
6
6
|
exports.MalformedEmailUrl = require_errors.MalformedEmailUrl;
|
|
7
7
|
exports.SMTPClient = require_client.SMTPClient;
|
|
8
8
|
exports.UnsupportedEmailScheme = require_errors.UnsupportedEmailScheme;
|
|
9
9
|
exports.createEmailClient = require_client.createEmailClient;
|
|
10
|
-
exports.parseEmailUrl = require_url.parseEmailUrl;
|
|
10
|
+
exports.parseEmailUrl = require_url.parseEmailUrl;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Attachment, EmailClient, EmailClientConfig, EmailOptions, EmailTemplate, PlainEmailOptions, SMTPConfig, SendOptions, SendResult, TemplateNames, TemplatePropsFor, TemplateRecord } from "./types
|
|
2
|
-
import { SMTPClient, createEmailClient } from "./client
|
|
3
|
-
import { EmailUrlError, MalformedEmailUrl, UnsupportedEmailScheme } from "./errors
|
|
4
|
-
import { parseEmailUrl } from "./url
|
|
5
|
-
export { Attachment, EmailClient, EmailClientConfig, EmailOptions, EmailTemplate, EmailUrlError, MalformedEmailUrl, PlainEmailOptions, SMTPClient, SMTPConfig, SendOptions, SendResult, TemplateNames, TemplatePropsFor, TemplateRecord, UnsupportedEmailScheme, createEmailClient, parseEmailUrl };
|
|
1
|
+
import { Attachment, EmailClient, EmailClientConfig, EmailOptions, EmailTemplate, PlainEmailOptions, SMTPConfig, SendOptions, SendResult, TemplateNames, TemplatePropsFor, TemplateRecord } from "./types.cjs";
|
|
2
|
+
import { SMTPClient, createEmailClient } from "./client.cjs";
|
|
3
|
+
import { EmailUrlError, MalformedEmailUrl, UnsupportedEmailScheme } from "./errors.cjs";
|
|
4
|
+
import { parseEmailUrl } from "./url.cjs";
|
|
5
|
+
export { type Attachment, type EmailClient, type EmailClientConfig, type EmailOptions, type EmailTemplate, EmailUrlError, MalformedEmailUrl, type PlainEmailOptions, SMTPClient, type SMTPConfig, type SendOptions, type SendResult, type TemplateNames, type TemplatePropsFor, type TemplateRecord, UnsupportedEmailScheme, createEmailClient, parseEmailUrl };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Attachment, EmailClient, EmailClientConfig, EmailOptions, EmailTemplate, PlainEmailOptions, SMTPConfig, SendOptions, SendResult, TemplateNames, TemplatePropsFor, TemplateRecord } from "./types
|
|
2
|
-
import { SMTPClient, createEmailClient } from "./client
|
|
3
|
-
import { EmailUrlError, MalformedEmailUrl, UnsupportedEmailScheme } from "./errors
|
|
4
|
-
import { parseEmailUrl } from "./url
|
|
5
|
-
export { Attachment, EmailClient, EmailClientConfig, EmailOptions, EmailTemplate, EmailUrlError, MalformedEmailUrl, PlainEmailOptions, SMTPClient, SMTPConfig, SendOptions, SendResult, TemplateNames, TemplatePropsFor, TemplateRecord, UnsupportedEmailScheme, createEmailClient, parseEmailUrl };
|
|
1
|
+
import { Attachment, EmailClient, EmailClientConfig, EmailOptions, EmailTemplate, PlainEmailOptions, SMTPConfig, SendOptions, SendResult, TemplateNames, TemplatePropsFor, TemplateRecord } from "./types.mjs";
|
|
2
|
+
import { SMTPClient, createEmailClient } from "./client.mjs";
|
|
3
|
+
import { EmailUrlError, MalformedEmailUrl, UnsupportedEmailScheme } from "./errors.mjs";
|
|
4
|
+
import { parseEmailUrl } from "./url.mjs";
|
|
5
|
+
export { type Attachment, type EmailClient, type EmailClientConfig, type EmailOptions, type EmailTemplate, EmailUrlError, MalformedEmailUrl, type PlainEmailOptions, SMTPClient, type SMTPConfig, type SendOptions, type SendResult, type TemplateNames, type TemplatePropsFor, type TemplateRecord, UnsupportedEmailScheme, createEmailClient, parseEmailUrl };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { SMTPClient, createEmailClient } from "./client
|
|
2
|
-
import { EmailUrlError, MalformedEmailUrl, UnsupportedEmailScheme } from "./errors
|
|
3
|
-
import { parseEmailUrl } from "./url
|
|
4
|
-
|
|
5
|
-
export { EmailUrlError, MalformedEmailUrl, SMTPClient, UnsupportedEmailScheme, createEmailClient, parseEmailUrl };
|
|
1
|
+
import { SMTPClient, createEmailClient } from "./client.mjs";
|
|
2
|
+
import { EmailUrlError, MalformedEmailUrl, UnsupportedEmailScheme } from "./errors.mjs";
|
|
3
|
+
import { parseEmailUrl } from "./url.mjs";
|
|
4
|
+
export { EmailUrlError, MalformedEmailUrl, SMTPClient, UnsupportedEmailScheme, createEmailClient, parseEmailUrl };
|