@upyo/core 0.1.0-dev.10

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/README.md ADDED
@@ -0,0 +1,19 @@
1
+ <!-- deno-fmt-ignore-file -->
2
+
3
+ @upyo/core
4
+ ==========
5
+
6
+ Shared types and interfaces for Upyo, a simple and cross-runtime library for
7
+ sending email messages.
8
+
9
+
10
+ Installation
11
+ ------------
12
+
13
+ ~~~~ sh
14
+ npm add @upyo/core
15
+ pnpm add @upyo/core
16
+ yarn add @upyo/core
17
+ deno add jsr:@upyo/core
18
+ bun add @upyo/core
19
+ ~~~~
@@ -0,0 +1,134 @@
1
+
2
+ //#region src/address.ts
3
+ /**
4
+ * Formats an address object into a string representation. This function is
5
+ * an inverse of the {@link parseAddress} function.
6
+ *
7
+ * @example Formatting an address with a name
8
+ * ```ts
9
+ * import { type Address, formatAddress } from "@upyo/core/address";
10
+ * const address: Address = { name: "John Doe", address: "john@example.com" };
11
+ * console.log(formatAddress(address)); // "John Doe <john@example.com>"
12
+ * ```
13
+ *
14
+ * @example Formatting an address without a name
15
+ * ```ts
16
+ * import { type Address, formatAddress } from "@upyo/core/address";
17
+ * const address: Address = { address: "jane@examle.com" };
18
+ * console.log(formatAddress(address)); // "jane@example.com"
19
+ * ```
20
+ *
21
+ * @param address The address object to format.
22
+ * @return A string representation of the address.
23
+ */
24
+ function formatAddress(address) {
25
+ return address.name == null ? address.address : `${address.name} <${address.address}>`;
26
+ }
27
+ /**
28
+ * Parses a string representation of an email address into an {@link Address}
29
+ * object. This function is an inverse of the {@link formatAddress} function.
30
+ *
31
+ * @example Parsing an address with a name
32
+ * ```ts
33
+ * import { parseAddress } from "@upyo/core/address";
34
+ * const address = parseAddress("John Doe <john@example.com>");
35
+ * console.log(address); // { name: "John Doe", address: "john@example.com" }
36
+ * ```
37
+ *
38
+ * @example Parsing an address without a name
39
+ * ```ts
40
+ * import { parseAddress } from "@upyo/core/address";
41
+ * const address = parseAddress("jane@example.com");
42
+ * console.log(address); // { address: "jane@example.com" }
43
+ * ```
44
+ *
45
+ * @example Trying to parse an invalid address
46
+ * ```ts
47
+ * import { parseAddress } from "@upyo/core/address";
48
+ * const address = parseAddress("invalid-email");
49
+ * console.log(address); // undefined
50
+ * ```
51
+ *
52
+ * @param address The string representation of the address to parse.
53
+ * @returns An {@link Address} object if the parsing is successful,
54
+ * or `undefined` if the input is invalid.
55
+ */
56
+ function parseAddress(address) {
57
+ if (!address || typeof address !== "string") return void 0;
58
+ const trimmed = address.trim();
59
+ if (!trimmed) return void 0;
60
+ const nameAngleBracketMatch = trimmed.match(/^(.+?)\s*<(.+?)>$/);
61
+ if (nameAngleBracketMatch) {
62
+ const name = nameAngleBracketMatch[1].trim();
63
+ const email = nameAngleBracketMatch[2].trim();
64
+ if (!isValidEmail(email)) return void 0;
65
+ const cleanName = name.replace(/^"(.+)"$/, "$1");
66
+ return {
67
+ name: cleanName,
68
+ address: email
69
+ };
70
+ }
71
+ const angleBracketMatch = trimmed.match(/^<(.+?)>$/);
72
+ if (angleBracketMatch) {
73
+ const email = angleBracketMatch[1].trim();
74
+ if (!isValidEmail(email)) return void 0;
75
+ return { address: email };
76
+ }
77
+ if (isValidEmail(trimmed)) return { address: trimmed };
78
+ return void 0;
79
+ }
80
+ function isValidEmail(email) {
81
+ if (!email || typeof email !== "string") return false;
82
+ let atIndex = -1;
83
+ let inQuotes = false;
84
+ for (let i = 0; i < email.length; i++) {
85
+ const char = email[i];
86
+ if (char === "\"" && (i === 0 || email[i - 1] !== "\\")) inQuotes = !inQuotes;
87
+ else if (char === "@" && !inQuotes) if (atIndex === -1) atIndex = i;
88
+ else return false;
89
+ }
90
+ if (atIndex === -1) return false;
91
+ const localPart = email.substring(0, atIndex);
92
+ const domainPart = email.substring(atIndex + 1);
93
+ return isValidLocalPart(localPart) && isValidDomainPart(domainPart);
94
+ }
95
+ function isValidLocalPart(localPart) {
96
+ if (!localPart || localPart.length === 0 || localPart.length > 64) return false;
97
+ if (localPart.startsWith("\"") && localPart.endsWith("\"")) {
98
+ const quotedContent = localPart.slice(1, -1);
99
+ let isValid = true;
100
+ for (let i = 0; i < quotedContent.length; i++) {
101
+ const char = quotedContent[i];
102
+ if (char === "\"" || char === "\r" || char === "\n") {
103
+ if (i === 0 || quotedContent[i - 1] !== "\\") {
104
+ isValid = false;
105
+ break;
106
+ }
107
+ }
108
+ }
109
+ return isValid;
110
+ }
111
+ if (localPart.startsWith(".") || localPart.endsWith(".") || localPart.includes("..")) return false;
112
+ const validLocalPartRegex = /^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*$/;
113
+ return validLocalPartRegex.test(localPart);
114
+ }
115
+ function isValidDomainPart(domainPart) {
116
+ if (!domainPart || domainPart.length === 0 || domainPart.length > 253) return false;
117
+ if (domainPart.startsWith("[") && domainPart.endsWith("]")) {
118
+ const literal = domainPart.slice(1, -1);
119
+ try {
120
+ return URL.canParse(`http://${literal}/`);
121
+ } catch {
122
+ return false;
123
+ }
124
+ }
125
+ try {
126
+ return URL.canParse(`http://${domainPart}/`);
127
+ } catch {
128
+ return false;
129
+ }
130
+ }
131
+
132
+ //#endregion
133
+ exports.formatAddress = formatAddress;
134
+ exports.parseAddress = parseAddress;
@@ -0,0 +1,68 @@
1
+ //#region src/address.d.ts
2
+ /**
3
+ * A pair of name (which is optional) and email address.
4
+ */
5
+ interface Address {
6
+ /**
7
+ * The name of the person or entity associated with the email address.
8
+ */
9
+ readonly name?: string;
10
+ /**
11
+ * The email address itself.
12
+ */
13
+ readonly address: string;
14
+ }
15
+ /**
16
+ * Formats an address object into a string representation. This function is
17
+ * an inverse of the {@link parseAddress} function.
18
+ *
19
+ * @example Formatting an address with a name
20
+ * ```ts
21
+ * import { type Address, formatAddress } from "@upyo/core/address";
22
+ * const address: Address = { name: "John Doe", address: "john@example.com" };
23
+ * console.log(formatAddress(address)); // "John Doe <john@example.com>"
24
+ * ```
25
+ *
26
+ * @example Formatting an address without a name
27
+ * ```ts
28
+ * import { type Address, formatAddress } from "@upyo/core/address";
29
+ * const address: Address = { address: "jane@examle.com" };
30
+ * console.log(formatAddress(address)); // "jane@example.com"
31
+ * ```
32
+ *
33
+ * @param address The address object to format.
34
+ * @return A string representation of the address.
35
+ */
36
+ declare function formatAddress(address: Address): string;
37
+ /**
38
+ * Parses a string representation of an email address into an {@link Address}
39
+ * object. This function is an inverse of the {@link formatAddress} function.
40
+ *
41
+ * @example Parsing an address with a name
42
+ * ```ts
43
+ * import { parseAddress } from "@upyo/core/address";
44
+ * const address = parseAddress("John Doe <john@example.com>");
45
+ * console.log(address); // { name: "John Doe", address: "john@example.com" }
46
+ * ```
47
+ *
48
+ * @example Parsing an address without a name
49
+ * ```ts
50
+ * import { parseAddress } from "@upyo/core/address";
51
+ * const address = parseAddress("jane@example.com");
52
+ * console.log(address); // { address: "jane@example.com" }
53
+ * ```
54
+ *
55
+ * @example Trying to parse an invalid address
56
+ * ```ts
57
+ * import { parseAddress } from "@upyo/core/address";
58
+ * const address = parseAddress("invalid-email");
59
+ * console.log(address); // undefined
60
+ * ```
61
+ *
62
+ * @param address The string representation of the address to parse.
63
+ * @returns An {@link Address} object if the parsing is successful,
64
+ * or `undefined` if the input is invalid.
65
+ */
66
+ declare function parseAddress(address: string): Address | undefined;
67
+ //#endregion
68
+ export { Address, formatAddress, parseAddress };
@@ -0,0 +1,68 @@
1
+ //#region src/address.d.ts
2
+ /**
3
+ * A pair of name (which is optional) and email address.
4
+ */
5
+ interface Address {
6
+ /**
7
+ * The name of the person or entity associated with the email address.
8
+ */
9
+ readonly name?: string;
10
+ /**
11
+ * The email address itself.
12
+ */
13
+ readonly address: string;
14
+ }
15
+ /**
16
+ * Formats an address object into a string representation. This function is
17
+ * an inverse of the {@link parseAddress} function.
18
+ *
19
+ * @example Formatting an address with a name
20
+ * ```ts
21
+ * import { type Address, formatAddress } from "@upyo/core/address";
22
+ * const address: Address = { name: "John Doe", address: "john@example.com" };
23
+ * console.log(formatAddress(address)); // "John Doe <john@example.com>"
24
+ * ```
25
+ *
26
+ * @example Formatting an address without a name
27
+ * ```ts
28
+ * import { type Address, formatAddress } from "@upyo/core/address";
29
+ * const address: Address = { address: "jane@examle.com" };
30
+ * console.log(formatAddress(address)); // "jane@example.com"
31
+ * ```
32
+ *
33
+ * @param address The address object to format.
34
+ * @return A string representation of the address.
35
+ */
36
+ declare function formatAddress(address: Address): string;
37
+ /**
38
+ * Parses a string representation of an email address into an {@link Address}
39
+ * object. This function is an inverse of the {@link formatAddress} function.
40
+ *
41
+ * @example Parsing an address with a name
42
+ * ```ts
43
+ * import { parseAddress } from "@upyo/core/address";
44
+ * const address = parseAddress("John Doe <john@example.com>");
45
+ * console.log(address); // { name: "John Doe", address: "john@example.com" }
46
+ * ```
47
+ *
48
+ * @example Parsing an address without a name
49
+ * ```ts
50
+ * import { parseAddress } from "@upyo/core/address";
51
+ * const address = parseAddress("jane@example.com");
52
+ * console.log(address); // { address: "jane@example.com" }
53
+ * ```
54
+ *
55
+ * @example Trying to parse an invalid address
56
+ * ```ts
57
+ * import { parseAddress } from "@upyo/core/address";
58
+ * const address = parseAddress("invalid-email");
59
+ * console.log(address); // undefined
60
+ * ```
61
+ *
62
+ * @param address The string representation of the address to parse.
63
+ * @returns An {@link Address} object if the parsing is successful,
64
+ * or `undefined` if the input is invalid.
65
+ */
66
+ declare function parseAddress(address: string): Address | undefined;
67
+ //#endregion
68
+ export { Address, formatAddress, parseAddress };
@@ -0,0 +1,132 @@
1
+ //#region src/address.ts
2
+ /**
3
+ * Formats an address object into a string representation. This function is
4
+ * an inverse of the {@link parseAddress} function.
5
+ *
6
+ * @example Formatting an address with a name
7
+ * ```ts
8
+ * import { type Address, formatAddress } from "@upyo/core/address";
9
+ * const address: Address = { name: "John Doe", address: "john@example.com" };
10
+ * console.log(formatAddress(address)); // "John Doe <john@example.com>"
11
+ * ```
12
+ *
13
+ * @example Formatting an address without a name
14
+ * ```ts
15
+ * import { type Address, formatAddress } from "@upyo/core/address";
16
+ * const address: Address = { address: "jane@examle.com" };
17
+ * console.log(formatAddress(address)); // "jane@example.com"
18
+ * ```
19
+ *
20
+ * @param address The address object to format.
21
+ * @return A string representation of the address.
22
+ */
23
+ function formatAddress(address) {
24
+ return address.name == null ? address.address : `${address.name} <${address.address}>`;
25
+ }
26
+ /**
27
+ * Parses a string representation of an email address into an {@link Address}
28
+ * object. This function is an inverse of the {@link formatAddress} function.
29
+ *
30
+ * @example Parsing an address with a name
31
+ * ```ts
32
+ * import { parseAddress } from "@upyo/core/address";
33
+ * const address = parseAddress("John Doe <john@example.com>");
34
+ * console.log(address); // { name: "John Doe", address: "john@example.com" }
35
+ * ```
36
+ *
37
+ * @example Parsing an address without a name
38
+ * ```ts
39
+ * import { parseAddress } from "@upyo/core/address";
40
+ * const address = parseAddress("jane@example.com");
41
+ * console.log(address); // { address: "jane@example.com" }
42
+ * ```
43
+ *
44
+ * @example Trying to parse an invalid address
45
+ * ```ts
46
+ * import { parseAddress } from "@upyo/core/address";
47
+ * const address = parseAddress("invalid-email");
48
+ * console.log(address); // undefined
49
+ * ```
50
+ *
51
+ * @param address The string representation of the address to parse.
52
+ * @returns An {@link Address} object if the parsing is successful,
53
+ * or `undefined` if the input is invalid.
54
+ */
55
+ function parseAddress(address) {
56
+ if (!address || typeof address !== "string") return void 0;
57
+ const trimmed = address.trim();
58
+ if (!trimmed) return void 0;
59
+ const nameAngleBracketMatch = trimmed.match(/^(.+?)\s*<(.+?)>$/);
60
+ if (nameAngleBracketMatch) {
61
+ const name = nameAngleBracketMatch[1].trim();
62
+ const email = nameAngleBracketMatch[2].trim();
63
+ if (!isValidEmail(email)) return void 0;
64
+ const cleanName = name.replace(/^"(.+)"$/, "$1");
65
+ return {
66
+ name: cleanName,
67
+ address: email
68
+ };
69
+ }
70
+ const angleBracketMatch = trimmed.match(/^<(.+?)>$/);
71
+ if (angleBracketMatch) {
72
+ const email = angleBracketMatch[1].trim();
73
+ if (!isValidEmail(email)) return void 0;
74
+ return { address: email };
75
+ }
76
+ if (isValidEmail(trimmed)) return { address: trimmed };
77
+ return void 0;
78
+ }
79
+ function isValidEmail(email) {
80
+ if (!email || typeof email !== "string") return false;
81
+ let atIndex = -1;
82
+ let inQuotes = false;
83
+ for (let i = 0; i < email.length; i++) {
84
+ const char = email[i];
85
+ if (char === "\"" && (i === 0 || email[i - 1] !== "\\")) inQuotes = !inQuotes;
86
+ else if (char === "@" && !inQuotes) if (atIndex === -1) atIndex = i;
87
+ else return false;
88
+ }
89
+ if (atIndex === -1) return false;
90
+ const localPart = email.substring(0, atIndex);
91
+ const domainPart = email.substring(atIndex + 1);
92
+ return isValidLocalPart(localPart) && isValidDomainPart(domainPart);
93
+ }
94
+ function isValidLocalPart(localPart) {
95
+ if (!localPart || localPart.length === 0 || localPart.length > 64) return false;
96
+ if (localPart.startsWith("\"") && localPart.endsWith("\"")) {
97
+ const quotedContent = localPart.slice(1, -1);
98
+ let isValid = true;
99
+ for (let i = 0; i < quotedContent.length; i++) {
100
+ const char = quotedContent[i];
101
+ if (char === "\"" || char === "\r" || char === "\n") {
102
+ if (i === 0 || quotedContent[i - 1] !== "\\") {
103
+ isValid = false;
104
+ break;
105
+ }
106
+ }
107
+ }
108
+ return isValid;
109
+ }
110
+ if (localPart.startsWith(".") || localPart.endsWith(".") || localPart.includes("..")) return false;
111
+ const validLocalPartRegex = /^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*$/;
112
+ return validLocalPartRegex.test(localPart);
113
+ }
114
+ function isValidDomainPart(domainPart) {
115
+ if (!domainPart || domainPart.length === 0 || domainPart.length > 253) return false;
116
+ if (domainPart.startsWith("[") && domainPart.endsWith("]")) {
117
+ const literal = domainPart.slice(1, -1);
118
+ try {
119
+ return URL.canParse(`http://${literal}/`);
120
+ } catch {
121
+ return false;
122
+ }
123
+ }
124
+ try {
125
+ return URL.canParse(`http://${domainPart}/`);
126
+ } catch {
127
+ return false;
128
+ }
129
+ }
130
+
131
+ //#endregion
132
+ export { formatAddress, parseAddress };
File without changes
@@ -0,0 +1,31 @@
1
+ //#region src/attachment.d.ts
2
+ /**
3
+ * Represents an attachment in an email message.
4
+ */
5
+ interface Attachment {
6
+ /**
7
+ * Whether the attachment is intended to be used for inline images.
8
+ */
9
+ readonly inline: boolean;
10
+ /**
11
+ * The filename of the attachment, which is used for display purposes
12
+ * and may not be the actual name of the file on disk.
13
+ */
14
+ readonly filename: string;
15
+ /**
16
+ * The content of the attachment as a byte array.
17
+ */
18
+ readonly content: Uint8Array;
19
+ /**
20
+ * The media type of the attachment, which indicates the type of content
21
+ * and how it should be handled by email clients.
22
+ */
23
+ readonly contentType: `${string}/${string}`;
24
+ /**
25
+ * The content ID of the attachment, which is used to reference
26
+ * inline images in HTML emails.
27
+ */
28
+ readonly contentId: string;
29
+ }
30
+ //#endregion
31
+ export { Attachment };
@@ -0,0 +1,31 @@
1
+ //#region src/attachment.d.ts
2
+ /**
3
+ * Represents an attachment in an email message.
4
+ */
5
+ interface Attachment {
6
+ /**
7
+ * Whether the attachment is intended to be used for inline images.
8
+ */
9
+ readonly inline: boolean;
10
+ /**
11
+ * The filename of the attachment, which is used for display purposes
12
+ * and may not be the actual name of the file on disk.
13
+ */
14
+ readonly filename: string;
15
+ /**
16
+ * The content of the attachment as a byte array.
17
+ */
18
+ readonly content: Uint8Array;
19
+ /**
20
+ * The media type of the attachment, which indicates the type of content
21
+ * and how it should be handled by email clients.
22
+ */
23
+ readonly contentType: `${string}/${string}`;
24
+ /**
25
+ * The content ID of the attachment, which is used to reference
26
+ * inline images in HTML emails.
27
+ */
28
+ readonly contentId: string;
29
+ }
30
+ //#endregion
31
+ export { Attachment };
File without changes
package/dist/index.cjs ADDED
@@ -0,0 +1,6 @@
1
+ const require_address = require('./address.cjs');
2
+ const require_priority = require('./priority.cjs');
3
+
4
+ exports.comparePriority = require_priority.comparePriority;
5
+ exports.formatAddress = require_address.formatAddress;
6
+ exports.parseAddress = require_address.parseAddress;
@@ -0,0 +1,7 @@
1
+ import { Address, formatAddress, parseAddress } from "./address.cjs";
2
+ import { Attachment } from "./attachment.cjs";
3
+ import { Priority, comparePriority } from "./priority.cjs";
4
+ import { ImmutableHeaders, Message, MessageContent } from "./message.cjs";
5
+ import { Receipt } from "./receipt.cjs";
6
+ import { Transport, TransportOptions } from "./transport.cjs";
7
+ export { Address, Attachment, ImmutableHeaders, Message, MessageContent, Priority, Receipt, Transport, TransportOptions, comparePriority, formatAddress, parseAddress };
@@ -0,0 +1,7 @@
1
+ import { Address, formatAddress, parseAddress } from "./address.js";
2
+ import { Attachment } from "./attachment.js";
3
+ import { Priority, comparePriority } from "./priority.js";
4
+ import { ImmutableHeaders, Message, MessageContent } from "./message.js";
5
+ import { Receipt } from "./receipt.js";
6
+ import { Transport, TransportOptions } from "./transport.js";
7
+ export { Address, Attachment, ImmutableHeaders, Message, MessageContent, Priority, Receipt, Transport, TransportOptions, comparePriority, formatAddress, parseAddress };
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { formatAddress, parseAddress } from "./address.js";
2
+ import { comparePriority } from "./priority.js";
3
+
4
+ export { comparePriority, formatAddress, parseAddress };
File without changes
@@ -0,0 +1,111 @@
1
+ import { Address } from "./address.cjs";
2
+ import { Attachment } from "./attachment.cjs";
3
+ import { Priority } from "./priority.cjs";
4
+
5
+ //#region src/message.d.ts
6
+
7
+ /**
8
+ * Represents an email message with various properties such as
9
+ * sender, recipients, subject, content, and attachments.
10
+ */
11
+ interface Message {
12
+ /**
13
+ * The email address of the sender of the message.
14
+ */
15
+ readonly sender: Address;
16
+ /**
17
+ * The email addresses of the recipient of the message.
18
+ */
19
+ readonly recipients: Address[];
20
+ /**
21
+ * The email addresses of the carbon copy (CC) recipients of the message.
22
+ */
23
+ readonly ccRecipients: Address[];
24
+ /**
25
+ * The email addresses of the blind carbon copy (BCC) recipients of the message.
26
+ */
27
+ readonly bccRecipients: Address[];
28
+ /**
29
+ * The email addresses of the reply-to recipients of the message.
30
+ */
31
+ readonly replyRecipients: Address[];
32
+ /**
33
+ * The attachments included in the email message. These are files that
34
+ * are sent along with the email, such as documents, images, or other
35
+ * media files. Each attachment is represented by an {@link Attachment}
36
+ * object, which contains information about the attachment such as its
37
+ * filename, content type, and content ID.
38
+ */
39
+ readonly attachments: Attachment[];
40
+ /**
41
+ * The subject of the email message. This is typically a brief summary
42
+ * of the content of the email, and is used to help the recipient identify
43
+ * the purpose of the message.
44
+ */
45
+ readonly subject: string;
46
+ /**
47
+ * The content of the email message, which can be either HTML or plain text.
48
+ * This property is represented by the {@link MessageContent} type, which
49
+ * includes both HTML and plain text content. The HTML content is typically
50
+ * used for rich formatting and layout, while the plain text content is
51
+ * used for simple text emails or for compatibility with email clients
52
+ * that do not support HTML.
53
+ */
54
+ readonly content: MessageContent;
55
+ /**
56
+ * The priority of the email message, which indicates its importance
57
+ * relative to other messages. The priority can be one of three
58
+ * levels: `"high"`, `"normal"`, or `"low"`. This is represented by
59
+ * the {@link Priority} type, which is a string literal type that
60
+ * allows only these three values.
61
+ */
62
+ readonly priority: Priority;
63
+ /**
64
+ * The tags associated with the email message.
65
+ */
66
+ readonly tags: string[];
67
+ /**
68
+ * The headers of the email message. This is represented by
69
+ * the {@link ImmutableHeaders} type, which is a supertype of
70
+ * the standard `Headers` class. The `ImmutableHeaders` type
71
+ * includes only the methods for reading the headers, such as `get`, `keys`,
72
+ * `has`, and `entries`, but does not include methods for modifying
73
+ * the headers, such as `append`, `delete`, or `set`.
74
+ */
75
+ readonly headers: ImmutableHeaders;
76
+ }
77
+ /**
78
+ * Represents the content of an email message, which can be either HTML
79
+ * or plain text. The `html` property is optional, and if it is
80
+ * provided, the `text` property may also be included for
81
+ * compatibility with email clients that do not support HTML.
82
+ */
83
+ type MessageContent = {
84
+ /**
85
+ * The HTML content of the email message. This is typically used
86
+ * for rich formatting and layout.
87
+ */
88
+ html: string;
89
+ /**
90
+ * The alternative plain text content of the email message. This is
91
+ * optional and may be included for compatibility with email
92
+ * clients that do not support HTML.
93
+ */
94
+ text?: string;
95
+ } | {
96
+ /**
97
+ * The plain text content of the email message. This is typically
98
+ * used for simple text emails.
99
+ */
100
+ text: string;
101
+ };
102
+ /**
103
+ * Represents the headers of an email message. This type is a supertype of
104
+ * the standard `Headers` class, which is used to manage HTTP headers.
105
+ * Note that this type does not include methods for modifying the headers,
106
+ * such as `append`, `delete`, or `set`. It is intended to be used for
107
+ * read-only access to the headers of an email message.
108
+ */
109
+ type ImmutableHeaders = Omit<Headers, "append" | "delete" | "set">;
110
+ //#endregion
111
+ export { ImmutableHeaders, Message, MessageContent };
@@ -0,0 +1,111 @@
1
+ import { Address } from "./address.js";
2
+ import { Attachment } from "./attachment.js";
3
+ import { Priority } from "./priority.js";
4
+
5
+ //#region src/message.d.ts
6
+
7
+ /**
8
+ * Represents an email message with various properties such as
9
+ * sender, recipients, subject, content, and attachments.
10
+ */
11
+ interface Message {
12
+ /**
13
+ * The email address of the sender of the message.
14
+ */
15
+ readonly sender: Address;
16
+ /**
17
+ * The email addresses of the recipient of the message.
18
+ */
19
+ readonly recipients: Address[];
20
+ /**
21
+ * The email addresses of the carbon copy (CC) recipients of the message.
22
+ */
23
+ readonly ccRecipients: Address[];
24
+ /**
25
+ * The email addresses of the blind carbon copy (BCC) recipients of the message.
26
+ */
27
+ readonly bccRecipients: Address[];
28
+ /**
29
+ * The email addresses of the reply-to recipients of the message.
30
+ */
31
+ readonly replyRecipients: Address[];
32
+ /**
33
+ * The attachments included in the email message. These are files that
34
+ * are sent along with the email, such as documents, images, or other
35
+ * media files. Each attachment is represented by an {@link Attachment}
36
+ * object, which contains information about the attachment such as its
37
+ * filename, content type, and content ID.
38
+ */
39
+ readonly attachments: Attachment[];
40
+ /**
41
+ * The subject of the email message. This is typically a brief summary
42
+ * of the content of the email, and is used to help the recipient identify
43
+ * the purpose of the message.
44
+ */
45
+ readonly subject: string;
46
+ /**
47
+ * The content of the email message, which can be either HTML or plain text.
48
+ * This property is represented by the {@link MessageContent} type, which
49
+ * includes both HTML and plain text content. The HTML content is typically
50
+ * used for rich formatting and layout, while the plain text content is
51
+ * used for simple text emails or for compatibility with email clients
52
+ * that do not support HTML.
53
+ */
54
+ readonly content: MessageContent;
55
+ /**
56
+ * The priority of the email message, which indicates its importance
57
+ * relative to other messages. The priority can be one of three
58
+ * levels: `"high"`, `"normal"`, or `"low"`. This is represented by
59
+ * the {@link Priority} type, which is a string literal type that
60
+ * allows only these three values.
61
+ */
62
+ readonly priority: Priority;
63
+ /**
64
+ * The tags associated with the email message.
65
+ */
66
+ readonly tags: string[];
67
+ /**
68
+ * The headers of the email message. This is represented by
69
+ * the {@link ImmutableHeaders} type, which is a supertype of
70
+ * the standard `Headers` class. The `ImmutableHeaders` type
71
+ * includes only the methods for reading the headers, such as `get`, `keys`,
72
+ * `has`, and `entries`, but does not include methods for modifying
73
+ * the headers, such as `append`, `delete`, or `set`.
74
+ */
75
+ readonly headers: ImmutableHeaders;
76
+ }
77
+ /**
78
+ * Represents the content of an email message, which can be either HTML
79
+ * or plain text. The `html` property is optional, and if it is
80
+ * provided, the `text` property may also be included for
81
+ * compatibility with email clients that do not support HTML.
82
+ */
83
+ type MessageContent = {
84
+ /**
85
+ * The HTML content of the email message. This is typically used
86
+ * for rich formatting and layout.
87
+ */
88
+ html: string;
89
+ /**
90
+ * The alternative plain text content of the email message. This is
91
+ * optional and may be included for compatibility with email
92
+ * clients that do not support HTML.
93
+ */
94
+ text?: string;
95
+ } | {
96
+ /**
97
+ * The plain text content of the email message. This is typically
98
+ * used for simple text emails.
99
+ */
100
+ text: string;
101
+ };
102
+ /**
103
+ * Represents the headers of an email message. This type is a supertype of
104
+ * the standard `Headers` class, which is used to manage HTTP headers.
105
+ * Note that this type does not include methods for modifying the headers,
106
+ * such as `append`, `delete`, or `set`. It is intended to be used for
107
+ * read-only access to the headers of an email message.
108
+ */
109
+ type ImmutableHeaders = Omit<Headers, "append" | "delete" | "set">;
110
+ //#endregion
111
+ export { ImmutableHeaders, Message, MessageContent };
File without changes
@@ -0,0 +1,25 @@
1
+
2
+ //#region src/priority.ts
3
+ /**
4
+ * Compares two priority levels and returns a number indicating their
5
+ * relative order.
6
+ *
7
+ * @example Sorting priorities
8
+ * ```ts
9
+ * import { comparePriority, type Priority } from "@upyo/core/priority";
10
+ * const priorities: Priority[] = ["normal", "low", "high"];
11
+ * priorities.sort(comparePriority);
12
+ * console.log(priorities); // ["high", "normal", "low"]
13
+ * ```
14
+ *
15
+ * @param a The first priority to compare.
16
+ * @param b The second priority to compare.
17
+ * @return A negative number if `a` is less than `b`, a positive number
18
+ * if `a` is greater than `b`, and zero if they are equal.
19
+ */
20
+ function comparePriority(a, b) {
21
+ return a === b ? 0 : a === "high" ? -1 : b === "high" ? 1 : a === "low" ? 1 : -1;
22
+ }
23
+
24
+ //#endregion
25
+ exports.comparePriority = comparePriority;
@@ -0,0 +1,25 @@
1
+ //#region src/priority.d.ts
2
+ /**
3
+ * The priority levels for email messages.
4
+ */
5
+ type Priority = "high" | "normal" | "low";
6
+ /**
7
+ * Compares two priority levels and returns a number indicating their
8
+ * relative order.
9
+ *
10
+ * @example Sorting priorities
11
+ * ```ts
12
+ * import { comparePriority, type Priority } from "@upyo/core/priority";
13
+ * const priorities: Priority[] = ["normal", "low", "high"];
14
+ * priorities.sort(comparePriority);
15
+ * console.log(priorities); // ["high", "normal", "low"]
16
+ * ```
17
+ *
18
+ * @param a The first priority to compare.
19
+ * @param b The second priority to compare.
20
+ * @return A negative number if `a` is less than `b`, a positive number
21
+ * if `a` is greater than `b`, and zero if they are equal.
22
+ */
23
+ declare function comparePriority(a: Priority, b: Priority): number;
24
+ //#endregion
25
+ export { Priority, comparePriority };
@@ -0,0 +1,25 @@
1
+ //#region src/priority.d.ts
2
+ /**
3
+ * The priority levels for email messages.
4
+ */
5
+ type Priority = "high" | "normal" | "low";
6
+ /**
7
+ * Compares two priority levels and returns a number indicating their
8
+ * relative order.
9
+ *
10
+ * @example Sorting priorities
11
+ * ```ts
12
+ * import { comparePriority, type Priority } from "@upyo/core/priority";
13
+ * const priorities: Priority[] = ["normal", "low", "high"];
14
+ * priorities.sort(comparePriority);
15
+ * console.log(priorities); // ["high", "normal", "low"]
16
+ * ```
17
+ *
18
+ * @param a The first priority to compare.
19
+ * @param b The second priority to compare.
20
+ * @return A negative number if `a` is less than `b`, a positive number
21
+ * if `a` is greater than `b`, and zero if they are equal.
22
+ */
23
+ declare function comparePriority(a: Priority, b: Priority): number;
24
+ //#endregion
25
+ export { Priority, comparePriority };
@@ -0,0 +1,24 @@
1
+ //#region src/priority.ts
2
+ /**
3
+ * Compares two priority levels and returns a number indicating their
4
+ * relative order.
5
+ *
6
+ * @example Sorting priorities
7
+ * ```ts
8
+ * import { comparePriority, type Priority } from "@upyo/core/priority";
9
+ * const priorities: Priority[] = ["normal", "low", "high"];
10
+ * priorities.sort(comparePriority);
11
+ * console.log(priorities); // ["high", "normal", "low"]
12
+ * ```
13
+ *
14
+ * @param a The first priority to compare.
15
+ * @param b The second priority to compare.
16
+ * @return A negative number if `a` is less than `b`, a positive number
17
+ * if `a` is greater than `b`, and zero if they are equal.
18
+ */
19
+ function comparePriority(a, b) {
20
+ return a === b ? 0 : a === "high" ? -1 : b === "high" ? 1 : a === "low" ? 1 : -1;
21
+ }
22
+
23
+ //#endregion
24
+ export { comparePriority };
File without changes
@@ -0,0 +1,21 @@
1
+ //#region src/receipt.d.ts
2
+ /**
3
+ * The response from the email service after sending an email message.
4
+ */
5
+ interface Receipt {
6
+ /**
7
+ * The unique identifier for the message that was sent.
8
+ */
9
+ readonly messageId: string;
10
+ /**
11
+ * An array of error messages that occurred during the sending process,
12
+ * if any. If the email was sent successfully, this array will be empty.
13
+ */
14
+ readonly errorMessages: string[];
15
+ /**
16
+ * Indicates whether the email was sent successfully.
17
+ */
18
+ readonly successful: boolean;
19
+ }
20
+ //#endregion
21
+ export { Receipt };
@@ -0,0 +1,21 @@
1
+ //#region src/receipt.d.ts
2
+ /**
3
+ * The response from the email service after sending an email message.
4
+ */
5
+ interface Receipt {
6
+ /**
7
+ * The unique identifier for the message that was sent.
8
+ */
9
+ readonly messageId: string;
10
+ /**
11
+ * An array of error messages that occurred during the sending process,
12
+ * if any. If the email was sent successfully, this array will be empty.
13
+ */
14
+ readonly errorMessages: string[];
15
+ /**
16
+ * Indicates whether the email was sent successfully.
17
+ */
18
+ readonly successful: boolean;
19
+ }
20
+ //#endregion
21
+ export { Receipt };
File without changes
File without changes
@@ -0,0 +1,43 @@
1
+ import { Message } from "./message.cjs";
2
+ import { Receipt } from "./receipt.cjs";
3
+
4
+ //#region src/transport.d.ts
5
+
6
+ /**
7
+ * A common interface for email sending services.
8
+ */
9
+ interface Transport {
10
+ /**
11
+ * Sends a single message using the email service.
12
+ * @param message The message to send.
13
+ * @param options Optional parameters for sending the message.
14
+ * @returns A promise that resolves to a receipt containing the result of
15
+ * the send operation.
16
+ */
17
+ send(message: Message, options?: TransportOptions): Promise<Receipt>;
18
+ /**
19
+ * Sends multiple messages using the email service.
20
+ * @param messages An iterable of messages to send.
21
+ * @param options Optional parameters for sending the messages.
22
+ * @return An async iterable that yields receipts for each sent message.
23
+ */
24
+ sendMany(messages: Iterable<Message>, options?: TransportOptions): AsyncIterable<Receipt>;
25
+ /**
26
+ * Sends multiple messages using the email service.
27
+ * @param messages An async iterable of messages to send.
28
+ * @param options Optional parameters for sending the messages.
29
+ * @return An async iterable that yields receipts for each sent message.
30
+ */
31
+ sendMany(messages: AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt>;
32
+ }
33
+ /**
34
+ * Options for sending messages with the email service.
35
+ */
36
+ interface TransportOptions {
37
+ /**
38
+ * The abort signal to cancel the send operation if needed.
39
+ */
40
+ signal?: AbortSignal;
41
+ }
42
+ //#endregion
43
+ export { Transport, TransportOptions };
@@ -0,0 +1,43 @@
1
+ import { Message } from "./message.js";
2
+ import { Receipt } from "./receipt.js";
3
+
4
+ //#region src/transport.d.ts
5
+
6
+ /**
7
+ * A common interface for email sending services.
8
+ */
9
+ interface Transport {
10
+ /**
11
+ * Sends a single message using the email service.
12
+ * @param message The message to send.
13
+ * @param options Optional parameters for sending the message.
14
+ * @returns A promise that resolves to a receipt containing the result of
15
+ * the send operation.
16
+ */
17
+ send(message: Message, options?: TransportOptions): Promise<Receipt>;
18
+ /**
19
+ * Sends multiple messages using the email service.
20
+ * @param messages An iterable of messages to send.
21
+ * @param options Optional parameters for sending the messages.
22
+ * @return An async iterable that yields receipts for each sent message.
23
+ */
24
+ sendMany(messages: Iterable<Message>, options?: TransportOptions): AsyncIterable<Receipt>;
25
+ /**
26
+ * Sends multiple messages using the email service.
27
+ * @param messages An async iterable of messages to send.
28
+ * @param options Optional parameters for sending the messages.
29
+ * @return An async iterable that yields receipts for each sent message.
30
+ */
31
+ sendMany(messages: AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt>;
32
+ }
33
+ /**
34
+ * Options for sending messages with the email service.
35
+ */
36
+ interface TransportOptions {
37
+ /**
38
+ * The abort signal to cancel the send operation if needed.
39
+ */
40
+ signal?: AbortSignal;
41
+ }
42
+ //#endregion
43
+ export { Transport, TransportOptions };
File without changes
package/package.json ADDED
@@ -0,0 +1,115 @@
1
+ {
2
+ "name": "@upyo/core",
3
+ "version": "0.1.0-dev.10+c222d7c6",
4
+ "description": "Simple email sending library for Node.js, Deno, Bun, and edge functions",
5
+ "keywords": [
6
+ "email",
7
+ "mail",
8
+ "sendmail",
9
+ "smtp"
10
+ ],
11
+ "license": "MIT",
12
+ "author": {
13
+ "name": "Hong Minhee",
14
+ "email": "hong@minhee.org",
15
+ "url": "https://hongminhee.org/"
16
+ },
17
+ "homepage": "https://upyo.org/",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/dahlia/upyo.git",
21
+ "directory": "packages/core/"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/dahlia/upyo/issues"
25
+ },
26
+ "funding": [
27
+ "https://github.com/sponsors/dahlia"
28
+ ],
29
+ "engines": {
30
+ "node": ">=20.0.0",
31
+ "bun": ">=1.2.0",
32
+ "deno": ">=2.3.0"
33
+ },
34
+ "files": [
35
+ "dist/",
36
+ "package.json",
37
+ "README.md"
38
+ ],
39
+ "type": "module",
40
+ "module": "./dist/index.js",
41
+ "main": "./dist/index.cjs",
42
+ "types": "./dist/index.d.ts",
43
+ "exports": {
44
+ ".": {
45
+ "types": {
46
+ "import": "./dist/index.d.ts",
47
+ "require": "./dist/index.d.cts"
48
+ },
49
+ "import": "./dist/index.js",
50
+ "require": "./dist/index.cjs"
51
+ },
52
+ "./address": {
53
+ "types": {
54
+ "import": "./dist/address.d.ts",
55
+ "require": "./dist/address.cts"
56
+ },
57
+ "import": "./dist/address.js",
58
+ "require": "./dist/address.cjs"
59
+ },
60
+ "./attachment": {
61
+ "types": {
62
+ "import": "./dist/attachment.d.ts",
63
+ "require": "./dist/attachment.cts"
64
+ },
65
+ "import": "./dist/attachment.js",
66
+ "require": "./dist/attachment.cjs"
67
+ },
68
+ "./message": {
69
+ "types": {
70
+ "import": "./dist/message.d.ts",
71
+ "require": "./dist/message.cts"
72
+ },
73
+ "import": "./dist/message.js",
74
+ "require": "./dist/message.cjs"
75
+ },
76
+ "./priority": {
77
+ "types": {
78
+ "import": "./dist/priority.d.ts",
79
+ "require": "./dist/priority.cts"
80
+ },
81
+ "import": "./dist/priority.js",
82
+ "require": "./dist/priority.cjs"
83
+ },
84
+ "./receipt": {
85
+ "types": {
86
+ "import": "./dist/receipt.d.ts",
87
+ "require": "./dist/receipt.cts"
88
+ },
89
+ "import": "./dist/receipt.js",
90
+ "require": "./dist/receipt.cjs"
91
+ },
92
+ "./transport": {
93
+ "types": {
94
+ "import": "./dist/transport.d.ts",
95
+ "require": "./dist/transport.cts"
96
+ },
97
+ "import": "./dist/transport.js",
98
+ "require": "./dist/transport.cjs"
99
+ },
100
+ "./package.json": "./package.json"
101
+ },
102
+ "sideEffects": false,
103
+ "devDependencies": {
104
+ "tsdown": "^0.12.7",
105
+ "typescript": "5.8.3"
106
+ },
107
+ "scripts": {
108
+ "build": "tsdown",
109
+ "prepublish": "tsdown",
110
+ "test": "tsdown && node --experimental-transform-types --test",
111
+ "test:bun": "tsdown && bun test",
112
+ "test:deno": "deno test",
113
+ "test-all": "tsdown && node --experimental-transform-types --test && bun test && deno test"
114
+ }
115
+ }