@upyo/core 0.1.0-dev.10 → 0.1.0-dev.11

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 CHANGED
@@ -3,9 +3,18 @@
3
3
  @upyo/core
4
4
  ==========
5
5
 
6
- Shared types and interfaces for Upyo, a simple and cross-runtime library for
6
+ [![JSR][JSR badge]][JSR]
7
+ [![npm][npm badge]][npm]
8
+
9
+ Shared types and interfaces for [Upyo], a simple and cross-runtime library for
7
10
  sending email messages.
8
11
 
12
+ [JSR]: https://jsr.io/@upyo/core
13
+ [JSR badge]: https://jsr.io/badges/@upyo/core
14
+ [npm]: https://www.npmjs.com/package/@upyo/core
15
+ [npm badge]: https://img.shields.io/npm/v/@upyo/core?logo=npm
16
+ [Upyo]: https://upyo.org/
17
+
9
18
 
10
19
  Installation
11
20
  ------------
@@ -0,0 +1,13 @@
1
+
2
+ //#region src/attachment.ts
3
+ /**
4
+ * Checks if the provided value is an {@link Attachment} object.
5
+ * @param attachment The value to check.
6
+ * @return `true` if the value is an {@link Attachment}, otherwise `false`.
7
+ */
8
+ function isAttachment(attachment) {
9
+ return typeof attachment === "object" && attachment !== null && "inline" in attachment && "filename" in attachment && "content" in attachment && "contentType" in attachment && "contentId" in attachment && typeof attachment.inline === "boolean" && typeof attachment.filename === "string" && (attachment.content instanceof Uint8Array || attachment.content instanceof Promise && typeof attachment.content.then === "function") && typeof attachment.contentType === "string" && typeof attachment.contentId === "string";
10
+ }
11
+
12
+ //#endregion
13
+ exports.isAttachment = isAttachment;
@@ -13,9 +13,11 @@ interface Attachment {
13
13
  */
14
14
  readonly filename: string;
15
15
  /**
16
- * The content of the attachment as a byte array.
16
+ * The content of the attachment as a byte array. It can be a `Promise`
17
+ * that resolves to a `Uint8Array`, allowing for asynchronous loading
18
+ * of the attachment content.
17
19
  */
18
- readonly content: Uint8Array;
20
+ readonly content: Uint8Array | Promise<Uint8Array>;
19
21
  /**
20
22
  * The media type of the attachment, which indicates the type of content
21
23
  * and how it should be handled by email clients.
@@ -27,5 +29,11 @@ interface Attachment {
27
29
  */
28
30
  readonly contentId: string;
29
31
  }
32
+ /**
33
+ * Checks if the provided value is an {@link Attachment} object.
34
+ * @param attachment The value to check.
35
+ * @return `true` if the value is an {@link Attachment}, otherwise `false`.
36
+ */
37
+ declare function isAttachment(attachment: unknown): attachment is Attachment;
30
38
  //#endregion
31
- export { Attachment };
39
+ export { Attachment, isAttachment };
@@ -13,9 +13,11 @@ interface Attachment {
13
13
  */
14
14
  readonly filename: string;
15
15
  /**
16
- * The content of the attachment as a byte array.
16
+ * The content of the attachment as a byte array. It can be a `Promise`
17
+ * that resolves to a `Uint8Array`, allowing for asynchronous loading
18
+ * of the attachment content.
17
19
  */
18
- readonly content: Uint8Array;
20
+ readonly content: Uint8Array | Promise<Uint8Array>;
19
21
  /**
20
22
  * The media type of the attachment, which indicates the type of content
21
23
  * and how it should be handled by email clients.
@@ -27,5 +29,11 @@ interface Attachment {
27
29
  */
28
30
  readonly contentId: string;
29
31
  }
32
+ /**
33
+ * Checks if the provided value is an {@link Attachment} object.
34
+ * @param attachment The value to check.
35
+ * @return `true` if the value is an {@link Attachment}, otherwise `false`.
36
+ */
37
+ declare function isAttachment(attachment: unknown): attachment is Attachment;
30
38
  //#endregion
