@upyo/mailgun 0.1.0-dev.9 → 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/README.md CHANGED
@@ -3,9 +3,17 @@
3
3
  @upyo/mailgun
4
4
  =============
5
5
 
6
- [Mailgun] transport for Upyo email library.
6
+ [![JSR][JSR badge]][JSR]
7
+ [![npm][npm badge]][npm]
7
8
 
9
+ [Mailgun] transport for the [Upyo] email library.
10
+
11
+ [JSR]: https://jsr.io/@upyo/mailgun
12
+ [JSR badge]: https://jsr.io/badges/@upyo/mailgun
13
+ [npm]: https://www.npmjs.com/package/@upyo/mailgun
14
+ [npm badge]: https://img.shields.io/npm/v/@upyo/mailgun?logo=npm
8
15
  [Mailgun]: https://www.mailgun.com/
16
+ [Upyo]: https://upyo.org/
9
17
 
10
18
 
11
19
  Installation
@@ -24,22 +32,35 @@ Usage
24
32
  -----
25
33
 
26
34
  ~~~~ typescript
27
- import { MailgunTransport } from '@upyo/mailgun';
35
+ import { createMessage } from "@upyo/core";
36
+ import { MailgunTransport } from "@upyo/mailgun";
37
+ import fs from "node:fs/promises";
38
+ import process from "node:process";
39
+
40
+ const message = createMessage({
41
+ from: "sender@example.com",
42
+ to: "recipient@example.net",
43
+ subject: "Hello from Upyo!",
44
+ content: { text: "This is a test email." },
45
+ attachments: [
46
+ new File(
47
+ [await fs.readFile("image.jpg"), "image.jpg", { type: "image/jpeg" }]
48
+ )
49
+ ],
50
+ });
28
51
 
29
52
  const transport = new MailgunTransport({
30
- apiKey: 'your-api-key',
31
- domain: 'your-domain.com'
53
+ apiKey: process.env.MAILGUN_KEY!,
54
+ domain: process.env.MAILGUN_DOMAIN!,
55
+ region: process.env.MAILGUN_REGION as "us" | "eu",
32
56
  });
33
57
 
34
- const message = {
35
- sender: { address: 'sender@example.com' },
36
- recipients: [{ address: 'recipient@example.com' }],
37
- subject: 'Hello from Mailgun!',
38
- content: { text: 'Hello, World!' }
39
- };
40
-
41
58
  const receipt = await transport.send(message);
42
- console.log('Message sent:', receipt.messageId);
59
+ if (receipt.successful) {
60
+ console.log("Message sent with ID:", receipt.messageId);
61
+ } else {
62
+ console.error("Send failed:", receipt.errorMessages.join(", "));
63
+ }
43
64
  ~~~~
44
65
 
45
66
 
package/dist/index.cjs CHANGED
@@ -5,21 +5,11 @@
5
5
  *
6
6
  * This function takes a partial Mailgun configuration and returns a complete
7
7
  * configuration with all optional fields filled with sensible defaults.
8
+ * It is used internally by the Mailgun transport.
8
9
  *
9
10
  * @param config - The Mailgun configuration with optional fields
10
11
  * @returns A resolved configuration with all defaults applied
11
- *
12
- * @example
13
- * ```typescript
14
- * const resolved = createMailgunConfig({
15
- * apiKey: 'your-api-key',
16
- * domain: 'your-domain.com'
17
- * });
18
- *
19
- * // resolved.region will be 'us' (default)
20
- * // resolved.timeout will be 30000 (default)
21
- * // resolved.retries will be 3 (default)
22
- * ```
12
+ * @internal
23
13
  */
