mailchannels-sdk 0.8.0 → 1.0.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 +22 -9
- package/dist/mailchannels.d.mts +9 -4
- package/dist/mailchannels.mjs +73 -88
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-

|
|
2
2
|
|
|
3
3
|
# MailChannels Node.js SDK
|
|
4
4
|
|
|
5
5
|
[![npm version][npm-version-src]][npm-version-href]
|
|
6
6
|
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
7
|
+
[![Build Status][build-status-src]][build-status-href]
|
|
8
|
+
[![License][license-src]][license-href]
|
|
9
|
+
[![TypeScript][typescript-src]][typescript-href]
|
|
10
|
+
[![Node.js][node-src]][node-href]
|
|
7
11
|
|
|
8
|
-
> Built and tested against Email API `0.
|
|
12
|
+
> Built and tested against Email API `1.0.0`
|
|
9
13
|
|
|
10
14
|
Node.js SDK to integrate [MailChannels Email API](https://docs.mailchannels.net/email-api) into your JavaScript or TypeScript server-side applications.
|
|
11
15
|
|
|
@@ -13,7 +17,7 @@ Node.js SDK to integrate [MailChannels Email API](https://docs.mailchannels.net/
|
|
|
13
17
|
This library provides a simple way to interact with the [MailChannels Email API](https://docs.mailchannels.net/email-api). It is written in TypeScript and can be used in both JavaScript and TypeScript projects and in different runtimes.
|
|
14
18
|
<!-- #endregion overview -->
|
|
15
19
|
|
|
16
|
-
- [✨ Release Notes](CHANGELOG.md)
|
|
20
|
+
- [✨ Release Notes](https://bitbucket.org/mailchannels/mailchannels-email-api-sdk-js/src/HEAD/CHANGELOG.md)
|
|
17
21
|
- [📖 Documentation](https://mailchannels.yizack.com)
|
|
18
22
|
|
|
19
23
|
## Contents
|
|
@@ -180,12 +184,11 @@ The next planned expansion is outbound webhook delivery so client applications c
|
|
|
180
184
|
|
|
181
185
|
## <a name="license">⚖️ License</a>
|
|
182
186
|
|
|
183
|
-
[MIT License](LICENSE)
|
|
187
|
+
[MIT License](https://bitbucket.org/mailchannels/mailchannels-email-api-sdk-js/src/HEAD/LICENSE)
|
|
184
188
|
|
|
185
189
|
## <a name="development">💻 Development</a>
|
|
186
190
|
|
|
187
|
-
|
|
188
|
-
<summary>Local development</summary>
|
|
191
|
+
Local development
|
|
189
192
|
|
|
190
193
|
```sh
|
|
191
194
|
# Install dependencies
|
|
@@ -204,7 +207,7 @@ pnpm test:watch
|
|
|
204
207
|
# Run typecheck
|
|
205
208
|
pnpm test:types
|
|
206
209
|
|
|
207
|
-
# Refresh API parity fixtures
|
|
210
|
+
# Refresh API parity fixtures, specs, and README version note
|
|
208
211
|
pnpm parity:fixtures
|
|
209
212
|
|
|
210
213
|
# Run the local simulator
|
|
@@ -217,11 +220,21 @@ pnpx jiti playground/emails/send.ts
|
|
|
217
220
|
pnpm release
|
|
218
221
|
```
|
|
219
222
|
|
|
220
|
-
</details>
|
|
221
|
-
|
|
222
223
|
<!-- Badges -->
|
|
223
224
|
[npm-version-src]: https://img.shields.io/npm/v/mailchannels-sdk.svg?style=flat&colorA=070a30&colorB=35a047
|
|
224
225
|
[npm-version-href]: https://npmjs.com/package/mailchannels-sdk
|
|
225
226
|
|
|
226
227
|
[npm-downloads-src]: https://img.shields.io/npm/dm/mailchannels-sdk.svg?style=flat&colorA=070a30&colorB=35a047
|
|
227
228
|
[npm-downloads-href]: https://npmjs.com/package/mailchannels-sdk
|
|
229
|
+
|
|
230
|
+
[build-status-src]: https://img.shields.io/bitbucket/pipelines/mailchannels/mailchannels-email-api-sdk-js/main.svg?style=flat&colorA=070a30
|
|
231
|
+
[build-status-href]: https://bitbucket.org/mailchannels/mailchannels-email-api-sdk-js/pipelines
|
|
232
|
+
|
|
233
|
+
[license-src]: https://img.shields.io/npm/l/mailchannels-sdk.svg?style=flat&colorA=070a30&colorB=35a047
|
|
234
|
+
[license-href]: https://bitbucket.org/mailchannels/mailchannels-email-api-sdk-js/src/HEAD/LICENSE
|
|
235
|
+
|
|
236
|
+
[typescript-src]: https://img.shields.io/badge/TypeScript-supported-35a047?style=flat&colorA=070a30
|
|
237
|
+
[typescript-href]: https://www.typescriptlang.org
|
|
238
|
+
|
|
239
|
+
[node-src]: https://img.shields.io/node/v/mailchannels-sdk.svg?style=flat&colorA=070a30&colorB=35a047
|
|
240
|
+
[node-href]: https://bitbucket.org/mailchannels/mailchannels-email-api-sdk-js/src/HEAD/package.json
|
package/dist/mailchannels.d.mts
CHANGED
|
@@ -44,6 +44,7 @@ declare class MailChannelsClient {
|
|
|
44
44
|
put<T>(path: string, options?: Omit<FetchOptions<"json">, "method">): Promise<T>;
|
|
45
45
|
patch<T>(path: string, options?: Omit<FetchOptions<"json">, "method">): Promise<T>;
|
|
46
46
|
}
|
|
47
|
+
type ErrorType = "invalid_request_error" | "authentication_error" | "permission_error" | "not_found" | "conflict_error" | "payload_too_large_error" | "unprocessable_entity_error" | "rate_limit_error" | "internal_server_error" | "validation_error" | "application_error" | "api_error";
|
|
47
48
|
interface ErrorResponse {
|
|
48
49
|
/**
|
|
49
50
|
* A human-readable description of the error.
|
|
@@ -55,6 +56,12 @@ interface ErrorResponse {
|
|
|
55
56
|
* This field is intended for diagnostic use only and should not be relied upon.
|
|
56
57
|
*/
|
|
57
58
|
statusCode: number | null;
|
|
59
|
+
/**
|
|
60
|
+
* A string identifier for the type of error.
|
|
61
|
+
*
|
|
62
|
+
* This field is intended for diagnostic use only and should not be relied upon.
|
|
63
|
+
*/
|
|
64
|
+
type: ErrorType;
|
|
58
65
|
}
|
|
59
66
|
interface SuccessResponse {
|
|
60
67
|
/**
|
|
@@ -1760,9 +1767,7 @@ declare class Suppressions {
|
|
|
1760
1767
|
type AttachmentOptions = Omit<EmailsSendAttachment, "content">;
|
|
1761
1768
|
declare class Attachment {
|
|
1762
1769
|
static fromBytes(data: ArrayBuffer | Uint8Array, options: AttachmentOptions): EmailsSendAttachment;
|
|
1763
|
-
static
|
|
1764
|
-
static fromUrl(url: string, options?: Partial<AttachmentOptions>): Promise<EmailsSendAttachment>;
|
|
1765
|
-
static inlineFile(path: string | URL, options?: Partial<Omit<AttachmentOptions, "disposition">>): Promise<EmailsSendAttachment>;
|
|
1770
|
+
static fromBlob(blob: Blob, options: AttachmentOptions): Promise<EmailsSendAttachment>;
|
|
1766
1771
|
}
|
|
1767
1772
|
declare class MailChannels extends MailChannelsClient {
|
|
1768
1773
|
readonly emails: Emails;
|
|
@@ -1773,4 +1778,4 @@ declare class MailChannels extends MailChannelsClient {
|
|
|
1773
1778
|
readonly suppressions: Suppressions;
|
|
1774
1779
|
constructor(key: string, options?: MailChannelsClientOptions);
|
|
1775
1780
|
}
|
|
1776
|
-
export { Attachment, type DataResponse, Domains, type DomainsCheckOptions, type DomainsCheckResponse, type DomainsCheckVerdict, type DomainsDkimCreateOptions, type DomainsDkimCreateResponse, type DomainsDkimKey, type DomainsDkimKeyStatus, type DomainsDkimListOptions, type DomainsDkimListResponse, type DomainsDkimRotateOptions, type DomainsDkimRotateResponse, type DomainsDkimUpdateStatusOptions, Emails, type EmailsQueueResponse, type EmailsSendAsyncResponse, type EmailsSendAttachment, type EmailsSendContent, type EmailsSendDkim, type EmailsSendOptions, type EmailsSendPersonalization, type EmailsSendRecipient, type EmailsSendRecipientInput, type EmailsSendResponse, type EmailsSendTemplate, type EmailsSendTemplateType, type EmailsSendTemplateValue, type EmailsSendTracking, type ErrorResponse, MailChannels, MailChannelsClient, type MailChannelsClientOptions, Metrics, type MetricsBucket, type MetricsEngagement, type MetricsEngagementResponse, type MetricsOptions, type MetricsPerformance, type MetricsPerformanceResponse, type MetricsRecipientBehaviour, type MetricsRecipientBehaviourResponse, type MetricsSenders, type MetricsSendersOptions, type MetricsSendersResponse, type MetricsSendersType, type MetricsUsageResponse, type MetricsVolume, type MetricsVolumeResponse, SubAccounts, type SubAccountsAccount, type SubAccountsApiKey, type SubAccountsCreateApiKeyResponse, type SubAccountsCreateResponse, type SubAccountsCreateSmtpPasswordResponse, type SubAccountsLimit, type SubAccountsLimitResponse, type SubAccountsListApiKeyOptions, type SubAccountsListApiKeyResponse, type SubAccountsListOptions, type SubAccountsListResponse, type SubAccountsListSmtpPasswordResponse, type SubAccountsSmtpPassword, type SubAccountsUsage, type SubAccountsUsageResponse, type SuccessResponse, Suppressions, type SuppressionsCreateOptions, type SuppressionsListEntry, type SuppressionsListOptions, type SuppressionsListResponse, type SuppressionsSource, type SuppressionsTypes, type WebhookEvent, type WebhookEventClick, type WebhookEventComplained, type WebhookEventDelivered, type WebhookEventDropped, type WebhookEventHardBounced, type WebhookEventOpen, type WebhookEventProcessed, type WebhookEventSoftBounced, type WebhookEventTest, type WebhookEventType, type WebhookEventUnsubscribed, Webhooks, type WebhooksBatch, type WebhooksBatchResponseStatus, type WebhooksBatchStatus, type WebhooksBatchesOptions, type WebhooksBatchesResponse, type WebhooksListResponse, type WebhooksResendBatch, type WebhooksResendBatchResponse, type WebhooksSigningKeyResponse, type WebhooksValidateResponse, type WebhooksVerifyOptions, type WebhooksVerifyResponse };
|
|
1781
|
+
export { Attachment, type DataResponse, Domains, type DomainsCheckOptions, type DomainsCheckResponse, type DomainsCheckVerdict, type DomainsDkimCreateOptions, type DomainsDkimCreateResponse, type DomainsDkimKey, type DomainsDkimKeyStatus, type DomainsDkimListOptions, type DomainsDkimListResponse, type DomainsDkimRotateOptions, type DomainsDkimRotateResponse, type DomainsDkimUpdateStatusOptions, Emails, type EmailsQueueResponse, type EmailsSendAsyncResponse, type EmailsSendAttachment, type EmailsSendContent, type EmailsSendDkim, type EmailsSendOptions, type EmailsSendPersonalization, type EmailsSendRecipient, type EmailsSendRecipientInput, type EmailsSendResponse, type EmailsSendTemplate, type EmailsSendTemplateType, type EmailsSendTemplateValue, type EmailsSendTracking, type ErrorResponse, type ErrorType, MailChannels, MailChannelsClient, type MailChannelsClientOptions, Metrics, type MetricsBucket, type MetricsEngagement, type MetricsEngagementResponse, type MetricsOptions, type MetricsPerformance, type MetricsPerformanceResponse, type MetricsRecipientBehaviour, type MetricsRecipientBehaviourResponse, type MetricsSenders, type MetricsSendersOptions, type MetricsSendersResponse, type MetricsSendersType, type MetricsUsageResponse, type MetricsVolume, type MetricsVolumeResponse, SubAccounts, type SubAccountsAccount, type SubAccountsApiKey, type SubAccountsCreateApiKeyResponse, type SubAccountsCreateResponse, type SubAccountsCreateSmtpPasswordResponse, type SubAccountsLimit, type SubAccountsLimitResponse, type SubAccountsListApiKeyOptions, type SubAccountsListApiKeyResponse, type SubAccountsListOptions, type SubAccountsListResponse, type SubAccountsListSmtpPasswordResponse, type SubAccountsSmtpPassword, type SubAccountsUsage, type SubAccountsUsageResponse, type SuccessResponse, Suppressions, type SuppressionsCreateOptions, type SuppressionsListEntry, type SuppressionsListOptions, type SuppressionsListResponse, type SuppressionsSource, type SuppressionsTypes, type WebhookEvent, type WebhookEventClick, type WebhookEventComplained, type WebhookEventDelivered, type WebhookEventDropped, type WebhookEventHardBounced, type WebhookEventOpen, type WebhookEventProcessed, type WebhookEventSoftBounced, type WebhookEventTest, type WebhookEventType, type WebhookEventUnsubscribed, Webhooks, type WebhooksBatch, type WebhooksBatchResponseStatus, type WebhooksBatchStatus, type WebhooksBatchesOptions, type WebhooksBatchesResponse, type WebhooksListResponse, type WebhooksResendBatch, type WebhooksResendBatchResponse, type WebhooksSigningKeyResponse, type WebhooksValidateResponse, type WebhooksVerifyOptions, type WebhooksVerifyResponse };
|
package/dist/mailchannels.mjs
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { $fetch } from "ofetch";
|
|
2
2
|
import { subtle } from "node:crypto";
|
|
3
3
|
import { Buffer } from "node:buffer";
|
|
4
|
-
import { basename } from "node:path";
|
|
5
|
-
import { readFile } from "node:fs/promises";
|
|
6
4
|
import mime from "mime";
|
|
7
|
-
var version = "0.
|
|
5
|
+
var version = "1.0.0";
|
|
8
6
|
var MailChannelsClient = class MailChannelsClient {
|
|
9
7
|
static DEFAULT_BASE_URL = "https://api.mailchannels.net";
|
|
10
8
|
static DEFAULT_TIMEOUT = 12e4;
|
|
@@ -70,10 +68,22 @@ var MailChannelsClient = class MailChannelsClient {
|
|
|
70
68
|
});
|
|
71
69
|
}
|
|
72
70
|
};
|
|
73
|
-
const
|
|
71
|
+
const STATUS_ERROR_TYPE_MAP = {
|
|
72
|
+
[400]: "invalid_request_error",
|
|
73
|
+
[401]: "authentication_error",
|
|
74
|
+
[403]: "permission_error",
|
|
75
|
+
[404]: "not_found",
|
|
76
|
+
[409]: "conflict_error",
|
|
77
|
+
[413]: "payload_too_large_error",
|
|
78
|
+
[422]: "unprocessable_entity_error",
|
|
79
|
+
[429]: "rate_limit_error",
|
|
80
|
+
[500]: "internal_server_error"
|
|
81
|
+
};
|
|
82
|
+
const createError = (message, statusCode = null, type) => {
|
|
74
83
|
return {
|
|
75
84
|
message,
|
|
76
|
-
statusCode
|
|
85
|
+
statusCode,
|
|
86
|
+
type
|
|
77
87
|
};
|
|
78
88
|
};
|
|
79
89
|
const getStatusError = (response, errors = {}) => {
|
|
@@ -83,15 +93,18 @@ const getStatusError = (response, errors = {}) => {
|
|
|
83
93
|
if (typeof payload === "string") details = payload;
|
|
84
94
|
else if (payload?.message) details = payload.message;
|
|
85
95
|
else if (Array.isArray(payload?.errors) && payload.errors?.length) details = payload.errors.join(", ");
|
|
86
|
-
return createError(details ? `${statusText} ${details}` : statusText, response.status ?? null);
|
|
96
|
+
return createError(details ? `${statusText} ${details}` : statusText, response.status ?? null, STATUS_ERROR_TYPE_MAP[response.status] || "api_error");
|
|
87
97
|
};
|
|
88
98
|
const getResultError = (e, fallback) => {
|
|
89
|
-
return createError(e instanceof Error ? e.message : fallback);
|
|
99
|
+
return createError(e instanceof Error ? e.message : fallback, null, "application_error");
|
|
100
|
+
};
|
|
101
|
+
const createValidationError = (message) => {
|
|
102
|
+
return createError(message, null, "validation_error");
|
|
90
103
|
};
|
|
91
104
|
const validatePagination = (pagination = {}) => {
|
|
92
105
|
const { limit, offset, max } = pagination;
|
|
93
|
-
if (typeof limit === "number" && (limit < 1 || max && limit > max)) return
|
|
94
|
-
if (typeof offset === "number" && offset < 0) return
|
|
106
|
+
if (typeof limit === "number" && (limit < 1 || max && limit > max)) return createValidationError("The limit value " + (max ? `must be between 1 and ${max}.` : "is invalid. Only positive values are allowed."));
|
|
107
|
+
if (typeof offset === "number" && offset < 0) return createValidationError("Offset must be greater than or equal to 0.");
|
|
95
108
|
return null;
|
|
96
109
|
};
|
|
97
110
|
const clean = (data) => {
|
|
@@ -243,7 +256,8 @@ const buildSendPayload = async (options) => {
|
|
|
243
256
|
const { from, html, text } = options;
|
|
244
257
|
const contentTypes = options.content ? new Set(options.content.map((item) => item.type.toLowerCase())) : void 0;
|
|
245
258
|
const parsedFrom = parseRecipient(from);
|
|
246
|
-
if (!parsedFrom || !parsedFrom.email) return "No sender provided. Use the
|
|
259
|
+
if (!parsedFrom || !parsedFrom.email) return "No sender provided. Use the 'from' option to specify a sender.";
|
|
260
|
+
if (!options.subject || typeof options.subject !== "string" || !options.subject.trim()) return "No subject provided. Use the 'subject' option to specify a subject.";
|
|
247
261
|
if (!text && !html && (!options.content || !options.content.length)) return "No email content provided";
|
|
248
262
|
if (html && contentTypes?.has("text/html")) return "Cannot provide both 'html' and a 'content' entry with type 'text/html'.";
|
|
249
263
|
if (text && contentTypes?.has("text/plain")) return "Cannot provide both 'text' and a 'content' entry with type 'text/plain'.";
|
|
@@ -337,7 +351,7 @@ var Emails = class {
|
|
|
337
351
|
let error = null;
|
|
338
352
|
const payload = await buildSendPayload(options);
|
|
339
353
|
if (typeof payload === "string") {
|
|
340
|
-
error =
|
|
354
|
+
error = createValidationError(payload);
|
|
341
355
|
return {
|
|
342
356
|
data: null,
|
|
343
357
|
error
|
|
@@ -420,7 +434,7 @@ var Domains = class {
|
|
|
420
434
|
async check(domain, options) {
|
|
421
435
|
let error = null;
|
|
422
436
|
if (!domain) {
|
|
423
|
-
error =
|
|
437
|
+
error = createValidationError("No domain provided.");
|
|
424
438
|
return {
|
|
425
439
|
data: null,
|
|
426
440
|
error
|
|
@@ -428,14 +442,14 @@ var Domains = class {
|
|
|
428
442
|
}
|
|
429
443
|
const dkimOptions = options?.dkim ? Array.isArray(options.dkim) ? options.dkim : [options.dkim] : void 0;
|
|
430
444
|
if (dkimOptions && dkimOptions.length > 10) {
|
|
431
|
-
error =
|
|
445
|
+
error = createValidationError("A maximum of 10 DKIM settings can be provided.");
|
|
432
446
|
return {
|
|
433
447
|
data: null,
|
|
434
448
|
error
|
|
435
449
|
};
|
|
436
450
|
}
|
|
437
451
|
if (dkimOptions?.find(({ privateKey, selector }) => privateKey && !selector)) {
|
|
438
|
-
error =
|
|
452
|
+
error = createValidationError("DKIM settings with a privateKey must also include a selector.");
|
|
439
453
|
return {
|
|
440
454
|
data: null,
|
|
441
455
|
error
|
|
@@ -492,14 +506,14 @@ var DomainsDkim = class {
|
|
|
492
506
|
async create(domain, options) {
|
|
493
507
|
let error = null;
|
|
494
508
|
if (!domain) {
|
|
495
|
-
error =
|
|
509
|
+
error = createValidationError("No domain provided.");
|
|
496
510
|
return {
|
|
497
511
|
data: null,
|
|
498
512
|
error
|
|
499
513
|
};
|
|
500
514
|
}
|
|
501
515
|
if (!options.selector || options.selector.length > 63) {
|
|
502
|
-
error =
|
|
516
|
+
error = createValidationError("Selector must be between 1 and 63 characters.");
|
|
503
517
|
return {
|
|
504
518
|
data: null,
|
|
505
519
|
error
|
|
@@ -534,14 +548,14 @@ var DomainsDkim = class {
|
|
|
534
548
|
async list(domain, options) {
|
|
535
549
|
let error = null;
|
|
536
550
|
if (!domain) {
|
|
537
|
-
error =
|
|
551
|
+
error = createValidationError("No domain provided.");
|
|
538
552
|
return {
|
|
539
553
|
data: null,
|
|
540
554
|
error
|
|
541
555
|
};
|
|
542
556
|
}
|
|
543
557
|
if (options?.selector && options.selector.length > 63) {
|
|
544
|
-
error =
|
|
558
|
+
error = createValidationError("Selector must be between 1 and 63 characters.");
|
|
545
559
|
return {
|
|
546
560
|
data: null,
|
|
547
561
|
error
|
|
@@ -583,14 +597,14 @@ var DomainsDkim = class {
|
|
|
583
597
|
async updateStatus(domain, options) {
|
|
584
598
|
let error = null;
|
|
585
599
|
if (!domain) {
|
|
586
|
-
error =
|
|
600
|
+
error = createValidationError("No domain provided.");
|
|
587
601
|
return {
|
|
588
602
|
success: false,
|
|
589
603
|
error
|
|
590
604
|
};
|
|
591
605
|
}
|
|
592
606
|
if (!options.selector || options.selector.length > 63) {
|
|
593
|
-
error =
|
|
607
|
+
error = createValidationError("Selector must be between 1 and 63 characters.");
|
|
594
608
|
return {
|
|
595
609
|
success: false,
|
|
596
610
|
error
|
|
@@ -616,21 +630,21 @@ var DomainsDkim = class {
|
|
|
616
630
|
async rotate(domain, selector, options) {
|
|
617
631
|
let error = null;
|
|
618
632
|
if (!domain) {
|
|
619
|
-
error =
|
|
633
|
+
error = createValidationError("No domain provided.");
|
|
620
634
|
return {
|
|
621
635
|
data: null,
|
|
622
636
|
error
|
|
623
637
|
};
|
|
624
638
|
}
|
|
625
639
|
if (!selector || selector.length > 63) {
|
|
626
|
-
error =
|
|
640
|
+
error = createValidationError("Selector must be between 1 and 63 characters.");
|
|
627
641
|
return {
|
|
628
642
|
data: null,
|
|
629
643
|
error
|
|
630
644
|
};
|
|
631
645
|
}
|
|
632
646
|
if (!options.newKey.selector || options.newKey.selector.length > 63) {
|
|
633
|
-
error =
|
|
647
|
+
error = createValidationError("New key selector must be between 1 and 63 characters.");
|
|
634
648
|
return {
|
|
635
649
|
data: null,
|
|
636
650
|
error
|
|
@@ -743,14 +757,14 @@ var Webhooks = class Webhooks {
|
|
|
743
757
|
async create(endpoint) {
|
|
744
758
|
let error = null;
|
|
745
759
|
if (!endpoint) {
|
|
746
|
-
error =
|
|
760
|
+
error = createValidationError("No endpoint provided.");
|
|
747
761
|
return {
|
|
748
762
|
success: false,
|
|
749
763
|
error
|
|
750
764
|
};
|
|
751
765
|
}
|
|
752
766
|
if (endpoint.length > 8e3) {
|
|
753
|
-
error =
|
|
767
|
+
error = createValidationError("The endpoint exceeds the maximum length of 8000 characters.");
|
|
754
768
|
return {
|
|
755
769
|
success: false,
|
|
756
770
|
error
|
|
@@ -827,7 +841,7 @@ var Webhooks = class Webhooks {
|
|
|
827
841
|
async validate(requestId) {
|
|
828
842
|
let error = null;
|
|
829
843
|
if (requestId && requestId.length > 28) {
|
|
830
|
-
error =
|
|
844
|
+
error = createValidationError("The request id should not exceed 28 characters.");
|
|
831
845
|
return {
|
|
832
846
|
data: null,
|
|
833
847
|
error
|
|
@@ -860,7 +874,7 @@ var Webhooks = class Webhooks {
|
|
|
860
874
|
static async verify(options) {
|
|
861
875
|
let error = null;
|
|
862
876
|
if (!await isValidWebhook(options).catch(() => false)) {
|
|
863
|
-
error =
|
|
877
|
+
error = createValidationError("Invalid webhook signature.");
|
|
864
878
|
return {
|
|
865
879
|
data: null,
|
|
866
880
|
error
|
|
@@ -869,7 +883,7 @@ var Webhooks = class Webhooks {
|
|
|
869
883
|
try {
|
|
870
884
|
const payload = JSON.parse(options.payload);
|
|
871
885
|
if (!Array.isArray(payload)) {
|
|
872
|
-
error =
|
|
886
|
+
error = createValidationError("Invalid webhook payload.");
|
|
873
887
|
return {
|
|
874
888
|
data: null,
|
|
875
889
|
error
|
|
@@ -894,7 +908,7 @@ var Webhooks = class Webhooks {
|
|
|
894
908
|
error: null
|
|
895
909
|
};
|
|
896
910
|
} catch {
|
|
897
|
-
error =
|
|
911
|
+
error = createValidationError("Invalid webhook payload.");
|
|
898
912
|
return {
|
|
899
913
|
data: null,
|
|
900
914
|
error
|
|
@@ -916,19 +930,19 @@ var Webhooks = class Webhooks {
|
|
|
916
930
|
};
|
|
917
931
|
if (options?.statuses && options.statuses.length > 6) return {
|
|
918
932
|
data: null,
|
|
919
|
-
error:
|
|
933
|
+
error: createValidationError("A maximum of 6 status filters can be provided.")
|
|
920
934
|
};
|
|
921
935
|
if (options?.statuses && new Set(options.statuses).size !== options.statuses.length) return {
|
|
922
936
|
data: null,
|
|
923
|
-
error:
|
|
937
|
+
error: createValidationError("Status filters must be unique.")
|
|
924
938
|
};
|
|
925
939
|
if (options?.createdAfter && Number.isNaN(Date.parse(options.createdAfter))) return {
|
|
926
940
|
data: null,
|
|
927
|
-
error:
|
|
941
|
+
error: createValidationError("createdAfter must be a valid date string.")
|
|
928
942
|
};
|
|
929
943
|
if (options?.createdBefore && Number.isNaN(Date.parse(options.createdBefore))) return {
|
|
930
944
|
data: null,
|
|
931
|
-
error:
|
|
945
|
+
error: createValidationError("createdBefore must be a valid date string.")
|
|
932
946
|
};
|
|
933
947
|
if (options?.createdAfter && options?.createdBefore) {
|
|
934
948
|
const createdAfter = Date.parse(options.createdAfter);
|
|
@@ -936,11 +950,11 @@ var Webhooks = class Webhooks {
|
|
|
936
950
|
const maxRangeMs = 744 * 60 * 60 * 1e3;
|
|
937
951
|
if (createdBefore <= createdAfter) return {
|
|
938
952
|
data: null,
|
|
939
|
-
error:
|
|
953
|
+
error: createValidationError("createdBefore must be later than createdAfter.")
|
|
940
954
|
};
|
|
941
955
|
if (createdBefore - createdAfter > maxRangeMs) return {
|
|
942
956
|
data: null,
|
|
943
|
-
error:
|
|
957
|
+
error: createValidationError("The time range between createdAfter and createdBefore must not exceed 31 days.")
|
|
944
958
|
};
|
|
945
959
|
}
|
|
946
960
|
const response = await this.mailchannels.get("/tx/v1/webhook-batch", {
|
|
@@ -1016,7 +1030,7 @@ var SubAccounts = class SubAccounts {
|
|
|
1016
1030
|
async create(companyName, handle) {
|
|
1017
1031
|
let error = null;
|
|
1018
1032
|
if (!SubAccounts.COMPANY_PATTERN.test(companyName)) {
|
|
1019
|
-
error =
|
|
1033
|
+
error = createValidationError("Invalid company name. Company name must be between 3 and 128 characters.");
|
|
1020
1034
|
return {
|
|
1021
1035
|
data: null,
|
|
1022
1036
|
error
|
|
@@ -1024,7 +1038,7 @@ var SubAccounts = class SubAccounts {
|
|
|
1024
1038
|
}
|
|
1025
1039
|
if (handle) {
|
|
1026
1040
|
if (!SubAccounts.HANDLE_PATTERN.test(handle)) {
|
|
1027
|
-
error =
|
|
1041
|
+
error = createValidationError("Invalid handle. Sub-account handle must be between 3 and 128 characters and contain only lowercase letters and numbers.");
|
|
1028
1042
|
return {
|
|
1029
1043
|
data: null,
|
|
1030
1044
|
error
|
|
@@ -1094,7 +1108,7 @@ var SubAccounts = class SubAccounts {
|
|
|
1094
1108
|
async delete(handle) {
|
|
1095
1109
|
let error = null;
|
|
1096
1110
|
if (!handle) {
|
|
1097
|
-
error =
|
|
1111
|
+
error = createValidationError("No handle provided.");
|
|
1098
1112
|
return {
|
|
1099
1113
|
success: false,
|
|
1100
1114
|
error
|
|
@@ -1113,7 +1127,7 @@ var SubAccounts = class SubAccounts {
|
|
|
1113
1127
|
async suspend(handle) {
|
|
1114
1128
|
let error = null;
|
|
1115
1129
|
if (!handle) {
|
|
1116
|
-
error =
|
|
1130
|
+
error = createValidationError("No handle provided.");
|
|
1117
1131
|
return {
|
|
1118
1132
|
success: false,
|
|
1119
1133
|
error
|
|
@@ -1132,7 +1146,7 @@ var SubAccounts = class SubAccounts {
|
|
|
1132
1146
|
async activate(handle) {
|
|
1133
1147
|
let error = null;
|
|
1134
1148
|
if (!handle) {
|
|
1135
|
-
error =
|
|
1149
|
+
error = createValidationError("No handle provided.");
|
|
1136
1150
|
return {
|
|
1137
1151
|
success: false,
|
|
1138
1152
|
error
|
|
@@ -1154,7 +1168,7 @@ var SubAccounts = class SubAccounts {
|
|
|
1154
1168
|
async createApiKey(handle) {
|
|
1155
1169
|
let error = null;
|
|
1156
1170
|
if (!handle) {
|
|
1157
|
-
error =
|
|
1171
|
+
error = createValidationError("No handle provided.");
|
|
1158
1172
|
return {
|
|
1159
1173
|
data: null,
|
|
1160
1174
|
error
|
|
@@ -1182,7 +1196,7 @@ var SubAccounts = class SubAccounts {
|
|
|
1182
1196
|
async listApiKeys(handle, options) {
|
|
1183
1197
|
let error = null;
|
|
1184
1198
|
if (!handle) {
|
|
1185
|
-
error =
|
|
1199
|
+
error = createValidationError("No handle provided.");
|
|
1186
1200
|
return {
|
|
1187
1201
|
data: null,
|
|
1188
1202
|
error
|
|
@@ -1217,7 +1231,7 @@ var SubAccounts = class SubAccounts {
|
|
|
1217
1231
|
async deleteApiKey(handle, id) {
|
|
1218
1232
|
let error = null;
|
|
1219
1233
|
if (!handle) {
|
|
1220
|
-
error =
|
|
1234
|
+
error = createValidationError("No handle provided.");
|
|
1221
1235
|
return {
|
|
1222
1236
|
success: false,
|
|
1223
1237
|
error
|
|
@@ -1236,7 +1250,7 @@ var SubAccounts = class SubAccounts {
|
|
|
1236
1250
|
async createSmtpPassword(handle) {
|
|
1237
1251
|
let error = null;
|
|
1238
1252
|
if (!handle) {
|
|
1239
|
-
error =
|
|
1253
|
+
error = createValidationError("No handle provided.");
|
|
1240
1254
|
return {
|
|
1241
1255
|
data: null,
|
|
1242
1256
|
error
|
|
@@ -1268,7 +1282,7 @@ var SubAccounts = class SubAccounts {
|
|
|
1268
1282
|
async listSmtpPasswords(handle) {
|
|
1269
1283
|
let error = null;
|
|
1270
1284
|
if (!handle) {
|
|
1271
|
-
error =
|
|
1285
|
+
error = createValidationError("No handle provided.");
|
|
1272
1286
|
return {
|
|
1273
1287
|
data: null,
|
|
1274
1288
|
error
|
|
@@ -1296,7 +1310,7 @@ var SubAccounts = class SubAccounts {
|
|
|
1296
1310
|
async deleteSmtpPassword(handle, id) {
|
|
1297
1311
|
let error = null;
|
|
1298
1312
|
if (!handle) {
|
|
1299
|
-
error =
|
|
1313
|
+
error = createValidationError("No handle provided.");
|
|
1300
1314
|
return {
|
|
1301
1315
|
success: false,
|
|
1302
1316
|
error
|
|
@@ -1315,7 +1329,7 @@ var SubAccounts = class SubAccounts {
|
|
|
1315
1329
|
async getLimit(handle) {
|
|
1316
1330
|
let error = null;
|
|
1317
1331
|
if (!handle) {
|
|
1318
|
-
error =
|
|
1332
|
+
error = createValidationError("No handle provided.");
|
|
1319
1333
|
return {
|
|
1320
1334
|
data: null,
|
|
1321
1335
|
error
|
|
@@ -1339,14 +1353,14 @@ var SubAccounts = class SubAccounts {
|
|
|
1339
1353
|
async setLimit(handle, limit) {
|
|
1340
1354
|
let error = null;
|
|
1341
1355
|
if (!handle) {
|
|
1342
|
-
error =
|
|
1356
|
+
error = createValidationError("No handle provided.");
|
|
1343
1357
|
return {
|
|
1344
1358
|
success: false,
|
|
1345
1359
|
error
|
|
1346
1360
|
};
|
|
1347
1361
|
}
|
|
1348
1362
|
if (limit.sends < 0) {
|
|
1349
|
-
error =
|
|
1363
|
+
error = createValidationError("The sends value must be at least 0.");
|
|
1350
1364
|
return {
|
|
1351
1365
|
success: false,
|
|
1352
1366
|
error
|
|
@@ -1371,7 +1385,7 @@ var SubAccounts = class SubAccounts {
|
|
|
1371
1385
|
async deleteLimit(handle) {
|
|
1372
1386
|
let error = null;
|
|
1373
1387
|
if (!handle) {
|
|
1374
|
-
error =
|
|
1388
|
+
error = createValidationError("No handle provided.");
|
|
1375
1389
|
return {
|
|
1376
1390
|
success: false,
|
|
1377
1391
|
error
|
|
@@ -1390,7 +1404,7 @@ var SubAccounts = class SubAccounts {
|
|
|
1390
1404
|
async getUsage(handle) {
|
|
1391
1405
|
let error = null;
|
|
1392
1406
|
if (!handle) {
|
|
1393
|
-
error =
|
|
1407
|
+
error = createValidationError("No handle provided.");
|
|
1394
1408
|
return {
|
|
1395
1409
|
data: null,
|
|
1396
1410
|
error
|
|
@@ -1643,7 +1657,7 @@ var Suppressions = class {
|
|
|
1643
1657
|
let error = null;
|
|
1644
1658
|
const { addToSubAccounts, entries } = options;
|
|
1645
1659
|
if (entries.length > 1e3) {
|
|
1646
|
-
error =
|
|
1660
|
+
error = createValidationError("The number of suppression entries must not exceed 1000.");
|
|
1647
1661
|
return {
|
|
1648
1662
|
success: false,
|
|
1649
1663
|
error
|
|
@@ -1692,7 +1706,7 @@ var Suppressions = class {
|
|
|
1692
1706
|
async list(options) {
|
|
1693
1707
|
let error = null;
|
|
1694
1708
|
if (options?.recipient && options.recipient.length > 255) {
|
|
1695
|
-
error =
|
|
1709
|
+
error = createValidationError("The recipient must not exceed 255 characters.");
|
|
1696
1710
|
return {
|
|
1697
1711
|
data: null,
|
|
1698
1712
|
error
|
|
@@ -1758,41 +1772,12 @@ var Attachment = class Attachment {
|
|
|
1758
1772
|
disposition
|
|
1759
1773
|
};
|
|
1760
1774
|
}
|
|
1761
|
-
static async
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
});
|
|
1768
|
-
} catch (error) {
|
|
1769
|
-
throw new Error(`Unable to read attachment file: ${path}`, { cause: error });
|
|
1770
|
-
}
|
|
1771
|
-
}
|
|
1772
|
-
static async fromUrl(url, options) {
|
|
1773
|
-
try {
|
|
1774
|
-
let contentType;
|
|
1775
|
-
const data = await $fetch(url, {
|
|
1776
|
-
responseType: "arrayBuffer",
|
|
1777
|
-
timeout: 12e4,
|
|
1778
|
-
onResponse: ({ response }) => {
|
|
1779
|
-
const contentTypeHeader = response.headers.get("content-type");
|
|
1780
|
-
if (contentTypeHeader) contentType = contentTypeHeader.split(";", 1)[0]?.trim();
|
|
1781
|
-
}
|
|
1782
|
-
});
|
|
1783
|
-
return Attachment.fromBytes(data, {
|
|
1784
|
-
type: contentType,
|
|
1785
|
-
filename: basename(new URL(url).pathname),
|
|
1786
|
-
...options
|
|
1787
|
-
});
|
|
1788
|
-
} catch (error) {
|
|
1789
|
-
throw new Error(`Unable to fetch attachment from URL: ${url}`, { cause: error });
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
static inlineFile(path, options) {
|
|
1793
|
-
return Attachment.fromFile(path, {
|
|
1794
|
-
...options,
|
|
1795
|
-
disposition: "inline"
|
|
1775
|
+
static async fromBlob(blob, options) {
|
|
1776
|
+
if (!(blob instanceof Blob)) throw new Error("Unable to create attachment: expected a Blob");
|
|
1777
|
+
const bytes = await blob.arrayBuffer();
|
|
1778
|
+
return Attachment.fromBytes(bytes, {
|
|
1779
|
+
type: blob.type,
|
|
1780
|
+
...options
|
|
1796
1781
|
});
|
|
1797
1782
|
}
|
|
1798
1783
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mailchannels-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Node.js SDK to integrate MailChannels Email API into your JavaScript or TypeScript server-side applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,25 +42,25 @@
|
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
44
44
|
"@types/markdown-it": "^14.1.2",
|
|
45
|
-
"@types/node": "^25.9.
|
|
46
|
-
"@vitest/coverage-v8": "^4.1.
|
|
45
|
+
"@types/node": "^25.9.2",
|
|
46
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
47
47
|
"changelogen": "^0.6.2",
|
|
48
48
|
"obuild": "^0.4.36",
|
|
49
|
-
"oxlint": "^1.
|
|
49
|
+
"oxlint": "^1.68.0",
|
|
50
50
|
"scule": "^1.3.0",
|
|
51
51
|
"typescript": "^6.0.3",
|
|
52
52
|
"vitepress": "^2.0.0-alpha.17",
|
|
53
53
|
"vitepress-plugin-group-icons": "^1.7.5",
|
|
54
|
-
"vitepress-plugin-llms": "^1.13.
|
|
55
|
-
"vitest": "^4.1.
|
|
54
|
+
"vitepress-plugin-llms": "^1.13.1",
|
|
55
|
+
"vitest": "^4.1.8"
|
|
56
56
|
},
|
|
57
57
|
"engines": {
|
|
58
58
|
"node": ">=20"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"build": "obuild",
|
|
62
|
-
"parity:fixtures": "node scripts/generate-parity-fixtures.
|
|
63
|
-
"release": "pnpm lint && pnpm test && pnpm build && changelogen --
|
|
62
|
+
"parity:fixtures": "node scripts/generate-parity-fixtures.ts",
|
|
63
|
+
"release": "pnpm lint && pnpm test && pnpm build && changelogen --bump",
|
|
64
64
|
"simulate": "node src/cli.ts simulate",
|
|
65
65
|
"lint": "oxlint",
|
|
66
66
|
"lint:fix": "oxlint --fix",
|