31
- export { Attachment };
39
+ export { Attachment, isAttachment };
@@ -0,0 +1,12 @@
1
+ //#region src/attachment.ts
2
+ /**
3
+ * Checks if the provided value is an {@link Attachment} object.
4
+ * @param attachment The value to check.
5
+ * @return `true` if the value is an {@link Attachment}, otherwise `false`.
6
+ */
7
+ function isAttachment(attachment) {
8
+ return typeof attachment === "object" && attachment !== null && "inline" in attachment && "filename" in attachment && "content" in attachment && "contentType" in attachment && "contentId" in attachment && typeof attachment.inline === "boolean" && typeof attachment.filename === "string" && (attachment.content instanceof Uint8Array || attachment.content instanceof Promise && typeof attachment.content.then === "function") && typeof attachment.contentType === "string" && typeof attachment.contentId === "string";
9
+ }
10
+
11
+ //#endregion
12
+ export { isAttachment };
package/dist/index.cjs CHANGED
@@ -1,6 +1,10 @@
1
1
  const require_address = require('./address.cjs');
2
+ const require_attachment = require('./attachment.cjs');
3
+ const require_message = require('./message.cjs');
2
4
  const require_priority = require('./priority.cjs');
3
5
 
4
6
  exports.comparePriority = require_priority.comparePriority;
7
+ exports.createMessage = require_message.createMessage;
5
8
  exports.formatAddress = require_address.formatAddress;
9
+ exports.isAttachment = require_attachment.isAttachment;
6
10
  exports.parseAddress = require_address.parseAddress;
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Address, formatAddress, parseAddress } from "./address.cjs";
2
- import { Attachment } from "./attachment.cjs";
2
+ import { Attachment, isAttachment } from "./attachment.cjs";
3
3
  import { Priority, comparePriority } from "./priority.cjs";
4
- import { ImmutableHeaders, Message, MessageContent } from "./message.cjs";
4
+ import { ImmutableHeaders, Message, MessageConstructor, MessageContent, createMessage } from "./message.cjs";
5
5
  import { Receipt } from "./receipt.cjs";
6
6
  import { Transport, TransportOptions } from "./transport.cjs";
7
- export { Address, Attachment, ImmutableHeaders, Message, MessageContent, Priority, Receipt, Transport, TransportOptions, comparePriority, formatAddress, parseAddress };
7
+ export { Address, Attachment, ImmutableHeaders, Message, MessageConstructor, MessageContent, Priority, Receipt, Transport, TransportOptions, comparePriority, createMessage, formatAddress, isAttachment, parseAddress };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Address, formatAddress, parseAddress } from "./address.js";
2
- import { Attachment } from "./attachment.js";
2
+ import { Attachment, isAttachment } from "./attachment.js";
3
3
  import { Priority, comparePriority } from "./priority.js";
4
- import { ImmutableHeaders, Message, MessageContent } from "./message.js";
4
+ import { ImmutableHeaders, Message, MessageConstructor, MessageContent, createMessage } from "./message.js";
5
5
  import { Receipt } from "./receipt.js";
6
6
  import { Transport, TransportOptions } from "./transport.js";
7
- export { Address, Attachment, ImmutableHeaders, Message, MessageContent, Priority, Receipt, Transport, TransportOptions, comparePriority, formatAddress, parseAddress };
7
+ export { Address, Attachment, ImmutableHeaders, Message, MessageConstructor, MessageContent, Priority, Receipt, Transport, TransportOptions, comparePriority, createMessage, formatAddress, isAttachment, parseAddress };
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { formatAddress, parseAddress } from "./address.js";
2
+ import { isAttachment } from "./attachment.js";
3
+ import { createMessage } from "./message.js";
2
4
  import { comparePriority } from "./priority.js";
3
5
 