24
14
  function createMailgunConfig(config) {
25
15
  const region = config.region ?? "us";
@@ -162,11 +152,11 @@ var MailgunApiError = class extends Error {
162
152
  *
163
153
  * @example
164
154
  * ```typescript
165
- * const formData = convertMessage(message, config);
155
+ * const formData = await convertMessage(message, config);
166
156
  * const response = await fetch(url, { method: 'POST', body: formData });
167
157
  * ```
168
158
  */
169
- function convertMessage(message, config) {
159
+ async function convertMessage(message, config) {
170
160
  const formData = new FormData();
171
161
  formData.append("from", formatAddress(message.sender));
172
162
  for (const recipient of message.recipients) formData.append("to", formatAddress(recipient));
@@ -191,7 +181,7 @@ function convertMessage(message, config) {
191
181
  }
192
182
  for (const tag of message.tags) formData.append("o:tag", tag);
193
183
  for (const [key, value] of message.headers.entries()) if (!isStandardHeader(key)) formData.append(`h:${key}`, value);
194
- for (const attachment of message.attachments) appendAttachment(formData, attachment);
184
+ for (const attachment of message.attachments) await appendAttachment(formData, attachment);
195
185
  if (config.tracking !== void 0) formData.append("o:tracking", config.tracking ? "yes" : "no");
196
186
  if (config.clickTracking !== void 0) formData.append("o:tracking-clicks", config.clickTracking ? "yes" : "no");
197
187
  if (config.openTracking !== void 0) formData.append("o:tracking-opens", config.openTracking ? "yes" : "no");
@@ -216,8 +206,8 @@ function formatAddress(address) {
216
206
  * @param formData - The FormData to append to
217
207
  * @param attachment - The attachment to append
218
208
  */
219
- function appendAttachment(formData, attachment) {
220
- const blob = new Blob([attachment.content], { type: attachment.contentType });
209
+ async function appendAttachment(formData, attachment) {
210
+ const blob = new Blob([await attachment.content], { type: attachment.contentType });
221
211
  if (attachment.contentId) formData.append("inline", blob, attachment.filename);
222
212
  else formData.append("attachment", blob, attachment.filename);
223
213
  }
@@ -263,10 +253,17 @@ function isStandardHeader(headerName) {
263
253
  * });
264
254
  *
265
255
  * const receipt = await transport.send(message);
266
- * console.log('Message sent:', receipt.messageId);
256
+ * if (receipt.successful) {
257
+ * console.log('Message sent with ID:', receipt.messageId);
258
+ * } else {
259
+ * console.error('Send failed:', receipt.errorMessages.join(', '));
260
+ * }
267
261
  * ```
268
262
  */
269
263
  var MailgunTransport = class {
264
+ /**
265
+ * The resolved Mailgun configuration used by this transport.
266
+ */
270
267
  config;
271
268
  httpClient;
272
269
  /**
@@ -314,20 +311,18 @@ var MailgunTransport = class {
314
311
  async send(message, options) {
315
312
  options?.signal?.throwIfAborted();
316
313
  try {
317
- const formData = convertMessage(message, this.config);
314
+ const formData = await convertMessage(message, this.config);
318
315
  options?.signal?.throwIfAborted();
319
316
  const response = await this.httpClient.sendMessage(formData, options?.signal);
320
317
  return {
321
- messageId: response.id,
322
- errorMessages: [],
323
- successful: true
318
+ successful: true,
319
+ messageId: response.id
324
320
  };
325
321
  } catch (error) {
326
322
  const errorMessage = error instanceof Error ? error.message : String(error);
327
323
  return {
328
- messageId: "",
329
- errorMessages: [errorMessage],
330
- successful: false
324
+ successful: false,
325
+ errorMessages: [errorMessage]
331
326
  };
332
327
  }
333
328
  }
@@ -399,5 +394,4 @@ var MailgunTransport = class {
399
394
 
400
395
  //#endregion
401
396
  exports.MailgunApiError = MailgunApiError;
402
- exports.MailgunTransport = MailgunTransport;
403
- exports.createMailgunConfig = createMailgunConfig;
397
+ exports.MailgunTransport = MailgunTransport;
package/dist/index.d.cts CHANGED
@@ -100,83 +100,12 @@ type ResolvedMailgunConfig = Required<MailgunConfig>;
100
100
  *
101
101
  * This function takes a partial Mailgun configuration and returns a complete
102
102
  * configuration with all optional fields filled with sensible defaults.
103
+ * It is used internally by the Mailgun transport.
103
104
  *
104
105
  * @param config - The Mailgun configuration with optional fields
105
106
  * @returns A resolved configuration with all defaults applied
106
- *
107
- * @example
108
- * ```typescript
109
- * const resolved = createMailgunConfig({
110
- * apiKey: 'your-api-key',
111
- * domain: 'your-domain.com'
112
- * });
113
- *
114
- * // resolved.region will be 'us' (default)
115
- * // resolved.timeout will be 30000 (default)
116
- * // resolved.retries will be 3 (default)
117
- * ```
107
+ * @internal
118
108
  */
119
- declare function createMailgunConfig(config: MailgunConfig): ResolvedMailgunConfig;
120
- //#endregion
121
- //#region src/http-client.d.ts
122
- /**
123
- * Response from Mailgun API for sending messages.
124
- */
125
- interface MailgunResponse {
126
- /**
127
- * The message ID returned by Mailgun.
128
- */
129
- id: string;
130
- /**
131
- * Success message from Mailgun.
132
- */
133
- message: string;
134
- }
135
- /**
136
- * Error response from Mailgun API.
137
- */
138
-
139
- /**
140
- * HTTP client wrapper for Mailgun API requests.
141
- *
142
- * This class handles authentication, request formatting, error handling,
143
- * and retry logic for Mailgun API calls.
144
- */
145
- declare class MailgunHttpClient {
146
- private config;
147
- constructor(config: ResolvedMailgunConfig);
148
- /**
149
- * Sends a message via Mailgun API.
150
- *
151
- * @param formData The form data to send to Mailgun.
152
- * @param signal Optional AbortSignal for cancellation.
153
- * @returns Promise that resolves to the Mailgun response.
154
- */
155
- sendMessage(formData: FormData, signal?: AbortSignal): Promise<MailgunResponse>;
156
- /**
157
- * Makes an HTTP request to Mailgun API with retry logic.
158
- *
159
- * @param url The URL to make the request to.
160
- * @param options Fetch options.
161
- * @returns Promise that resolves to the parsed response.
162
- */
163
- private makeRequest;
164
- /**
165
- * Makes a fetch request with Mailgun authentication.
166
- *
167
- * @param url The URL to make the request to.
168
- * @param options Fetch options.
169
- * @returns Promise that resolves to the fetch response.
170
- */
171
- private fetchWithAuth;
172
- }
173
- /**
174
- * Custom error class for Mailgun API errors.
175
- */
176
- declare class MailgunApiError extends Error {
177
- statusCode?: number;
178
- constructor(message: string, statusCode?: number);
179
- }
180
109
  //#endregion
181
110
  //#region src/mailgun-transport.d.ts
182
111
  /**
@@ -196,12 +125,19 @@ declare class MailgunApiError extends Error {
196
125
  * });
197
126
  *
198
127
  * const receipt = await transport.send(message);
199
- * console.log('Message sent:', receipt.messageId);
128
+ * if (receipt.successful) {
129
+ * console.log('Message sent with ID:', receipt.messageId);
130
+ * } else {
131
+ * console.error('Send failed:', receipt.errorMessages.join(', '));
132
+ * }
200
133
  * ```
201
134
  */
202
135
  declare class MailgunTransport implements Transport {
203
- config: ReturnType<typeof createMailgunConfig>;
204
- httpClient: MailgunHttpClient;
136
+ /**
137
+ * The resolved Mailgun configuration used by this transport.
138
+ */
139
+ config: ResolvedMailgunConfig;
140
+ private httpClient;
205
141
  /**
206
142
  * Creates a new Mailgun transport instance.
207
143
  *
@@ -297,4 +233,14 @@ declare class MailgunTransport implements Transport {
297
233
  sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt>;
298
234
  }
299
235
  //#endregion
300
- export { MailgunApiError, MailgunConfig, MailgunTransport, createMailgunConfig };
236
+ //#region src/http-client.d.ts
237
+
238
+ /**
239
+ * Custom error class for Mailgun API errors.
240
+ */
241
+ declare class MailgunApiError extends Error {
242
+ statusCode?: number;
243
+ constructor(message: string, statusCode?: number);
244
+ }
245
+ //#endregion
246
+ export { MailgunApiError, MailgunConfig, MailgunTransport };
package/dist/index.d.ts CHANGED
@@ -100,83 +100,12 @@ type ResolvedMailgunConfig = Required<MailgunConfig>;
100
100
  *
101
101
  * This function takes a partial Mailgun configuration and returns a complete
102
102
  * configuration with all optional fields filled with sensible defaults.
103
+ * It is used internally by the Mailgun transport.
103
104
  *
104
105
  * @param config - The Mailgun configuration with optional fields
105
106
  * @returns A resolved configuration with all defaults applied
106
- *
107
- * @example
108
- * ```typescript
109
- * const resolved = createMailgunConfig({
110
- * apiKey: 'your-api-key',
111
- * domain: 'your-domain.com'
112
- * });
113
- *
114
- * // resolved.region will be 'us' (default)
115
- * // resolved.timeout will be 30000 (default)
116
- * // resolved.retries will be 3 (default)
117
- * ```
107
+ * @internal
118
108
  */
119
- declare function createMailgunConfig(config: MailgunConfig): ResolvedMailgunConfig;
120
- //#endregion
121
- //#region src/http-client.d.ts
122
- /**
123
- * Response from Mailgun API for sending messages.
124
- */
125
- interface MailgunResponse {
126
- /**
127
- * The message ID returned by Mailgun.
128
- */
129
- id: string;
130
- /**
131
- * Success message from Mailgun.
132
- */
133
- message: string;
134
- }
135
- /**
136
- * Error response from Mailgun API.
137
- */
138
-
139
- /**
140
- * HTTP client wrapper for Mailgun API requests.
141
- *
142
- * This class handles authentication, request formatting, error handling,
143
- * and retry logic for Mailgun API calls.
144
- */
145
- declare class MailgunHttpClient {
146
- private config;
147
- constructor(config: ResolvedMailgunConfig);
148
- /**
149
- * Sends a message via Mailgun API.
150
- *
151
- * @param formData The form data to send to Mailgun.
152
- * @param signal Optional AbortSignal for cancellation.
153
- * @returns Promise that resolves to the Mailgun response.
154
- */
155
- sendMessage(formData: FormData, signal?: AbortSignal): Promise<MailgunResponse>;
156
- /**
157
- * Makes an HTTP request to Mailgun API with retry logic.
158
- *
159
- * @param url The URL to make the request to.
160
- * @param options Fetch options.
161
- * @returns Promise that resolves to the parsed response.
162
- */
163
- private makeRequest;
164
- /**
165
- * Makes a fetch request with Mailgun authentication.
166
- *
167
- * @param url The URL to make the request to.
168
- * @param options Fetch options.
169
- * @returns Promise that resolves to the fetch response.
170
- */
171
- private fetchWithAuth;
172
- }
173
- /**
174
- * Custom error class for Mailgun API errors.
175
- */
176
- declare class MailgunApiError extends Error {
177
- statusCode?: number;
178
- constructor(message: string, statusCode?: number);
179
- }
180
109
  //#endregion
181
110
  //#region src/mailgun-transport.d.ts
182
111
  /**
@@ -196,12 +125,19 @@ declare class MailgunApiError extends Error {
196
125
  * });
197
126
  *
198
127
  * const receipt = await transport.send(message);
199
- * console.log('Message sent:', receipt.messageId);
128
+ * if (receipt.successful) {
129
+ * console.log('Message sent with ID:', receipt.messageId);
130
+ * } else {
131
+ * console.error('Send failed:', receipt.errorMessages.join(', '));
132
+ * }
200
133
  * ```
201
134
  */
202
135
  declare class MailgunTransport implements Transport {
203
- config: ReturnType<typeof createMailgunConfig>;
204
- httpClient: MailgunHttpClient;
136
+ /**
137
+ * The resolved Mailgun configuration used by this transport.
138
+ */
139
+ config: ResolvedMailgunConfig;
140
+ private httpClient;
205
141
  /**
206
142
  * Creates a new Mailgun transport instance.
207
143
  *
@@ -297,4 +233,14 @@ declare class MailgunTransport implements Transport {
297
233
  sendMany(messages: Iterable<Message> | AsyncIterable<Message>, options?: TransportOptions): AsyncIterable<Receipt>;
298
234
  }
299
235
  //#endregion
300
- export { MailgunApiError, MailgunConfig, MailgunTransport, createMailgunConfig };
236
+ //#region src/http-client.d.ts
237
+
238
+ /**
239
+ * Custom error class for Mailgun API errors.
240
+ */
241
+ declare class MailgunApiError extends Error {
242
+ statusCode?: number;
243
+ constructor(message: string, statusCode?: number);
244
+ }
245
+ //#endregion
246
+ export { MailgunApiError, MailgunConfig, MailgunTransport };
package/dist/index.js CHANGED
@@ -4,21 +4,11 @@
4
4
  *
5
5
  * This function takes a partial Mailgun configuration and returns a complete
6
6
  * configuration with all optional fields filled with sensible defaults.
7
+ * It is used internally by the Mailgun transport.
7
8
  *
8
9
  * @param config - The Mailgun configuration with optional fields
9
10
  * @returns A resolved configuration with all defaults applied
10
- *
11
- * @example
12
- * ```typescript
13
- * const resolved = createMailgunConfig({
14
- * apiKey: 'your-api-key',
15
- * domain: 'your-domain.com'
16
- * });
17
- *
18
- * // resolved.region will be 'us' (default)
19
- * // resolved.timeout will be 30000 (default)
20
- * // resolved.retries will be 3 (default)
21
- * ```
11
+ * @internal
22
12
  */
23
13
  function createMailgunConfig(config) {
24
14
  const region = config.region ?? "us";
@@ -161,11 +151,11 @@ var MailgunApiError = class extends Error {
161
151
  *
162
152
  * @example
163
153
  * ```typescript
164
- * const formData = convertMessage(message, config);
154
+ * const formData = await convertMessage(message, config);
165
155
  * const response = await fetch(url, { method: 'POST', body: formData });
166
156
  * ```
167
157
  */
168
- function convertMessage(message, config) {
158
+ async function convertMessage(message, config) {
169
159
  const formData = new FormData();
170
160
  formData.append("from", formatAddress(message.sender));
171
161
  for (const recipient of message.recipients) formData.append("to", formatAddress(recipient));
@@ -190,7 +180,7 @@ function convertMessage(message, config) {
190
180
  }
191
181
  for (const tag of message.tags) formData.append("o:tag", tag);
192
182
  for (const [key, value] of message.headers.entries()) if (!isStandardHeader(key)) formData.append(`h:${key}`, value);
193
- for (const attachment of message.attachments) appendAttachment(formData, attachment);
183
+ for (const attachment of message.attachments) await appendAttachment(formData, attachment);
194
184
  if (config.tracking !== void 0) formData.append("o:tracking", config.tracking ? "yes" : "no");
195
185
  if (config.clickTracking !== void 0) formData.append("o:tracking-clicks", config.clickTracking ? "yes" : "no");
196
186
  if (config.openTracking !== void 0) formData.append("o:tracking-opens", config.openTracking ? "yes" : "no");
@@ -215,8 +205,8 @@ function formatAddress(address) {
215
205
  * @param formData - The FormData to append to
216
206
  * @param attachment - The attachment to append
217
207
  */
218
- function appendAttachment(formData, attachment) {
219
- const blob = new Blob([attachment.content], { type: attachment.contentType });
208
+ async function appendAttachment(formData, attachment) {
209
+ const blob = new Blob([await attachment.content], { type: attachment.contentType });
220
210
  if (attachment.contentId) formData.append("inline", blob, attachment.filename);
221
211
  else formData.append("attachment", blob, attachment.filename);
222
212
  }
@@ -262,10 +252,17 @@ function isStandardHeader(headerName) {
262
252
  * });
263
253
  *
264
254
  * const receipt = await transport.send(message);
265
- * console.log('Message sent:', receipt.messageId);
255
+ * if (receipt.successful) {
256
+ * console.log('Message sent with ID:', receipt.messageId);
257
+ * } else {
258
+ * console.error('Send failed:', receipt.errorMessages.join(', '));
259
+ * }
266
260
  * ```
267
261
  */
268
262
  var MailgunTransport = class {
263
+ /**
264
+ * The resolved Mailgun configuration used by this transport.
265
+ */
269
266
  config;
270
267
  httpClient;
271
268
  /**
@@ -313,20 +310,18 @@ var MailgunTransport = class {
313
310
  async send(message, options) {
314
311
  options?.signal?.throwIfAborted();
315
312
  try {
316
- const formData = convertMessage(message, this.config);
313
+ const formData = await convertMessage(message, this.config);
317
314
  options?.signal?.throwIfAborted();
318
315
  const response = await this.httpClient.sendMessage(formData, options?.signal);
319
316
  return {
320
- messageId: response.id,
321
- errorMessages: [],
322
- successful: true
317
+ successful: true,
318
+ messageId: response.id
323
319
  };
324
320
  } catch (error) {
325
321
  const errorMessage = error instanceof Error ? error.message : String(error);
326
322
  return {
327
- messageId: "",
328
- errorMessages: [errorMessage],
329
- successful: false
323
+ successful: false,
324
+ errorMessages: [errorMessage]
330
325
  };
331
326
  }
332
327
  }
@@ -397,4 +392,4 @@ var MailgunTransport = class {
397
392
  };
398
393
 
399
394
  //#endregion
400
- export { MailgunApiError, MailgunTransport, createMailgunConfig };
395
+ export { MailgunApiError, MailgunTransport };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upyo/mailgun",
3
- "version": "0.1.0-dev.9+a57dde18",
3
+ "version": "0.1.0",
4
4
  "description": "Mailgun transport for Upyo email library",
5
5
  "keywords": [
6
6
  "email",
@@ -53,12 +53,12 @@
53
53
  },
54
54
  "sideEffects": false,
55
55
  "peerDependencies": {
56
- "@upyo/core": "0.1.0-dev.9+a57dde18"
56
+ "@upyo/core": "0.1.0"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@dotenvx/dotenvx": "^1.47.3",
60
60
  "tsdown": "^0.12.7",
61
- "typescript": "^5.8.3"
61
+ "typescript": "5.8.3"
62
62
  },
63
63
  "scripts": {
64
64
  "build": "tsdown",