@mailzeno/core 0.1.0 → 0.1.2
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/LICENSE +21 -0
- package/dist/index.cjs +53 -63
- package/dist/index.d.cts +37 -11
- package/dist/index.d.ts +37 -11
- package/dist/index.js +53 -63
- package/package.json +2 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MailZeno
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
CHANGED
|
@@ -100,14 +100,14 @@ async function retry(fn, attempts = 3) {
|
|
|
100
100
|
|
|
101
101
|
// src/render/render-html.ts
|
|
102
102
|
function renderHtml(html, text) {
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
if (!
|
|
103
|
+
const cleanHtml = html?.trim();
|
|
104
|
+
const cleanText = text?.trim();
|
|
105
|
+
if (!cleanHtml && !cleanText) {
|
|
106
106
|
throw new Error("Email must contain html or text");
|
|
107
107
|
}
|
|
108
108
|
return {
|
|
109
|
-
html:
|
|
110
|
-
text:
|
|
109
|
+
html: cleanHtml || void 0,
|
|
110
|
+
text: cleanText || void 0
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -171,83 +171,73 @@ async function renderReact(component) {
|
|
|
171
171
|
|
|
172
172
|
// src/smtp/send.ts
|
|
173
173
|
async function sendEmail(smtp, options) {
|
|
174
|
+
validateOptions(options);
|
|
175
|
+
const { html, text } = await resolveContent(options);
|
|
176
|
+
const transporter = getTransporter(smtp);
|
|
174
177
|
try {
|
|
175
|
-
if (!options.from || !options.to || !options.subject) {
|
|
176
|
-
throw new ValidationError(
|
|
177
|
-
"Missing required fields: from, to, subject"
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
let html = options.html;
|
|
181
|
-
let text = options.text;
|
|
182
|
-
if (options.react) {
|
|
183
|
-
try {
|
|
184
|
-
const rendered2 = await renderReact(options.react);
|
|
185
|
-
html = rendered2.html;
|
|
186
|
-
} catch (err) {
|
|
187
|
-
throw new RenderError("React email rendering failed", err);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
const rendered = renderHtml(html, text);
|
|
191
|
-
const transporter = getTransporter(smtp);
|
|
192
178
|
const info = await retry(
|
|
193
179
|
() => transporter.sendMail({
|
|
194
180
|
from: options.from,
|
|
195
181
|
to: options.to,
|
|
196
182
|
subject: options.subject,
|
|
197
|
-
html:
|
|
198
|
-
text:
|
|
183
|
+
html: html || void 0,
|
|
184
|
+
text: text || void 0
|
|
199
185
|
})
|
|
200
186
|
);
|
|
201
187
|
return {
|
|
202
|
-
success: true,
|
|
203
188
|
messageId: info.messageId,
|
|
204
189
|
accepted: info.accepted,
|
|
205
190
|
rejected: info.rejected,
|
|
206
191
|
response: info.response
|
|
207
192
|
};
|
|
208
193
|
} catch (err) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
)
|
|
231
|
-
}
|
|
194
|
+
throw mapSMTPError(err);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function validateOptions(options) {
|
|
198
|
+
if (!options.from?.trim()) {
|
|
199
|
+
throw new ValidationError("Invalid 'from' address");
|
|
200
|
+
}
|
|
201
|
+
if (!options.subject?.trim()) {
|
|
202
|
+
throw new ValidationError("Subject is required");
|
|
203
|
+
}
|
|
204
|
+
if (!options.to || Array.isArray(options.to) && options.to.length === 0) {
|
|
205
|
+
throw new ValidationError("Invalid 'to' address");
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
async function resolveContent(options) {
|
|
209
|
+
switch (options.type) {
|
|
210
|
+
case "react": {
|
|
211
|
+
try {
|
|
212
|
+
const rendered = await renderReact(options.react);
|
|
213
|
+
return renderHtml(rendered.html);
|
|
214
|
+
} catch (err) {
|
|
215
|
+
throw new RenderError("React email rendering failed", err);
|
|
216
|
+
}
|
|
232
217
|
}
|
|
233
|
-
|
|
234
|
-
return
|
|
235
|
-
success: false,
|
|
236
|
-
error: new SMTPConnectionError(
|
|
237
|
-
"SMTP connection failed",
|
|
238
|
-
err
|
|
239
|
-
).message
|
|
240
|
-
};
|
|
218
|
+
case "raw": {
|
|
219
|
+
return renderHtml(options.html, options.text);
|
|
241
220
|
}
|
|
242
|
-
return {
|
|
243
|
-
success: false,
|
|
244
|
-
error: new SMTPResponseError(
|
|
245
|
-
err?.message || "SMTP sending failed",
|
|
246
|
-
err
|
|
247
|
-
).message
|
|
248
|
-
};
|
|
249
221
|
}
|
|
250
222
|
}
|
|
223
|
+
function mapSMTPError(err) {
|
|
224
|
+
if (err instanceof MailZenoError) {
|
|
225
|
+
return err;
|
|
226
|
+
}
|
|
227
|
+
if (err?.code === "EAUTH") {
|
|
228
|
+
return new SMTPAuthError("SMTP authentication failed", err);
|
|
229
|
+
}
|
|
230
|
+
if (err?.code === "ETIMEDOUT") {
|
|
231
|
+
return new SMTPTimeoutError("SMTP connection timed out", err);
|
|
232
|
+
}
|
|
233
|
+
if (err?.code === "ECONNECTION") {
|
|
234
|
+
return new SMTPConnectionError("SMTP connection failed", err);
|
|
235
|
+
}
|
|
236
|
+
if (err?.responseCode) {
|
|
237
|
+
return new SMTPResponseError(`SMTP error ${err.responseCode}`, err);
|
|
238
|
+
}
|
|
239
|
+
return new SMTPResponseError(err?.message || "SMTP sending failed", err);
|
|
240
|
+
}
|
|
251
241
|
// Annotate the CommonJS export names for ESM import in node:
|
|
252
242
|
0 && (module.exports = {
|
|
253
243
|
sendEmail
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SMTP configuration (engine-level only)
|
|
5
|
+
*/
|
|
1
6
|
interface SMTPConfig {
|
|
2
7
|
id: string;
|
|
3
8
|
host: string;
|
|
@@ -11,23 +16,44 @@ interface SMTPConfig {
|
|
|
11
16
|
maxConnections?: number;
|
|
12
17
|
maxMessages?: number;
|
|
13
18
|
}
|
|
14
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Raw HTML email content
|
|
21
|
+
*/
|
|
22
|
+
type RawContent = {
|
|
23
|
+
type: "raw";
|
|
24
|
+
html?: string;
|
|
25
|
+
text?: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* React email content
|
|
29
|
+
*/
|
|
30
|
+
type ReactContent = {
|
|
31
|
+
type: "react";
|
|
32
|
+
react: React.ReactElement;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Strict email options
|
|
36
|
+
*/
|
|
37
|
+
type SendEmailOptions = {
|
|
15
38
|
from: string;
|
|
16
39
|
to: string | string[];
|
|
17
40
|
subject: string;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
41
|
+
} & (RawContent | ReactContent);
|
|
42
|
+
/**
|
|
43
|
+
* Engine success response
|
|
44
|
+
* Throws on failure
|
|
45
|
+
*/
|
|
22
46
|
interface SendEmailResponse {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
response?: string;
|
|
28
|
-
error?: string;
|
|
47
|
+
messageId: string;
|
|
48
|
+
accepted: string[];
|
|
49
|
+
rejected: string[];
|
|
50
|
+
response: string;
|
|
29
51
|
}
|
|
30
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Core email sending engine
|
|
55
|
+
* Throws errors on failure
|
|
56
|
+
*/
|
|
31
57
|
declare function sendEmail(smtp: SMTPConfig, options: SendEmailOptions): Promise<SendEmailResponse>;
|
|
32
58
|
|
|
33
59
|
export { type SMTPConfig, type SendEmailOptions, type SendEmailResponse, sendEmail };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SMTP configuration (engine-level only)
|
|
5
|
+
*/
|
|
1
6
|
interface SMTPConfig {
|
|
2
7
|
id: string;
|
|
3
8
|
host: string;
|
|
@@ -11,23 +16,44 @@ interface SMTPConfig {
|
|
|
11
16
|
maxConnections?: number;
|
|
12
17
|
maxMessages?: number;
|
|
13
18
|
}
|
|
14
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Raw HTML email content
|
|
21
|
+
*/
|
|
22
|
+
type RawContent = {
|
|
23
|
+
type: "raw";
|
|
24
|
+
html?: string;
|
|
25
|
+
text?: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* React email content
|
|
29
|
+
*/
|
|
30
|
+
type ReactContent = {
|
|
31
|
+
type: "react";
|
|
32
|
+
react: React.ReactElement;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Strict email options
|
|
36
|
+
*/
|
|
37
|
+
type SendEmailOptions = {
|
|
15
38
|
from: string;
|
|
16
39
|
to: string | string[];
|
|
17
40
|
subject: string;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
41
|
+
} & (RawContent | ReactContent);
|
|
42
|
+
/**
|
|
43
|
+
* Engine success response
|
|
44
|
+
* Throws on failure
|
|
45
|
+
*/
|
|
22
46
|
interface SendEmailResponse {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
response?: string;
|
|
28
|
-
error?: string;
|
|
47
|
+
messageId: string;
|
|
48
|
+
accepted: string[];
|
|
49
|
+
rejected: string[];
|
|
50
|
+
response: string;
|
|
29
51
|
}
|
|
30
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Core email sending engine
|
|
55
|
+
* Throws errors on failure
|
|
56
|
+
*/
|
|
31
57
|
declare function sendEmail(smtp: SMTPConfig, options: SendEmailOptions): Promise<SendEmailResponse>;
|
|
32
58
|
|
|
33
59
|
export { type SMTPConfig, type SendEmailOptions, type SendEmailResponse, sendEmail };
|
package/dist/index.js
CHANGED
|
@@ -64,14 +64,14 @@ async function retry(fn, attempts = 3) {
|
|
|
64
64
|
|
|
65
65
|
// src/render/render-html.ts
|
|
66
66
|
function renderHtml(html, text) {
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
if (!
|
|
67
|
+
const cleanHtml = html?.trim();
|
|
68
|
+
const cleanText = text?.trim();
|
|
69
|
+
if (!cleanHtml && !cleanText) {
|
|
70
70
|
throw new Error("Email must contain html or text");
|
|
71
71
|
}
|
|
72
72
|
return {
|
|
73
|
-
html:
|
|
74
|
-
text:
|
|
73
|
+
html: cleanHtml || void 0,
|
|
74
|
+
text: cleanText || void 0
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -135,83 +135,73 @@ async function renderReact(component) {
|
|
|
135
135
|
|
|
136
136
|
// src/smtp/send.ts
|
|
137
137
|
async function sendEmail(smtp, options) {
|
|
138
|
+
validateOptions(options);
|
|
139
|
+
const { html, text } = await resolveContent(options);
|
|
140
|
+
const transporter = getTransporter(smtp);
|
|
138
141
|
try {
|
|
139
|
-
if (!options.from || !options.to || !options.subject) {
|
|
140
|
-
throw new ValidationError(
|
|
141
|
-
"Missing required fields: from, to, subject"
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
let html = options.html;
|
|
145
|
-
let text = options.text;
|
|
146
|
-
if (options.react) {
|
|
147
|
-
try {
|
|
148
|
-
const rendered2 = await renderReact(options.react);
|
|
149
|
-
html = rendered2.html;
|
|
150
|
-
} catch (err) {
|
|
151
|
-
throw new RenderError("React email rendering failed", err);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
const rendered = renderHtml(html, text);
|
|
155
|
-
const transporter = getTransporter(smtp);
|
|
156
142
|
const info = await retry(
|
|
157
143
|
() => transporter.sendMail({
|
|
158
144
|
from: options.from,
|
|
159
145
|
to: options.to,
|
|
160
146
|
subject: options.subject,
|
|
161
|
-
html:
|
|
162
|
-
text:
|
|
147
|
+
html: html || void 0,
|
|
148
|
+
text: text || void 0
|
|
163
149
|
})
|
|
164
150
|
);
|
|
165
151
|
return {
|
|
166
|
-
success: true,
|
|
167
152
|
messageId: info.messageId,
|
|
168
153
|
accepted: info.accepted,
|
|
169
154
|
rejected: info.rejected,
|
|
170
155
|
response: info.response
|
|
171
156
|
};
|
|
172
157
|
} catch (err) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
)
|
|
195
|
-
}
|
|
158
|
+
throw mapSMTPError(err);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function validateOptions(options) {
|
|
162
|
+
if (!options.from?.trim()) {
|
|
163
|
+
throw new ValidationError("Invalid 'from' address");
|
|
164
|
+
}
|
|
165
|
+
if (!options.subject?.trim()) {
|
|
166
|
+
throw new ValidationError("Subject is required");
|
|
167
|
+
}
|
|
168
|
+
if (!options.to || Array.isArray(options.to) && options.to.length === 0) {
|
|
169
|
+
throw new ValidationError("Invalid 'to' address");
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function resolveContent(options) {
|
|
173
|
+
switch (options.type) {
|
|
174
|
+
case "react": {
|
|
175
|
+
try {
|
|
176
|
+
const rendered = await renderReact(options.react);
|
|
177
|
+
return renderHtml(rendered.html);
|
|
178
|
+
} catch (err) {
|
|
179
|
+
throw new RenderError("React email rendering failed", err);
|
|
180
|
+
}
|
|
196
181
|
}
|
|
197
|
-
|
|
198
|
-
return
|
|
199
|
-
success: false,
|
|
200
|
-
error: new SMTPConnectionError(
|
|
201
|
-
"SMTP connection failed",
|
|
202
|
-
err
|
|
203
|
-
).message
|
|
204
|
-
};
|
|
182
|
+
case "raw": {
|
|
183
|
+
return renderHtml(options.html, options.text);
|
|
205
184
|
}
|
|
206
|
-
return {
|
|
207
|
-
success: false,
|
|
208
|
-
error: new SMTPResponseError(
|
|
209
|
-
err?.message || "SMTP sending failed",
|
|
210
|
-
err
|
|
211
|
-
).message
|
|
212
|
-
};
|
|
213
185
|
}
|
|
214
186
|
}
|
|
187
|
+
function mapSMTPError(err) {
|
|
188
|
+
if (err instanceof MailZenoError) {
|
|
189
|
+
return err;
|
|
190
|
+
}
|
|
191
|
+
if (err?.code === "EAUTH") {
|
|
192
|
+
return new SMTPAuthError("SMTP authentication failed", err);
|
|
193
|
+
}
|
|
194
|
+
if (err?.code === "ETIMEDOUT") {
|
|
195
|
+
return new SMTPTimeoutError("SMTP connection timed out", err);
|
|
196
|
+
}
|
|
197
|
+
if (err?.code === "ECONNECTION") {
|
|
198
|
+
return new SMTPConnectionError("SMTP connection failed", err);
|
|
199
|
+
}
|
|
200
|
+
if (err?.responseCode) {
|
|
201
|
+
return new SMTPResponseError(`SMTP error ${err.responseCode}`, err);
|
|
202
|
+
}
|
|
203
|
+
return new SMTPResponseError(err?.message || "SMTP sending failed", err);
|
|
204
|
+
}
|
|
215
205
|
export {
|
|
216
206
|
sendEmail
|
|
217
207
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mailzeno/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "SMTP engine for MailZeno",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
12
|
"import": "./dist/index.js",
|
|
13
|
-
"require": "./dist/index.cjs"
|
|
14
|
-
"types": "./dist/index.d.ts"
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
15
14
|
}
|
|
16
15
|
},
|
|
17
16
|
"files": [
|