4
- export { comparePriority, formatAddress, parseAddress };
6
+ export { comparePriority, createMessage, formatAddress, isAttachment, parseAddress };
package/dist/message.cjs CHANGED
@@ -0,0 +1,61 @@
1
+ const require_address = require('./address.cjs');
2
+ const require_attachment = require('./attachment.cjs');
3
+
4
+ //#region src/message.ts
5
+ /**
6
+ * Creates a new email {@link Message} based on the provided constructor
7
+ * parameters. This function provides a more convenient API for creating
8
+ * messages compared to constructing a {@link Message} object directly.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const message = createMessage({
13
+ * from: "sender@example.com",
14
+ * to: "recipient1@example.com",
15
+ * subject: "Hello World",
16
+ * content: { text: "This is a test message" }
17
+ * });
18
+ * ```
19
+ *
20
+ * @param constructor The constructor parameters for the message. Uses more
21
+ * user-friendly types like accepting strings for email
22
+ * addresses and `File` objects for attachments.
23
+ * @returns A new {@link Message} object with all properties normalized and
24
+ * validated.
25
+ * @throws {TypeError} When any email address string cannot be parsed or when
26
+ * an attachment object is invalid.
27
+ */
28
+ function createMessage(constructor) {
29
+ return {
30
+ sender: typeof constructor.from === "string" ? require_address.parseAddress(constructor.from) ?? throwTypeError(`Invalid sender address: ${JSON.stringify(constructor.from)}`) : constructor.from,
31
+ recipients: esureArray(constructor.to).map((to) => typeof to === "string" ? require_address.parseAddress(to) ?? throwTypeError(`Invalid recipient address: ${JSON.stringify(to)}`) : to),
32
+ ccRecipients: esureArray(constructor.cc).map((cc) => typeof cc === "string" ? require_address.parseAddress(cc) ?? throwTypeError(`Invalid CC address: ${JSON.stringify(cc)}`) : cc),
33
+ bccRecipients: esureArray(constructor.bcc).map((bcc) => typeof bcc === "string" ? require_address.parseAddress(bcc) ?? throwTypeError(`Invalid BCC address: ${JSON.stringify(bcc)}`) : bcc),
34
+ replyRecipients: esureArray(constructor.replyTo).map((replyTo) => typeof replyTo === "string" ? require_address.parseAddress(replyTo) ?? throwTypeError(`Invalid reply-to address: ${JSON.stringify(replyTo)}`) : replyTo),
35
+ attachments: esureArray(constructor.attachments).map((attachment) => {
36
+ if (attachment instanceof File) return {
37
+ inline: false,
38
+ filename: attachment.name,
39
+ content: attachment.arrayBuffer().then((b) => new Uint8Array(b)),
40
+ contentType: attachment.type == null || attachment.type === "" ? "application/octet-stream" : attachment.type,
41
+ contentId: ""
42
+ };
43
+ else if (require_attachment.isAttachment(attachment)) return attachment;
44
+ else throwTypeError(`Invalid attachment: ${JSON.stringify(attachment)}`);
45
+ }),
46
+ subject: constructor.subject,
47
+ content: constructor.content,
48
+ priority: constructor.priority ?? "normal",
49
+ tags: esureArray(constructor.tags),
50
+ headers: new Headers(constructor.headers ?? {})
51
+ };
52
+ }
53
+ function throwTypeError(message) {
54
+ throw new TypeError(message);
55
+ }
56
+ function esureArray(value) {
57
+ return Array.isArray(value) ? value : value == null ? [] : [value];
58
+ }
59
+
60
+ //#endregion
61
+ exports.createMessage = createMessage;
@@ -7,6 +7,10 @@ import { Priority } from "./priority.cjs";
7
7
  /**
8
8
  * Represents an email message with various properties such as
9
9
  * sender, recipients, subject, content, and attachments.
10
+ *
11
+ * You wouldn't typically create this type directly. Instead, you probably
12
+ * want to use the {@link createMessage} function, which provides a more
13
+ * convenient API for creating messages.
10
14
  */
