@upyo/maileroo 0.6.0-dev.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 +20 -0
- package/README.md +102 -0
- package/dist/index.cjs +462 -0
- package/dist/index.d.cts +267 -0
- package/dist/index.d.ts +267 -0
- package/dist/index.js +436 -0
- package/package.json +65 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
import { combineSignals, createFailedReceipt, parseRetryAfter } from "@upyo/core";
|
|
2
|
+
|
|
3
|
+
//#region src/config.ts
|
|
4
|
+
/**
|
|
5
|
+
* Creates a resolved Maileroo configuration by applying default values.
|
|
6
|
+
*
|
|
7
|
+
* @param config The Maileroo configuration with optional fields.
|
|
8
|
+
* @returns A resolved configuration with all defaults applied.
|
|
9
|
+
* @since 0.6.0
|
|
10
|
+
*/
|
|
11
|
+
function createMailerooConfig(config) {
|
|
12
|
+
return {
|
|
13
|
+
apiKey: config.apiKey,
|
|
14
|
+
baseUrl: normalizeBaseUrl(config.baseUrl ?? "https://smtp.maileroo.com/api/v2"),
|
|
15
|
+
timeout: config.timeout ?? 3e4,
|
|
16
|
+
retries: config.retries ?? 3,
|
|
17
|
+
headers: config.headers ?? {},
|
|
18
|
+
tracking: config.tracking,
|
|
19
|
+
tags: config.tags == null ? void 0 : { ...config.tags }
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function normalizeBaseUrl(baseUrl) {
|
|
23
|
+
return baseUrl.replace(/\/+$/, "");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/http-client.ts
|
|
28
|
+
/**
|
|
29
|
+
* Maileroo API error class for API-specific failures.
|
|
30
|
+
*
|
|
31
|
+
* @since 0.6.0
|
|
32
|
+
*/
|
|
33
|
+
var MailerooApiError = class extends Error {
|
|
34
|
+
statusCode;
|
|
35
|
+
retryAfterMilliseconds;
|
|
36
|
+
attempts;
|
|
37
|
+
/**
|
|
38
|
+
* Creates a Maileroo API error.
|
|
39
|
+
*
|
|
40
|
+
* @param message Error message.
|
|
41
|
+
* @param statusCode HTTP status code.
|
|
42
|
+
* @param retryAfterMilliseconds Retry delay from the response.
|
|
43
|
+
* @param attempts Number of attempts made before this error.
|
|
44
|
+
*/
|
|
45
|
+
constructor(message, statusCode, retryAfterMilliseconds, attempts) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.name = "MailerooApiError";
|
|
48
|
+
this.statusCode = statusCode;
|
|
49
|
+
this.retryAfterMilliseconds = retryAfterMilliseconds;
|
|
50
|
+
this.attempts = attempts;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Maileroo request timeout error.
|
|
55
|
+
*
|
|
56
|
+
* @since 0.6.0
|
|
57
|
+
*/
|
|
58
|
+
var MailerooTimeoutError = class extends Error {
|
|
59
|
+
/**
|
|
60
|
+
* Request timeout in milliseconds.
|
|
61
|
+
*
|
|
62
|
+
* @since 0.6.0
|
|
63
|
+
*/
|
|
64
|
+
timeout;
|
|
65
|
+
/**
|
|
66
|
+
* Number of attempts made before this error was produced.
|
|
67
|
+
*
|
|
68
|
+
* @since 0.6.0
|
|
69
|
+
*/
|
|
70
|
+
attempts;
|
|
71
|
+
/**
|
|
72
|
+
* Creates a Maileroo request timeout error.
|
|
73
|
+
*
|
|
74
|
+
* @param timeout Request timeout in milliseconds.
|
|
75
|
+
* @param attempts Number of attempts made before this error.
|
|
76
|
+
*/
|
|
77
|
+
constructor(timeout, attempts) {
|
|
78
|
+
super(`Maileroo API request timed out after ${timeout} ms.`);
|
|
79
|
+
this.name = "MailerooTimeoutError";
|
|
80
|
+
this.timeout = timeout;
|
|
81
|
+
this.attempts = attempts;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* HTTP client wrapper for Maileroo API requests.
|
|
86
|
+
*
|
|
87
|
+
* @since 0.6.0
|
|
88
|
+
*/
|
|
89
|
+
var MailerooHttpClient = class {
|
|
90
|
+
config;
|
|
91
|
+
/**
|
|
92
|
+
* Creates a new Maileroo HTTP client.
|
|
93
|
+
*
|
|
94
|
+
* @param config Resolved Maileroo configuration.
|
|
95
|
+
*/
|
|
96
|
+
constructor(config) {
|
|
97
|
+
this.config = config;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Sends a single message via Maileroo API.
|
|
101
|
+
*
|
|
102
|
+
* @param messageData The JSON data to send to Maileroo.
|
|
103
|
+
* @param signal Optional AbortSignal for cancellation.
|
|
104
|
+
* @returns Promise that resolves to the Maileroo response.
|
|
105
|
+
*/
|
|
106
|
+
sendMessage(messageData, signal) {
|
|
107
|
+
const url = `${this.config.baseUrl}/emails`;
|
|
108
|
+
return this.makeRequest(url, messageData, signal);
|
|
109
|
+
}
|
|
110
|
+
async makeRequest(url, body, signal) {
|
|
111
|
+
let lastError = null;
|
|
112
|
+
for (let attempt = 0; attempt <= this.config.retries; attempt++) {
|
|
113
|
+
signal?.throwIfAborted();
|
|
114
|
+
try {
|
|
115
|
+
const response = await this.fetchWithAuth(url, body, signal);
|
|
116
|
+
const text = await response.text();
|
|
117
|
+
if (!response.ok) throw new MailerooApiError(parseErrorMessage(text, response.status), response.status, parseRetryAfter(response.headers.get("Retry-After")), attempt + 1);
|
|
118
|
+
try {
|
|
119
|
+
return JSON.parse(text);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
throw new SyntaxError(`Invalid JSON response from Maileroo API: ${error instanceof Error ? error.message : String(error)}.`);
|
|
122
|
+
}
|
|
123
|
+
} catch (error) {
|
|
124
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
125
|
+
if (error instanceof MailerooApiError && !isRetryable(error)) throw error;
|
|
126
|
+
if (error instanceof Error && error.name === "AbortError" && signal?.aborted) throw error;
|
|
127
|
+
if (attempt === this.config.retries) throw withAttempts(lastError, attempt + 1);
|
|
128
|
+
await sleep(calculateRetryDelay(attempt), signal);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
throw lastError ?? /* @__PURE__ */ new Error("Request failed after all retry attempts.");
|
|
132
|
+
}
|
|
133
|
+
async fetchWithAuth(url, body, signal) {
|
|
134
|
+
const headers = new Headers({
|
|
135
|
+
"Content-Type": "application/json",
|
|
136
|
+
"X-API-Key": this.config.apiKey
|
|
137
|
+
});
|
|
138
|
+
for (const [key, value] of Object.entries(this.config.headers)) headers.set(key, value);
|
|
139
|
+
const timeoutController = new AbortController();
|
|
140
|
+
const timeoutId = this.config.timeout > 0 ? setTimeout(() => timeoutController.abort(), this.config.timeout) : void 0;
|
|
141
|
+
const requestSignal = combineSignals(timeoutController.signal, signal);
|
|
142
|
+
try {
|
|
143
|
+
return await globalThis.fetch(url, {
|
|
144
|
+
method: "POST",
|
|
145
|
+
headers,
|
|
146
|
+
body: JSON.stringify(body),
|
|
147
|
+
signal: requestSignal.signal
|
|
148
|
+
});
|
|
149
|
+
} catch (error) {
|
|
150
|
+
if (error instanceof Error && error.name === "AbortError" && timeoutController.signal.aborted && !signal?.aborted) throw new MailerooTimeoutError(this.config.timeout);
|
|
151
|
+
throw error;
|
|
152
|
+
} finally {
|
|
153
|
+
requestSignal.cleanup();
|
|
154
|
+
if (timeoutId !== void 0) clearTimeout(timeoutId);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
function withAttempts(error, attempts) {
|
|
159
|
+
if (error instanceof MailerooTimeoutError) return new MailerooTimeoutError(error.timeout, attempts);
|
|
160
|
+
if (error instanceof MailerooApiError) return error;
|
|
161
|
+
return Object.assign(error, { attempts });
|
|
162
|
+
}
|
|
163
|
+
function isRetryable(error) {
|
|
164
|
+
return error.statusCode === 408 || error.statusCode === 429 || error.statusCode >= 500;
|
|
165
|
+
}
|
|
166
|
+
function calculateRetryDelay(attempt) {
|
|
167
|
+
const baseDelay = Math.min(1e3 * Math.pow(2, attempt), 1e4);
|
|
168
|
+
return Math.round(baseDelay / 2 + Math.random() * (baseDelay / 2));
|
|
169
|
+
}
|
|
170
|
+
function parseErrorMessage(text, statusCode) {
|
|
171
|
+
try {
|
|
172
|
+
const errorBody = JSON.parse(text);
|
|
173
|
+
if (typeof errorBody.message === "string" && errorBody.message !== "") return errorBody.message;
|
|
174
|
+
if (typeof errorBody.error === "string" && errorBody.error !== "") return errorBody.error;
|
|
175
|
+
if (Array.isArray(errorBody.errors) && errorBody.errors.length > 0) return JSON.stringify(errorBody.errors);
|
|
176
|
+
} catch {}
|
|
177
|
+
return text || `HTTP ${statusCode}`;
|
|
178
|
+
}
|
|
179
|
+
function sleep(ms, signal) {
|
|
180
|
+
return new Promise((resolve, reject) => {
|
|
181
|
+
if (signal?.aborted) {
|
|
182
|
+
reject(new DOMException("The operation was aborted.", "AbortError"));
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const onAbort = () => {
|
|
186
|
+
clearTimeout(timeoutId);
|
|
187
|
+
signal?.removeEventListener("abort", onAbort);
|
|
188
|
+
reject(new DOMException("The operation was aborted.", "AbortError"));
|
|
189
|
+
};
|
|
190
|
+
const timeoutId = setTimeout(() => {
|
|
191
|
+
signal?.removeEventListener("abort", onAbort);
|
|
192
|
+
resolve();
|
|
193
|
+
}, ms);
|
|
194
|
+
if (signal?.aborted) {
|
|
195
|
+
onAbort();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/message-converter.ts
|
|
204
|
+
const STANDARD_HEADERS = new Set([
|
|
205
|
+
"from",
|
|
206
|
+
"to",
|
|
207
|
+
"cc",
|
|
208
|
+
"bcc",
|
|
209
|
+
"reply-to",
|
|
210
|
+
"subject",
|
|
211
|
+
"date",
|
|
212
|
+
"message-id",
|
|
213
|
+
"content-type",
|
|
214
|
+
"content-transfer-encoding",
|
|
215
|
+
"mime-version",
|
|
216
|
+
"x-priority"
|
|
217
|
+
]);
|
|
218
|
+
/**
|
|
219
|
+
* Converts an Upyo message to Maileroo API JSON format.
|
|
220
|
+
*
|
|
221
|
+
* @param message The Upyo message to convert.
|
|
222
|
+
* @param config The resolved Maileroo configuration.
|
|
223
|
+
* @returns JSON object ready for Maileroo API submission.
|
|
224
|
+
* @since 0.6.0
|
|
225
|
+
*/
|
|
226
|
+
async function convertMessage(message, config) {
|
|
227
|
+
const emailData = {
|
|
228
|
+
from: convertAddress(message.sender),
|
|
229
|
+
to: convertAddressList(message.recipients),
|
|
230
|
+
subject: message.subject
|
|
231
|
+
};
|
|
232
|
+
const cc = convertOptionalAddressList(message.ccRecipients);
|
|
233
|
+
if (cc !== void 0) emailData.cc = cc;
|
|
234
|
+
const bcc = convertOptionalAddressList(message.bccRecipients);
|
|
235
|
+
if (bcc !== void 0) emailData.bcc = bcc;
|
|
236
|
+
const replyTo = convertOptionalAddressList(message.replyRecipients);
|
|
237
|
+
if (replyTo !== void 0) emailData.reply_to = replyTo;
|
|
238
|
+
if ("html" in message.content) {
|
|
239
|
+
emailData.html = message.content.html;
|
|
240
|
+
if (message.content.text !== void 0) emailData.plain = message.content.text;
|
|
241
|
+
} else emailData.plain = message.content.text;
|
|
242
|
+
if (config.tracking !== void 0) emailData.tracking = config.tracking;
|
|
243
|
+
const tags = convertTags(message, config);
|
|
244
|
+
if (Object.keys(tags).length > 0) emailData.tags = tags;
|
|
245
|
+
const headers = convertHeaders(message);
|
|
246
|
+
if (Object.keys(headers).length > 0) emailData.headers = headers;
|
|
247
|
+
if (message.attachments.length > 0) emailData.attachments = await Promise.all(message.attachments.map(convertAttachment));
|
|
248
|
+
return emailData;
|
|
249
|
+
}
|
|
250
|
+
function convertAddress(address) {
|
|
251
|
+
return address.name == null || address.name === "" ? { address: address.address } : {
|
|
252
|
+
address: address.address,
|
|
253
|
+
display_name: address.name
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
function convertAddressList(addresses) {
|
|
257
|
+
const converted = addresses.map(convertAddress);
|
|
258
|
+
return converted.length === 1 ? converted[0] : converted;
|
|
259
|
+
}
|
|
260
|
+
function convertOptionalAddressList(addresses) {
|
|
261
|
+
return addresses.length === 0 ? void 0 : convertAddressList(addresses);
|
|
262
|
+
}
|
|
263
|
+
function convertTags(message, config) {
|
|
264
|
+
const tags = config.tags == null ? {} : { ...config.tags };
|
|
265
|
+
for (const [index, tag] of message.tags.entries()) tags[`tag${index + 1}`] = tag;
|
|
266
|
+
return tags;
|
|
267
|
+
}
|
|
268
|
+
function convertHeaders(message) {
|
|
269
|
+
const headers = {};
|
|
270
|
+
if (message.priority !== "normal") {
|
|
271
|
+
const priorityMap = {
|
|
272
|
+
"high": "1",
|
|
273
|
+
"normal": "3",
|
|
274
|
+
"low": "5"
|
|
275
|
+
};
|
|
276
|
+
headers["X-Priority"] = priorityMap[message.priority];
|
|
277
|
+
}
|
|
278
|
+
for (const [key, value] of message.headers.entries()) if (!isStandardHeader(key)) headers[key] = value;
|
|
279
|
+
return headers;
|
|
280
|
+
}
|
|
281
|
+
async function convertAttachment(attachment) {
|
|
282
|
+
const content = await attachment.content;
|
|
283
|
+
return {
|
|
284
|
+
file_name: attachment.filename,
|
|
285
|
+
content_type: attachment.contentType,
|
|
286
|
+
content: uint8ArrayToBase64(content),
|
|
287
|
+
inline: attachment.inline || void 0
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function uint8ArrayToBase64(bytes) {
|
|
291
|
+
const nativeToBase64 = getNativeToBase64(bytes);
|
|
292
|
+
if (nativeToBase64 != null) return nativeToBase64();
|
|
293
|
+
const chunkSize = 32768;
|
|
294
|
+
const chunks = [];
|
|
295
|
+
for (let offset = 0; offset < bytes.length; offset += chunkSize) chunks.push(String.fromCharCode(...bytes.subarray(offset, offset + chunkSize)));
|
|
296
|
+
return btoa(chunks.join(""));
|
|
297
|
+
}
|
|
298
|
+
function getNativeToBase64(bytes) {
|
|
299
|
+
const candidate = bytes;
|
|
300
|
+
const toBase64 = candidate.toBase64;
|
|
301
|
+
if (typeof toBase64 !== "function") return void 0;
|
|
302
|
+
return () => toBase64.call(bytes);
|
|
303
|
+
}
|
|
304
|
+
function isStandardHeader(headerName) {
|
|
305
|
+
return STANDARD_HEADERS.has(headerName.toLowerCase());
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
//#endregion
|
|
309
|
+
//#region src/maileroo-transport.ts
|
|
310
|
+
/**
|
|
311
|
+
* Maileroo transport implementation for sending emails via Maileroo API.
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* ```typescript
|
|
315
|
+
* import { createMessage } from "@upyo/core";
|
|
316
|
+
* import { MailerooTransport } from "@upyo/maileroo";
|
|
317
|
+
*
|
|
318
|
+
* const transport = new MailerooTransport({
|
|
319
|
+
* apiKey: "your-sending-key",
|
|
320
|
+
* });
|
|
321
|
+
*
|
|
322
|
+
* const receipt = await transport.send(createMessage({
|
|
323
|
+
* from: "sender@example.com",
|
|
324
|
+
* to: "recipient@example.com",
|
|
325
|
+
* subject: "Hello from Maileroo",
|
|
326
|
+
* content: { text: "Hello!" },
|
|
327
|
+
* }));
|
|
328
|
+
* ```
|
|
329
|
+
*
|
|
330
|
+
* @since 0.6.0
|
|
331
|
+
*/
|
|
332
|
+
var MailerooTransport = class {
|
|
333
|
+
id = "maileroo";
|
|
334
|
+
/**
|
|
335
|
+
* The resolved Maileroo configuration used by this transport.
|
|
336
|
+
*/
|
|
337
|
+
config;
|
|
338
|
+
httpClient;
|
|
339
|
+
/**
|
|
340
|
+
* Creates a new Maileroo transport instance.
|
|
341
|
+
*
|
|
342
|
+
* @param config Maileroo configuration including API key and options.
|
|
343
|
+
*/
|
|
344
|
+
constructor(config) {
|
|
345
|
+
this.config = createMailerooConfig(config);
|
|
346
|
+
this.httpClient = new MailerooHttpClient(this.config);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Sends a single email message via Maileroo API.
|
|
350
|
+
*
|
|
351
|
+
* @param message The email message to send.
|
|
352
|
+
* @param options Optional transport options including `AbortSignal`.
|
|
353
|
+
* @returns A receipt indicating success or failure.
|
|
354
|
+
*/
|
|
355
|
+
async send(message, options) {
|
|
356
|
+
try {
|
|
357
|
+
options?.signal?.throwIfAborted();
|
|
358
|
+
const emailData = await convertMessage(message, this.config);
|
|
359
|
+
options?.signal?.throwIfAborted();
|
|
360
|
+
const response = await this.httpClient.sendMessage(emailData, options?.signal);
|
|
361
|
+
return responseToReceipt(response);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
if (isCallerAbort(error, options?.signal)) throw error;
|
|
364
|
+
return createMailerooFailure(error instanceof Error ? error.message : String(error), error);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Sends multiple email messages sequentially via Maileroo API.
|
|
369
|
+
*
|
|
370
|
+
* @param messages An iterable or async iterable of messages to send.
|
|
371
|
+
* @param options Optional transport options including `AbortSignal`.
|
|
372
|
+
* @returns An async iterable of receipts, one for each message.
|
|
373
|
+
*/
|
|
374
|
+
async *sendMany(messages, options) {
|
|
375
|
+
options?.signal?.throwIfAborted();
|
|
376
|
+
for await (const message of messages) {
|
|
377
|
+
options?.signal?.throwIfAborted();
|
|
378
|
+
yield await this.send(message, options);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
function responseToReceipt(response) {
|
|
383
|
+
if (!response.success) return createFailedReceipt(response.message ?? "Maileroo reported an unsuccessful response.", {
|
|
384
|
+
provider: "maileroo",
|
|
385
|
+
category: "rejected",
|
|
386
|
+
code: "maileroo.unsuccessful",
|
|
387
|
+
retryable: false,
|
|
388
|
+
providerDetails: response
|
|
389
|
+
});
|
|
390
|
+
const messageId = response.data?.reference_id;
|
|
391
|
+
if (messageId == null || messageId === "") return createFailedReceipt("Maileroo response is missing a reference ID.", {
|
|
392
|
+
provider: "maileroo",
|
|
393
|
+
category: "unknown",
|
|
394
|
+
code: "maileroo.missing_reference_id",
|
|
395
|
+
retryable: false,
|
|
396
|
+
providerDetails: response
|
|
397
|
+
});
|
|
398
|
+
return {
|
|
399
|
+
successful: true,
|
|
400
|
+
messageId,
|
|
401
|
+
provider: "maileroo"
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function createMailerooFailure(message, error) {
|
|
405
|
+
if (error instanceof MailerooApiError) return createFailedReceipt(message, {
|
|
406
|
+
provider: "maileroo",
|
|
407
|
+
statusCode: error.statusCode,
|
|
408
|
+
retryAfterMilliseconds: error.retryAfterMilliseconds,
|
|
409
|
+
attempts: error.attempts
|
|
410
|
+
});
|
|
411
|
+
if (error instanceof MailerooTimeoutError) return createFailedReceipt(message, {
|
|
412
|
+
provider: "maileroo",
|
|
413
|
+
category: "timeout",
|
|
414
|
+
code: "timeout",
|
|
415
|
+
retryable: true,
|
|
416
|
+
attempts: error.attempts
|
|
417
|
+
});
|
|
418
|
+
return createFailedReceipt(message, {
|
|
419
|
+
provider: "maileroo",
|
|
420
|
+
attempts: getErrorAttempts(error)
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
function getErrorAttempts(error) {
|
|
424
|
+
if (typeof error !== "object" || error == null || !("attempts" in error)) return void 0;
|
|
425
|
+
const attempts = error.attempts;
|
|
426
|
+
return typeof attempts === "number" ? attempts : void 0;
|
|
427
|
+
}
|
|
428
|
+
function isAbortError(error) {
|
|
429
|
+
return error instanceof Error && error.name === "AbortError";
|
|
430
|
+
}
|
|
431
|
+
function isCallerAbort(error, signal) {
|
|
432
|
+
return signal?.aborted === true && (isAbortError(error) || error === signal.reason);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
//#endregion
|
|
436
|
+
export { MailerooApiError, MailerooTimeoutError, MailerooTransport, createMailerooConfig };
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@upyo/maileroo",
|
|
3
|
+
"version": "0.6.0-dev.0",
|
|
4
|
+
"description": "Maileroo transport for Upyo email library",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"email",
|
|
7
|
+
"mail",
|
|
8
|
+
"sendmail",
|
|
9
|
+
"maileroo"
|
|
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/transports/maileroo",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/dahlia/upyo.git",
|
|
21
|
+
"directory": "packages/maileroo/"
|
|
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
|
+
"./package.json": "./package.json"
|
|
53
|
+
},
|
|
54
|
+
"sideEffects": false,
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"@upyo/core": "0.6.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"tsdown": "^0.12.7",
|
|
60
|
+
"typescript": "5.8.3"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"prepublish": "mise run --no-deps :build"
|
|
64
|
+
}
|
|
65
|
+
}
|