@sharadox/email-web-library 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +67 -2
- package/distribution/email-address/index.d.ts +4 -0
- package/distribution/email-address/index.d.ts.map +1 -0
- package/distribution/email-address/index.js +16 -0
- package/distribution/email-address/index.js.map +1 -0
- package/distribution/email-address/type.d.ts +6 -0
- package/distribution/email-address/type.d.ts.map +1 -0
- package/distribution/email-address/type.js +2 -0
- package/distribution/email-address/type.js.map +1 -0
- package/distribution/email-error/index.d.ts +9 -0
- package/distribution/email-error/index.d.ts.map +1 -0
- package/distribution/email-error/index.js +10 -0
- package/distribution/email-error/index.js.map +1 -0
- package/distribution/email-error/type.d.ts +8 -0
- package/distribution/email-error/type.d.ts.map +1 -0
- package/distribution/email-error/type.js +2 -0
- package/distribution/email-error/type.js.map +1 -0
- package/distribution/email-message/index.d.ts +11 -0
- package/distribution/email-message/index.d.ts.map +1 -0
- package/distribution/email-message/index.js +28 -0
- package/distribution/email-message/index.js.map +1 -0
- package/distribution/email-message/type.d.ts +14 -0
- package/distribution/email-message/type.d.ts.map +1 -0
- package/distribution/email-message/type.js +2 -0
- package/distribution/email-message/type.js.map +1 -0
- package/distribution/email-sender/index.d.ts +11 -0
- package/distribution/email-sender/index.d.ts.map +1 -0
- package/distribution/email-sender/index.js +75 -0
- package/distribution/email-sender/index.js.map +1 -0
- package/distribution/email-sender/type.d.ts +18 -0
- package/distribution/email-sender/type.d.ts.map +1 -0
- package/distribution/email-sender/type.js +2 -0
- package/distribution/email-sender/type.js.map +1 -0
- package/distribution/email-server-configuration/index.d.ts +9 -0
- package/distribution/email-server-configuration/index.d.ts.map +1 -0
- package/distribution/email-server-configuration/index.js +35 -0
- package/distribution/email-server-configuration/index.js.map +1 -0
- package/distribution/email-server-configuration/type.d.ts +19 -0
- package/distribution/email-server-configuration/type.d.ts.map +1 -0
- package/distribution/email-server-configuration/type.js +2 -0
- package/distribution/email-server-configuration/type.js.map +1 -0
- package/distribution/index.d.ts +23 -0
- package/distribution/index.d.ts.map +1 -0
- package/distribution/index.js +18 -0
- package/distribution/index.js.map +1 -0
- package/package.json +73 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sharadox
|
|
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/README.md
CHANGED
|
@@ -1,3 +1,68 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @sharadox/email-web-library
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Send email from web applications — contact forms, notifications, receipts — through **any SMTP server**: your existing mailbox provider (Proton, Google Workspace, Microsoft 365, Fastmail…) or a sending service (Amazon SES, Postmark, Resend…). No provider SDK, no cloud account; switching provider is a change of four environment variables.
|
|
4
|
+
|
|
5
|
+
- Checks every message before it leaves: valid addresses, a one-line subject, a body — so form input can't inject extra headers or recipients.
|
|
6
|
+
- One error type, `EmailError`, whose `code` says whose problem it is: `configuration`, `invalid-message` or `delivery`.
|
|
7
|
+
- One short connection per message, which suits serverless functions (Next.js server actions, Vercel functions).
|
|
8
|
+
- Typed, tested against a real SMTP server, ESM only. The SMTP protocol work is done by [Nodemailer](https://nodemailer.com).
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
pnpm add @sharadox/email-web-library
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Use
|
|
17
|
+
|
|
18
|
+
Set the server in the environment:
|
|
19
|
+
|
|
20
|
+
| Variable | Example | Notes |
|
|
21
|
+
| ----------------------- | ------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
22
|
+
| `EMAIL_SERVER_HOST` | `smtp.protonmail.ch` | |
|
|
23
|
+
| `EMAIL_SERVER_PORT` | `587` | Optional, 587 by default |
|
|
24
|
+
| `EMAIL_SERVER_USERNAME` | `website@example.com` | |
|
|
25
|
+
| `EMAIL_SERVER_PASSWORD` | an SMTP token or password | Keep it in your host's secret store, never in the repository |
|
|
26
|
+
| `EMAIL_SERVER_SECURITY` | `starttls` | Optional: `starttls` (default), `tls` (default on port 465) or `none` (local test servers only) |
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
"use server";
|
|
30
|
+
import { createEmailSender, EmailError, readEmailServerConfiguration } from "@sharadox/email-web-library";
|
|
31
|
+
|
|
32
|
+
export async function sendContact(name: string, email: string, text: string) {
|
|
33
|
+
try {
|
|
34
|
+
const sender = createEmailSender(readEmailServerConfiguration());
|
|
35
|
+
await sender.send({
|
|
36
|
+
from: { name: "Website", address: "website@example.com" }, // usually must be the login address or an alias
|
|
37
|
+
to: "inbox@example.com",
|
|
38
|
+
replyTo: { name, address: email }, // replying goes straight to the visitor
|
|
39
|
+
subject: `New message from ${name}`,
|
|
40
|
+
text,
|
|
41
|
+
});
|
|
42
|
+
return { sent: true };
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (error instanceof EmailError && error.code === "invalid-message")
|
|
45
|
+
return { sent: false, reason: "check-the-form" };
|
|
46
|
+
console.error(error); // configuration or delivery: tell the visitor to write directly
|
|
47
|
+
return { sent: false, reason: "unavailable" };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`sender.verify()` logs in without sending, handy for a health check. `checkEmailMessage` and `checkEmailAddress` are exported for validating input before you get as far as sending.
|
|
53
|
+
|
|
54
|
+
## Development
|
|
55
|
+
|
|
56
|
+
Built on the Sharadox engineering standard: TypeScript in `source/` (one thing per folder: `index.ts`, `type.ts`, `unit-test.ts`, `benchmark-test.ts`), Turborepo, ESLint, Prettier, Vitest, TypeDoc, Husky.
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
pnpm install
|
|
60
|
+
pnpm verify # format, lint, types, unit and benchmark tests, build
|
|
61
|
+
pnpm documentation # API reference in documentation/
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Branches: `index` is released, `pre-production` gathers finished work, and each change lives on `<thing>.development`. Releases are a `v<version>` tag on `index`; the publish workflow releases to npm with provenance.
|
|
65
|
+
|
|
66
|
+
## License
|
|
67
|
+
|
|
68
|
+
[MIT](LICENSE) © Sharadox
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { EmailAddress } from "./type.ts";
|
|
2
|
+
/** Checks an address (and its display name) and returns it unchanged; throws `invalid-message` otherwise. */
|
|
3
|
+
export declare function checkEmailAddress(value: EmailAddress, field: string): EmailAddress;
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/email-address/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAM9C,6GAA6G;AAC7G,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CASlF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { EmailError } from "../email-error/index.js";
|
|
2
|
+
/** One address: a local part, an @, and a domain with at least one dot; no spaces, angle brackets or line breaks. */
|
|
3
|
+
const ADDRESS = /^[^\s@<>()[\]\\,;:"]+@[^\s@<>()[\]\\,;:"]+\.[^\s@<>()[\]\\,;:"]+$/;
|
|
4
|
+
const LINE_BREAK = /[\r\n]/;
|
|
5
|
+
/** Checks an address (and its display name) and returns it unchanged; throws `invalid-message` otherwise. */
|
|
6
|
+
export function checkEmailAddress(value, field) {
|
|
7
|
+
const address = typeof value === "string" ? value : value.address;
|
|
8
|
+
if (address.length > 254 || !ADDRESS.test(address)) {
|
|
9
|
+
throw new EmailError("invalid-message", `${field} is not a valid email address`);
|
|
10
|
+
}
|
|
11
|
+
if (typeof value !== "string" && (LINE_BREAK.test(value.name) || value.name.length > 200)) {
|
|
12
|
+
throw new EmailError("invalid-message", `${field} has an invalid display name`);
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../source/email-address/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAGrD,qHAAqH;AACrH,MAAM,OAAO,GAAG,mEAAmE,CAAC;AACpF,MAAM,UAAU,GAAG,QAAQ,CAAC;AAE5B,6GAA6G;AAC7G,MAAM,UAAU,iBAAiB,CAAC,KAAmB,EAAE,KAAa;IAClE,MAAM,OAAO,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;IAClE,IAAI,OAAO,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,UAAU,CAAC,iBAAiB,EAAE,GAAG,KAAK,+BAA+B,CAAC,CAAC;IACnF,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC;QAC1F,MAAM,IAAI,UAAU,CAAC,iBAAiB,EAAE,GAAG,KAAK,8BAA8B,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../source/email-address/type.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.js","sourceRoot":"","sources":["../../source/email-address/type.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EmailErrorCode } from "./type.ts";
|
|
2
|
+
/** The only error this library throws; `code` says whose problem it is. */
|
|
3
|
+
export declare class EmailError extends Error {
|
|
4
|
+
readonly code: EmailErrorCode;
|
|
5
|
+
constructor(code: EmailErrorCode, message: string, options?: {
|
|
6
|
+
cause?: unknown;
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/email-error/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,2EAA2E;AAC3E,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;gBAElB,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAKjF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** The only error this library throws; `code` says whose problem it is. */
|
|
2
|
+
export class EmailError extends Error {
|
|
3
|
+
code;
|
|
4
|
+
constructor(code, message, options) {
|
|
5
|
+
super(message, options);
|
|
6
|
+
this.name = "EmailError";
|
|
7
|
+
this.code = code;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../source/email-error/index.ts"],"names":[],"mappings":"AAEA,2EAA2E;AAC3E,MAAM,OAAO,UAAW,SAAQ,KAAK;IAC1B,IAAI,CAAiB;IAE9B,YAAY,IAAoB,EAAE,OAAe,EAAE,OAA6B;QAC9E,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why sending failed:
|
|
3
|
+
* - `configuration`: the email server settings are missing or invalid (fix the environment).
|
|
4
|
+
* - `invalid-message`: the message itself is wrong, for example a malformed address (fix the input).
|
|
5
|
+
* - `delivery`: the server could not be reached, refused the login or rejected the message (retry or alert).
|
|
6
|
+
*/
|
|
7
|
+
export type EmailErrorCode = "configuration" | "invalid-message" | "delivery";
|
|
8
|
+
//# sourceMappingURL=type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../source/email-error/type.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG,iBAAiB,GAAG,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.js","sourceRoot":"","sources":["../../source/email-error/type.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EmailMessage } from "./type.ts";
|
|
2
|
+
/** Longest subject accepted, in characters. */
|
|
3
|
+
export declare const SUBJECT_LIMIT = 300;
|
|
4
|
+
/** Most recipients accepted in one message. */
|
|
5
|
+
export declare const RECIPIENT_LIMIT = 50;
|
|
6
|
+
/**
|
|
7
|
+
* Checks a message before it reaches the server: valid addresses, a one-line subject (no header injection), at
|
|
8
|
+
* least one recipient and a body. Returns the message unchanged; throws `invalid-message` otherwise.
|
|
9
|
+
*/
|
|
10
|
+
export declare function checkEmailMessage(message: EmailMessage): EmailMessage;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/email-message/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,+CAA+C;AAC/C,eAAO,MAAM,aAAa,MAAM,CAAC;AACjC,+CAA+C;AAC/C,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,GAAG,YAAY,CAerE"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { checkEmailAddress } from "../email-address/index.js";
|
|
2
|
+
import { EmailError } from "../email-error/index.js";
|
|
3
|
+
/** Longest subject accepted, in characters. */
|
|
4
|
+
export const SUBJECT_LIMIT = 300;
|
|
5
|
+
/** Most recipients accepted in one message. */
|
|
6
|
+
export const RECIPIENT_LIMIT = 50;
|
|
7
|
+
/**
|
|
8
|
+
* Checks a message before it reaches the server: valid addresses, a one-line subject (no header injection), at
|
|
9
|
+
* least one recipient and a body. Returns the message unchanged; throws `invalid-message` otherwise.
|
|
10
|
+
*/
|
|
11
|
+
export function checkEmailMessage(message) {
|
|
12
|
+
checkEmailAddress(message.from, "from");
|
|
13
|
+
const recipients = Array.isArray(message.to) ? message.to : [message.to];
|
|
14
|
+
if (recipients.length === 0 || recipients.length > RECIPIENT_LIMIT) {
|
|
15
|
+
throw new EmailError("invalid-message", `to needs between 1 and ${RECIPIENT_LIMIT} recipients`);
|
|
16
|
+
}
|
|
17
|
+
recipients.forEach((recipient, index) => checkEmailAddress(recipient, `to[${index}]`));
|
|
18
|
+
if (message.replyTo !== undefined)
|
|
19
|
+
checkEmailAddress(message.replyTo, "replyTo");
|
|
20
|
+
if (!message.subject.trim() || /[\r\n]/.test(message.subject) || message.subject.length > SUBJECT_LIMIT) {
|
|
21
|
+
throw new EmailError("invalid-message", `subject must be one line of 1 to ${SUBJECT_LIMIT} characters`);
|
|
22
|
+
}
|
|
23
|
+
if (!message.text?.trim() && !message.html?.trim()) {
|
|
24
|
+
throw new EmailError("invalid-message", "the message needs a text or html body");
|
|
25
|
+
}
|
|
26
|
+
return message;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../source/email-message/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAGrD,+CAA+C;AAC/C,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AACjC,+CAA+C;AAC/C,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAElC;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAqB;IACrD,iBAAiB,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;QACnE,MAAM,IAAI,UAAU,CAAC,iBAAiB,EAAE,0BAA0B,eAAe,aAAa,CAAC,CAAC;IAClG,CAAC;IACD,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,EAAE,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC;IACvF,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS;QAAE,iBAAiB,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACjF,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,aAAa,EAAE,CAAC;QACxG,MAAM,IAAI,UAAU,CAAC,iBAAiB,EAAE,oCAAoC,aAAa,aAAa,CAAC,CAAC;IAC1G,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;QACnD,MAAM,IAAI,UAAU,CAAC,iBAAiB,EAAE,uCAAuC,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { EmailAddress } from "../email-address/type.ts";
|
|
2
|
+
/** An email to send. Give `text`, `html` or both (text is the fallback for clients that don't show html). */
|
|
3
|
+
export interface EmailMessage {
|
|
4
|
+
/** Sender. Most servers only accept the address you log in with (or an alias of it). */
|
|
5
|
+
from: EmailAddress;
|
|
6
|
+
/** One or more recipients. */
|
|
7
|
+
to: EmailAddress | EmailAddress[];
|
|
8
|
+
/** Where replies go, for example the visitor who filled a contact form. */
|
|
9
|
+
replyTo?: EmailAddress;
|
|
10
|
+
subject: string;
|
|
11
|
+
text?: string;
|
|
12
|
+
html?: string;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../source/email-message/type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,6GAA6G;AAC7G,MAAM,WAAW,YAAY;IAC3B,wFAAwF;IACxF,IAAI,EAAE,YAAY,CAAC;IACnB,8BAA8B;IAC9B,EAAE,EAAE,YAAY,GAAG,YAAY,EAAE,CAAC;IAClC,2EAA2E;IAC3E,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.js","sourceRoot":"","sources":["../../source/email-message/type.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EmailServerConfiguration } from "../email-server-configuration/type.ts";
|
|
2
|
+
import type { EmailSender } from "./type.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a sender for one SMTP server. Each `send` opens its own short connection, which suits serverless
|
|
5
|
+
* functions (nothing to close, nothing kept between invocations).
|
|
6
|
+
*
|
|
7
|
+
* The SMTP protocol work (STARTTLS, login, MIME, encoding of non-English text) is done by Nodemailer; this library
|
|
8
|
+
* adds the configuration, the message checks and one error type.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createEmailSender(configuration: EmailServerConfiguration): EmailSender;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/email-sender/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACtF,OAAO,KAAK,EAAgB,WAAW,EAAE,MAAM,WAAW,CAAC;AAI3D;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,wBAAwB,GAAG,WAAW,CAuDtF"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createTransport } from "nodemailer";
|
|
2
|
+
import { EmailError } from "../email-error/index.js";
|
|
3
|
+
import { checkEmailMessage } from "../email-message/index.js";
|
|
4
|
+
const addressText = (entry) => (typeof entry === "string" ? entry : entry.address);
|
|
5
|
+
/**
|
|
6
|
+
* Creates a sender for one SMTP server. Each `send` opens its own short connection, which suits serverless
|
|
7
|
+
* functions (nothing to close, nothing kept between invocations).
|
|
8
|
+
*
|
|
9
|
+
* The SMTP protocol work (STARTTLS, login, MIME, encoding of non-English text) is done by Nodemailer; this library
|
|
10
|
+
* adds the configuration, the message checks and one error type.
|
|
11
|
+
*/
|
|
12
|
+
export function createEmailSender(configuration) {
|
|
13
|
+
const security = configuration.security ?? (configuration.port === 465 ? "tls" : "starttls");
|
|
14
|
+
const timeout = configuration.timeout ?? 10_000;
|
|
15
|
+
const transport = createTransport({
|
|
16
|
+
host: configuration.host,
|
|
17
|
+
port: configuration.port,
|
|
18
|
+
secure: security === "tls",
|
|
19
|
+
requireTLS: security === "starttls",
|
|
20
|
+
ignoreTLS: security === "none",
|
|
21
|
+
auth: { user: configuration.username, pass: configuration.password },
|
|
22
|
+
connectionTimeout: timeout,
|
|
23
|
+
greetingTimeout: timeout,
|
|
24
|
+
socketTimeout: timeout * 2,
|
|
25
|
+
});
|
|
26
|
+
return {
|
|
27
|
+
async send(message) {
|
|
28
|
+
checkEmailMessage(message);
|
|
29
|
+
try {
|
|
30
|
+
const info = await transport.sendMail({
|
|
31
|
+
from: message.from,
|
|
32
|
+
to: message.to,
|
|
33
|
+
replyTo: message.replyTo,
|
|
34
|
+
subject: message.subject,
|
|
35
|
+
text: message.text,
|
|
36
|
+
html: message.html,
|
|
37
|
+
disableFileAccess: true,
|
|
38
|
+
disableUrlAccess: true,
|
|
39
|
+
});
|
|
40
|
+
const receipt = {
|
|
41
|
+
messageId: info.messageId,
|
|
42
|
+
accepted: info.accepted.map(addressText),
|
|
43
|
+
rejected: info.rejected.map(addressText),
|
|
44
|
+
};
|
|
45
|
+
if (receipt.accepted.length === 0) {
|
|
46
|
+
throw new EmailError("delivery", `the server refused every recipient (${receipt.rejected.join(", ")})`);
|
|
47
|
+
}
|
|
48
|
+
return receipt;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error instanceof EmailError)
|
|
52
|
+
throw error;
|
|
53
|
+
throw new EmailError("delivery", `sending through ${configuration.host} failed: ${describe(error)}`, {
|
|
54
|
+
cause: error,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
async verify() {
|
|
59
|
+
try {
|
|
60
|
+
await transport.verify();
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
throw new EmailError("delivery", `logging in to ${configuration.host} failed: ${describe(error)}`, {
|
|
64
|
+
cause: error,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** A short reason without the password or message content. */
|
|
71
|
+
function describe(error) {
|
|
72
|
+
const { code, responseCode, message } = (error ?? {});
|
|
73
|
+
return [code, responseCode, message?.split("\n")[0]].filter(Boolean).join(" ") || "unknown error";
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../source/email-sender/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { EmailMessage } from "../email-message/type.ts";
|
|
2
|
+
/** What the server said when it took the message. */
|
|
3
|
+
export interface EmailReceipt {
|
|
4
|
+
/** The Message-ID header, useful to find the message in logs. */
|
|
5
|
+
messageId: string;
|
|
6
|
+
/** Recipients the server accepted. */
|
|
7
|
+
accepted: string[];
|
|
8
|
+
/** Recipients the server refused (the message still went to the accepted ones). */
|
|
9
|
+
rejected: string[];
|
|
10
|
+
}
|
|
11
|
+
/** Sends email through one SMTP server. */
|
|
12
|
+
export interface EmailSender {
|
|
13
|
+
/** Checks the message, then sends it. Throws `EmailError` with code `invalid-message` or `delivery`. */
|
|
14
|
+
send(message: EmailMessage): Promise<EmailReceipt>;
|
|
15
|
+
/** Connects and logs in without sending, to check the configuration. Throws `EmailError` (`delivery`). */
|
|
16
|
+
verify(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../source/email-sender/type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,qDAAqD;AACrD,MAAM,WAAW,YAAY;IAC3B,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,mFAAmF;IACnF,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,2CAA2C;AAC3C,MAAM,WAAW,WAAW;IAC1B,wGAAwG;IACxG,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACnD,0GAA0G;IAC1G,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.js","sourceRoot":"","sources":["../../source/email-sender/type.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EmailServerConfiguration } from "./type.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Reads the email server settings from environment variables:
|
|
4
|
+
* `EMAIL_SERVER_HOST`, `EMAIL_SERVER_PORT` (default 587), `EMAIL_SERVER_USERNAME`, `EMAIL_SERVER_PASSWORD` and the
|
|
5
|
+
* optional `EMAIL_SERVER_SECURITY` (`starttls`, `tls` or `none`). Pass another `prefix` to keep several servers.
|
|
6
|
+
* Throws `configuration` naming every missing or invalid variable.
|
|
7
|
+
*/
|
|
8
|
+
export declare function readEmailServerConfiguration(environment?: Record<string, string | undefined>, prefix?: string): EmailServerConfiguration;
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/email-server-configuration/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAuB,MAAM,WAAW,CAAC;AAI/E;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,WAAW,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,EAC7D,MAAM,SAAkB,GACvB,wBAAwB,CAwB1B"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { EmailError } from "../email-error/index.js";
|
|
2
|
+
const SECURITIES = ["starttls", "tls", "none"];
|
|
3
|
+
/**
|
|
4
|
+
* Reads the email server settings from environment variables:
|
|
5
|
+
* `EMAIL_SERVER_HOST`, `EMAIL_SERVER_PORT` (default 587), `EMAIL_SERVER_USERNAME`, `EMAIL_SERVER_PASSWORD` and the
|
|
6
|
+
* optional `EMAIL_SERVER_SECURITY` (`starttls`, `tls` or `none`). Pass another `prefix` to keep several servers.
|
|
7
|
+
* Throws `configuration` naming every missing or invalid variable.
|
|
8
|
+
*/
|
|
9
|
+
export function readEmailServerConfiguration(environment = process.env, prefix = "EMAIL_SERVER_") {
|
|
10
|
+
const read = (name) => environment[`${prefix}${name}`]?.trim() || undefined;
|
|
11
|
+
const host = read("HOST");
|
|
12
|
+
const username = read("USERNAME");
|
|
13
|
+
const password = read("PASSWORD");
|
|
14
|
+
const port = Number(read("PORT") ?? 587);
|
|
15
|
+
const security = read("SECURITY");
|
|
16
|
+
const problems = [
|
|
17
|
+
...["HOST", "USERNAME", "PASSWORD"]
|
|
18
|
+
.filter((name) => !read(name))
|
|
19
|
+
.map((name) => `${prefix}${name} is missing`),
|
|
20
|
+
...(Number.isInteger(port) && port > 0 && port < 65536 ? [] : [`${prefix}PORT must be a port number`]),
|
|
21
|
+
...(security === undefined || SECURITIES.includes(security)
|
|
22
|
+
? []
|
|
23
|
+
: [`${prefix}SECURITY must be one of ${SECURITIES.join(", ")}`]),
|
|
24
|
+
];
|
|
25
|
+
if (problems.length)
|
|
26
|
+
throw new EmailError("configuration", problems.join("; "));
|
|
27
|
+
return {
|
|
28
|
+
host: host,
|
|
29
|
+
port,
|
|
30
|
+
username: username,
|
|
31
|
+
password: password,
|
|
32
|
+
...(security ? { security: security } : {}),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../source/email-server-configuration/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAGrD,MAAM,UAAU,GAAmC,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;AAE/E;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B,CAC1C,cAAkD,OAAO,CAAC,GAAG,EAC7D,MAAM,GAAG,eAAe;IAExB,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,MAAM,GAAG,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;IACpF,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG;QACf,GAAI,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,CAAW;aAC3C,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aAC7B,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,MAAM,GAAG,IAAI,aAAa,CAAC;QAC/C,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,4BAA4B,CAAC,CAAC;QACtG,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,UAAU,CAAC,QAAQ,CAAC,QAA+B,CAAC;YAChF,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,CAAC,GAAG,MAAM,2BAA2B,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;KACnE,CAAC;IACF,IAAI,QAAQ,CAAC,MAAM;QAAE,MAAM,IAAI,UAAU,CAAC,eAAe,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAChF,OAAO;QACL,IAAI,EAAE,IAAK;QACX,IAAI;QACJ,QAAQ,EAAE,QAAS;QACnB,QAAQ,EAAE,QAAS;QACnB,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAA+B,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How the connection is protected:
|
|
3
|
+
* - `starttls`: connect in plain text, then upgrade with STARTTLS, which is required (port 587, the default).
|
|
4
|
+
* - `tls`: TLS from the first byte (port 465).
|
|
5
|
+
* - `none`: no encryption, only for a local test server.
|
|
6
|
+
*/
|
|
7
|
+
export type EmailServerSecurity = "starttls" | "tls" | "none";
|
|
8
|
+
/** Where and how to log in to the SMTP server that sends the email. */
|
|
9
|
+
export interface EmailServerConfiguration {
|
|
10
|
+
host: string;
|
|
11
|
+
port: number;
|
|
12
|
+
username: string;
|
|
13
|
+
password: string;
|
|
14
|
+
/** Defaults to `tls` on port 465 and `starttls` everywhere else. */
|
|
15
|
+
security?: EmailServerSecurity;
|
|
16
|
+
/** Milliseconds to wait for the server before failing with `delivery`. Defaults to 10 000. */
|
|
17
|
+
timeout?: number;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../source/email-server-configuration/type.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,KAAK,GAAG,MAAM,CAAC;AAE9D,uEAAuE;AACvE,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type.js","sourceRoot":"","sources":["../../source/email-server-configuration/type.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Send email from web applications through any SMTP server.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* import { createEmailSender, readEmailServerConfiguration } from "@sharadox/email-web-library";
|
|
6
|
+
*
|
|
7
|
+
* const sender = createEmailSender(readEmailServerConfiguration());
|
|
8
|
+
* await sender.send({ from: "website@example.com", to: "inbox@example.com", subject: "Hello", text: "Hi!" });
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*/
|
|
13
|
+
export { checkEmailAddress } from "./email-address/index.ts";
|
|
14
|
+
export type { EmailAddress } from "./email-address/type.ts";
|
|
15
|
+
export { EmailError } from "./email-error/index.ts";
|
|
16
|
+
export type { EmailErrorCode } from "./email-error/type.ts";
|
|
17
|
+
export { checkEmailMessage, RECIPIENT_LIMIT, SUBJECT_LIMIT } from "./email-message/index.ts";
|
|
18
|
+
export type { EmailMessage } from "./email-message/type.ts";
|
|
19
|
+
export { createEmailSender } from "./email-sender/index.ts";
|
|
20
|
+
export type { EmailReceipt, EmailSender } from "./email-sender/type.ts";
|
|
21
|
+
export { readEmailServerConfiguration } from "./email-server-configuration/index.ts";
|
|
22
|
+
export type { EmailServerConfiguration, EmailServerSecurity } from "./email-server-configuration/type.ts";
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../source/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC7F,YAAY,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC;AACrF,YAAY,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Send email from web applications through any SMTP server.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* import { createEmailSender, readEmailServerConfiguration } from "@sharadox/email-web-library";
|
|
6
|
+
*
|
|
7
|
+
* const sender = createEmailSender(readEmailServerConfiguration());
|
|
8
|
+
* await sender.send({ from: "website@example.com", to: "inbox@example.com", subject: "Hello", text: "Hi!" });
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*/
|
|
13
|
+
export { checkEmailAddress } from "./email-address/index.js";
|
|
14
|
+
export { EmailError } from "./email-error/index.js";
|
|
15
|
+
export { checkEmailMessage, RECIPIENT_LIMIT, SUBJECT_LIMIT } from "./email-message/index.js";
|
|
16
|
+
export { createEmailSender } from "./email-sender/index.js";
|
|
17
|
+
export { readEmailServerConfiguration } from "./email-server-configuration/index.js";
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../source/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE7F,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,OAAO,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,76 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sharadox/email-web-library",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Send email from web applications through any SMTP server, with message checks and clear errors.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Sharadox <email@sharadox.com>",
|
|
7
|
+
"homepage": "https://github.com/Sharadox/email-web-library#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Sharadox/email-web-library.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": "https://github.com/Sharadox/email-web-library/issues",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"email",
|
|
15
|
+
"smtp",
|
|
16
|
+
"nextjs",
|
|
17
|
+
"server-actions",
|
|
18
|
+
"contact-form"
|
|
19
|
+
],
|
|
20
|
+
"type": "module",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./distribution/index.d.ts",
|
|
24
|
+
"default": "./distribution/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"distribution"
|
|
29
|
+
],
|
|
30
|
+
"sideEffects": false,
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=20"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public",
|
|
36
|
+
"provenance": true
|
|
37
|
+
},
|
|
38
|
+
"lint-staged": {
|
|
39
|
+
"*.{ts,js,mjs,cjs}": [
|
|
40
|
+
"prettier --write",
|
|
41
|
+
"eslint --fix --max-warnings=0"
|
|
42
|
+
],
|
|
43
|
+
"*.{json,md,yml,yaml}": "prettier --write"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"nodemailer": "^8.0.11"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@eslint/js": "^9.39.5",
|
|
50
|
+
"@types/node": "^24.19.2",
|
|
51
|
+
"@types/nodemailer": "^8.0.2",
|
|
52
|
+
"@types/smtp-server": "^3.5.13",
|
|
53
|
+
"eslint": "9.39.5",
|
|
54
|
+
"eslint-config-prettier": "^10.1.8",
|
|
55
|
+
"husky": "^9.1.7",
|
|
56
|
+
"lint-staged": "^17.6.0",
|
|
57
|
+
"prettier": "^3.9.10",
|
|
58
|
+
"smtp-server": "^3.21.3",
|
|
59
|
+
"turbo": "^2.11.10",
|
|
60
|
+
"typedoc": "^0.28.20",
|
|
61
|
+
"typescript": "6.0.3",
|
|
62
|
+
"typescript-eslint": "^8.72.1",
|
|
63
|
+
"vitest": "^5.1.0"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "tsc -p tsconfig.build.json",
|
|
67
|
+
"lint": "eslint --max-warnings=0 .",
|
|
68
|
+
"format": "prettier --write .",
|
|
69
|
+
"format:check": "prettier --check .",
|
|
70
|
+
"type-check": "tsc --noEmit",
|
|
71
|
+
"test:unit": "vitest run",
|
|
72
|
+
"test:benchmark": "vitest bench --run --passWithNoTests",
|
|
73
|
+
"documentation": "typedoc",
|
|
74
|
+
"verify": "turbo run format:check lint type-check test:unit test:benchmark build"
|
|
75
|
+
}
|
|
6
76
|
}
|