11
15
  interface Message {
12
16
  /**
@@ -107,5 +111,101 @@ type MessageContent = {
107
111
  * read-only access to the headers of an email message.
108
112
  */
109
113
  type ImmutableHeaders = Omit<Headers, "append" | "delete" | "set">;
114
+ /**
115
+ * A constructor interface for creating a new email message using
116
+ * the {@link createMessage} function.
117
+ */
118
+ interface MessageConstructor {
119
+ /**
120
+ * The email address of the sender of the message.
121
+ */
122
+ readonly from: Address | string;
123
+ /**
124
+ * The email addresses of the recipient of the message.
125
+ */
126
+ readonly to: Address | string | (Address | string)[];
127
+ /**
128
+ * The email addresses of the carbon copy (CC) recipients of the message.
129
+ * @default `[]`
130
+ */
131
+ readonly cc?: Address | string | (Address | string)[];
132
+ /**
133
+ * The email addresses of the blind carbon copy (BCC) recipients of the message.
134
+ * @default `[]`
135
+ */
136
+ readonly bcc?: Address | string | (Address | string)[];
137
+ /**
138
+ * The email addresses of the reply-to recipients of the message.
139
+ * @default `[]`
140
+ */
141
+ readonly replyTo?: Address | string | (Address | string)[];
142
+ /**
143
+ * The attachments included in the email message. These are files that
144
+ * are sent along with the email, such as documents, images, or other
145
+ * media files. Each attachment can be represented by an {@link Attachment}
146
+ * or a `File` object, which contains information about the attachment such as
147
+ * its filename and content type.
148
+ * @default `[]`
149
+ */
150
+ readonly attachments?: Attachment | File | (Attachment | File)[];
151
+ /**
152
+ * The subject of the email message. This is typically a brief summary
153
+ * of the content of the email, and is used to help the recipient identify
154
+ * the purpose of the message.
155
+ */
156
+ readonly subject: string;
157
+ /**
158
+ * The content of the email message, which can be either HTML or plain text.
159
+ * This property is represented by the {@link MessageContent} type, which
160
+ * includes both HTML and plain text content. The HTML content is typically
161
+ * used for rich formatting and layout, while the plain text content is
162
+ * used for simple text emails or for compatibility with email clients
163
+ * that do not support HTML.
164
+ */
165
+ readonly content: MessageContent;
166
+ /**
167
+ * The priority of the email message, which indicates its importance
168
+ * relative to other messages. The priority can be one of three
169
+ * levels: `"high"`, `"normal"`, or `"low"`. This is represented by
170
+ * the {@link Priority} type, which is a string literal type that
171
+ * allows only these three values.
172
+ * @default `"normal"`
173
+ */
174
+ readonly priority?: Priority;
175
+ /**
176
+ * The tags associated with the email message.
177
+ * @default `[]`
178
+ */
179
+ readonly tags?: string[];
180
+ /**
181
+ * The headers of the email message.
182
+ * @default `{}`
183
+ */
184
+ readonly headers?: ImmutableHeaders | Record<string, string>;
185
+ }
186
+ /**
187
+ * Creates a new email {@link Message} based on the provided constructor
188
+ * parameters. This function provides a more convenient API for creating
189
+ * messages compared to constructing a {@link Message} object directly.
190
+ *
191
+ * @example
192
+ * ```typescript
193
+ * const message = createMessage({
194
+ * from: "sender@example.com",
195
+ * to: "recipient1@example.com",
196
+ * subject: "Hello World",
197
+ * content: { text: "This is a test message" }
198
+ * });
199
+ * ```
200
+ *
201
+ * @param constructor The constructor parameters for the message. Uses more
202
+ * user-friendly types like accepting strings for email
203
+ * addresses and `File` objects for attachments.
204
+ * @returns A new {@link Message} object with all properties normalized and
205
+ * validated.
206
+ * @throws {TypeError} When any email address string cannot be parsed or when
207
+ * an attachment object is invalid.
208
+ */
209
+ declare function createMessage(constructor: MessageConstructor): Message;
110
210
  //#endregion
111
- export { ImmutableHeaders, Message, MessageContent };
211
+ export { ImmutableHeaders, Message, MessageConstructor, MessageContent, createMessage };
package/dist/message.d.ts CHANGED
@@ -7,6 +7,10 @@ import { Priority } from "./priority.js";
7
7
  /**
8
8
  * Represents an email message with various properties such as
9
9
  * sender, recipients, subject, content, and attachments.
10
+ *
11
+ * You wouldn't typically create this type directly. Instead, you probably
12
+ * want to use the {@link createMessage} function, which provides a more
13
+ * convenient API for creating messages.
10
14
  */
11
15
  interface Message {
12
16
  /**
@@ -107,5 +111,101 @@ type MessageContent = {
107
111
  * read-only access to the headers of an email message.
108
112
  */
109
113
  type ImmutableHeaders = Omit<Headers, "append" | "delete" | "set">;
114
+ /**
115
+ * A constructor interface for creating a new email message using
116
+ * the {@link createMessage} function.
117
+ */
118
+ interface MessageConstructor {
119
+ /**
120
+ * The email address of the sender of the message.
121
+ */
122
+ readonly from: Address | string;
123
+ /**
124
+ * The email addresses of the recipient of the message.
125
+ */
126
+ readonly to: Address | string | (Address | string)[];
127
+ /**
128
+ * The email addresses of the carbon copy (CC) recipients of the message.
129
+ * @default `[]`
130
+ */
131
+ readonly cc?: Address | string | (Address | string)[];
132
+ /**
133
+ * The email addresses of the blind carbon copy (BCC) recipients of the message.
134
+ * @default `[]`
135
+ */
136
+ readonly bcc?: Address | string | (Address | string)[];
137
+ /**
138
+ * The email addresses of the reply-to recipients of the message.
139
+ * @default `[]`
140
+ */
141
+ readonly replyTo?: Address | string | (Address | string)[];
142
+ /**
143
+ * The attachments included in the email message. These are files that
144
+ * are sent along with the email, such as documents, images, or other
145
+ * media files. Each attachment can be represented by an {@link Attachment}
146
+ * or a `File` object, which contains information about the attachment such as
147
+ * its filename and content type.
148
+ * @default `[]`
149
+ */
150
+ readonly attachments?: Attachment | File | (Attachment | File)[];
151
+ /**
152
+ * The subject of the email message. This is typically a brief summary
153
+ * of the content of the email, and is used to help the recipient identify
154
+ * the purpose of the message.
155
+ */
156
+ readonly subject: string;
157
+ /**
158
+ * The content of the email message, which can be either HTML or plain text.
159
+ * This property is represented by the {@link MessageContent} type, which
160
+ * includes both HTML and plain text content. The HTML content is typically
161
+ * used for rich formatting and layout, while the plain text content is
162
+ * used for simple text emails or for compatibility with email clients
163
+ * that do not support HTML.
164
+ */
165
+ readonly content: MessageContent;
166
+ /**
167
+ * The priority of the email message, which indicates its importance
168
+ * relative to other messages. The priority can be one of three
169
+ * levels: `"high"`, `"normal"`, or `"low"`. This is represented by
170
+ * the {@link Priority} type, which is a string literal type that
171
+ * allows only these three values.
172
+ * @default `"normal"`
173
+ */
174
+ readonly priority?: Priority;
175
+ /**
176
+ * The tags associated with the email message.
177
+ * @default `[]`
178
+ */
179
+ readonly tags?: string[];
180
+ /**
181
+ * The headers of the email message.
182
+ * @default `{}`
183
+ */
184
+ readonly headers?: ImmutableHeaders | Record<string, string>;
185
+ }
186
+ /**
187
+ * Creates a new email {@link Message} based on the provided constructor
188
+ * parameters. This function provides a more convenient API for creating
189
+ * messages compared to constructing a {@link Message} object directly.
190
+ *
191
+ * @example
192
+ * ```typescript
193
+ * const message = createMessage({
194
+ * from: "sender@example.com",
195
+ * to: "recipient1@example.com",
196
+ * subject: "Hello World",
197
+ * content: { text: "This is a test message" }
198
+ * });
199
+ * ```
200
+ *
201
+ * @param constructor The constructor parameters for the message. Uses more
202
+ * user-friendly types like accepting strings for email
203
+ * addresses and `File` objects for attachments.
204
+ * @returns A new {@link Message} object with all properties normalized and
205
+ * validated.
206
+ * @throws {TypeError} When any email address string cannot be parsed or when
207
+ * an attachment object is invalid.
208
+ */
209
+ declare function createMessage(constructor: MessageConstructor): Message;
110
210
  //#endregion
111
- export { ImmutableHeaders, Message, MessageContent };
211
+ export { ImmutableHeaders, Message, MessageConstructor, MessageContent, createMessage };
package/dist/message.js CHANGED
@@ -0,0 +1,61 @@
1
+ import { parseAddress } from "./address.js";
2
+ import { isAttachment } from "./attachment.js";
3
+
4
+ //#region src/message.ts
5
+ /**
6
+ * Creates a new email {@link Message} based on the provided constructor
7
+ * parameters. This function provides a more convenient API for creating
8
+ * messages compared to constructing a {@link Message} object directly.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const message = createMessage({
13
+ * from: "sender@example.com",
14
+ * to: "recipient1@example.com",
15
+ * subject: "Hello World",
16
+ * content: { text: "This is a test message" }
17
+ * });
18
+ * ```
19
+ *
20
+ * @param constructor The constructor parameters for the message. Uses more
21
+ * user-friendly types like accepting strings for email
22
+ * addresses and `File` objects for attachments.
23
+ * @returns A new {@link Message} object with all properties normalized and
24
+ * validated.
25
+ * @throws {TypeError} When any email address string cannot be parsed or when
26
+ * an attachment object is invalid.
27
+ */
28
+ function createMessage(constructor) {
29
+ return {
30
+ sender: typeof constructor.from === "string" ? parseAddress(constructor.from) ?? throwTypeError(`Invalid sender address: ${JSON.stringify(constructor.from)}`) : constructor.from,
31
+ recipients: esureArray(constructor.to).map((to) => typeof to === "string" ? parseAddress(to) ?? throwTypeError(`Invalid recipient address: ${JSON.stringify(to)}`) : to),
32
+ ccRecipients: esureArray(constructor.cc).map((cc) => typeof cc === "string" ? parseAddress(cc) ?? throwTypeError(`Invalid CC address: ${JSON.stringify(cc)}`) : cc),
33
+ bccRecipients: esureArray(constructor.bcc).map((bcc) => typeof bcc === "string" ? parseAddress(bcc) ?? throwTypeError(`Invalid BCC address: ${JSON.stringify(bcc)}`) : bcc),
34
+ replyRecipients: esureArray(constructor.replyTo).map((replyTo) => typeof replyTo === "string" ? parseAddress(replyTo) ?? throwTypeError(`Invalid reply-to address: ${JSON.stringify(replyTo)}`) : replyTo),
35
+ attachments: esureArray(constructor.attachments).map((attachment) => {
36
+ if (attachment instanceof File) return {
37
+ inline: false,
38
+ filename: attachment.name,
39
+ content: attachment.arrayBuffer().then((b) => new Uint8Array(b)),
40
+ contentType: attachment.type == null || attachment.type === "" ? "application/octet-stream" : attachment.type,
41
+ contentId: ""
42
+ };
43
+ else if (isAttachment(attachment)) return attachment;
44
+ else throwTypeError(`Invalid attachment: ${JSON.stringify(attachment)}`);
45
+ }),
46
+ subject: constructor.subject,
47
+ content: constructor.content,
48
+ priority: constructor.priority ?? "normal",
49
+ tags: esureArray(constructor.tags),
50
+ headers: new Headers(constructor.headers ?? {})
51
+ };
52
+ }
53
+ function throwTypeError(message) {
54
+ throw new TypeError(message);
55
+ }
56
+ function esureArray(value) {
57
+ return Array.isArray(value) ? value : value == null ? [] : [value];
58
+ }
59
+
60
+ //#endregion
61
+ export { createMessage };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upyo/core",
3
- "version": "0.1.0-dev.10+c222d7c6",
3
+ "version": "0.1.0-dev.11+9d41b08f",
4
4
  "description": "Simple email sending library for Node.js, Deno, Bun, and edge functions",
5
5
  "keywords": [
6
6
  "email",