mailchannels-sdk 0.6.0 → 0.6.1
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 +2 -2
- package/dist/mailchannels.d.mts +9 -0
- package/dist/mailchannels.d.ts +9 -0
- package/dist/mailchannels.mjs +203 -130
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -43,8 +43,8 @@ This library provides a simple way to interact with the [MailChannels API](https
|
|
|
43
43
|
|
|
44
44
|
## <a name="requirements">📏 Requirements</a>
|
|
45
45
|
|
|
46
|
-
- MailChannels account
|
|
47
|
-
-
|
|
46
|
+
- [Create a MailChannels account](https://www.mailchannels.com/pricing/#for_devs)
|
|
47
|
+
- [Create an API key](https://console.mailchannels.net/settings/accountSettings#APIKeys)
|
|
48
48
|
|
|
49
49
|
## <a name="quick-setup">🏃 Quick setup</a>
|
|
50
50
|
|
package/dist/mailchannels.d.mts
CHANGED
|
@@ -17,11 +17,20 @@ interface SuccessResponse {
|
|
|
17
17
|
* Whether the operation was successful.
|
|
18
18
|
*/
|
|
19
19
|
success: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Error message if the operation failed.
|
|
22
|
+
*/
|
|
20
23
|
error: string | null;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
interface DataResponse<T> {
|
|
27
|
+
/**
|
|
28
|
+
* The response data.
|
|
29
|
+
*/
|
|
24
30
|
data: T | null;
|
|
31
|
+
/**
|
|
32
|
+
* Error message if the operation failed.
|
|
33
|
+
*/
|
|
25
34
|
error: string | null;
|
|
26
35
|
}
|
|
27
36
|
|
package/dist/mailchannels.d.ts
CHANGED
|
@@ -17,11 +17,20 @@ interface SuccessResponse {
|
|
|
17
17
|
* Whether the operation was successful.
|
|
18
18
|
*/
|
|
19
19
|
success: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Error message if the operation failed.
|
|
22
|
+
*/
|
|
20
23
|
error: string | null;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
interface DataResponse<T> {
|
|
27
|
+
/**
|
|
28
|
+
* The response data.
|
|
29
|
+
*/
|
|
24
30
|
data: T | null;
|
|
31
|
+
/**
|
|
32
|
+
* Error message if the operation failed.
|
|
33
|
+
*/
|
|
25
34
|
error: string | null;
|
|
26
35
|
}
|
|
27
36
|
|
package/dist/mailchannels.mjs
CHANGED
|
@@ -63,33 +63,37 @@ const getStatusError = (response, errors = {}) => {
|
|
|
63
63
|
}
|
|
64
64
|
return details ? `${statusText} ${details}` : statusText;
|
|
65
65
|
};
|
|
66
|
+
function getResultError(result, error, fallback) {
|
|
67
|
+
if (result.error) return result.error;
|
|
68
|
+
return error instanceof Error ? error.message : fallback;
|
|
69
|
+
}
|
|
66
70
|
|
|
71
|
+
const isValidEmail = (email) => {
|
|
72
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
73
|
+
};
|
|
67
74
|
const parseRecipientString = (input) => {
|
|
68
75
|
const trimmed = input.trim();
|
|
69
|
-
const match = trimmed.match(/^([^<]*)<([
|
|
76
|
+
const match = trimmed.match(/^([^<]*)<([^>]*)>$/);
|
|
70
77
|
if (match) {
|
|
71
78
|
const [, name, email] = match;
|
|
72
|
-
|
|
79
|
+
if (!email?.trim() || !isValidEmail(email.trim())) return void 0;
|
|
80
|
+
return { email: email.trim(), name: name?.trim() };
|
|
73
81
|
}
|
|
82
|
+
if (!isValidEmail(trimmed)) return void 0;
|
|
74
83
|
return { email: trimmed };
|
|
75
84
|
};
|
|
76
85
|
const parseRecipient = (recipient) => {
|
|
77
86
|
if (typeof recipient === "string") {
|
|
78
87
|
return parseRecipientString(recipient);
|
|
79
88
|
}
|
|
80
|
-
if (recipient?.email)
|
|
81
|
-
|
|
82
|
-
}
|
|
89
|
+
if (!recipient?.email || !isValidEmail(recipient.email)) return void 0;
|
|
90
|
+
return { email: recipient.email, name: recipient.name };
|
|
83
91
|
};
|
|
84
92
|
const parseArrayRecipients = (recipients) => {
|
|
85
|
-
if (!recipients) return;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (Array.isArray(recipients)) {
|
|
90
|
-
return recipients.map((recipient) => parseRecipient(recipient)).filter((recipient) => Boolean(recipient));
|
|
91
|
-
}
|
|
92
|
-
return [recipients];
|
|
93
|
+
if (!recipients) return void 0;
|
|
94
|
+
const arr = typeof recipients === "string" ? [parseRecipientString(recipients)] : Array.isArray(recipients) ? recipients.map(parseRecipient) : [recipients];
|
|
95
|
+
const filtered = arr.filter((recipient) => Boolean(recipient));
|
|
96
|
+
return filtered.length > 0 ? filtered : void 0;
|
|
93
97
|
};
|
|
94
98
|
|
|
95
99
|
const stripPemHeaders = (pem) => pem.replace(/-----[^-]+-----|\s|#.*$/gm, "");
|
|
@@ -119,6 +123,21 @@ const clean = (data) => {
|
|
|
119
123
|
}
|
|
120
124
|
return data;
|
|
121
125
|
};
|
|
126
|
+
const validateLimit = (limit, max) => {
|
|
127
|
+
if (typeof limit === "number" && (limit < 1 || max && limit > max)) {
|
|
128
|
+
return "The limit value " + (max ? `must be between 1 and ${max}.` : "is invalid. Only positive values are allowed.");
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
};
|
|
132
|
+
const validateOffset = (offset) => {
|
|
133
|
+
if (typeof offset === "number" && offset < 0) {
|
|
134
|
+
return "Offset must be greater than or equal to 0.";
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
};
|
|
138
|
+
const mapBuckets = (arr) => {
|
|
139
|
+
return arr.map(({ count, period_start }) => ({ count, periodStart: period_start }));
|
|
140
|
+
};
|
|
122
141
|
|
|
123
142
|
class Emails {
|
|
124
143
|
constructor(mailchannels) {
|
|
@@ -197,7 +216,10 @@ class Emails {
|
|
|
197
216
|
[ErrorCode.PayloadTooLarge]: "The total message size should not exceed 30MB. This includes the message itself, headers, and the combined size of any attachments."
|
|
198
217
|
});
|
|
199
218
|
}
|
|
200
|
-
}).catch(() =>
|
|
219
|
+
}).catch((error) => {
|
|
220
|
+
result.error = getResultError(result, error, "Failed to send email.");
|
|
221
|
+
return null;
|
|
222
|
+
});
|
|
201
223
|
if (!response) return result;
|
|
202
224
|
result.data = clean({
|
|
203
225
|
rendered: response.data,
|
|
@@ -249,7 +271,10 @@ class Emails {
|
|
|
249
271
|
[ErrorCode.Forbidden]: "User does not have access to this feature."
|
|
250
272
|
});
|
|
251
273
|
}
|
|
252
|
-
}).catch(() =>
|
|
274
|
+
}).catch((error) => {
|
|
275
|
+
result.error = getResultError(result, error, "Failed to check domain.");
|
|
276
|
+
return null;
|
|
277
|
+
});
|
|
253
278
|
if (!response) return result;
|
|
254
279
|
result.data = clean({
|
|
255
280
|
dkim: response.check_results.dkim.map((dkimResults) => ({
|
|
@@ -297,7 +322,10 @@ class Emails {
|
|
|
297
322
|
[ErrorCode.Conflict]: "Key pair already created for domain, and selector."
|
|
298
323
|
});
|
|
299
324
|
}
|
|
300
|
-
}).catch(() =>
|
|
325
|
+
}).catch((error) => {
|
|
326
|
+
result.error = getResultError(result, error, "Failed to create DKIM key.");
|
|
327
|
+
return null;
|
|
328
|
+
});
|
|
301
329
|
if (!response) return result;
|
|
302
330
|
result.data = clean({
|
|
303
331
|
algorithm: response.algorithm,
|
|
@@ -329,17 +357,11 @@ class Emails {
|
|
|
329
357
|
async getDkimKeys(domain, options) {
|
|
330
358
|
const result = { data: null, error: null };
|
|
331
359
|
if (options?.selector && options.selector.length > 63) {
|
|
332
|
-
result.error = "Selector must be
|
|
333
|
-
return result;
|
|
334
|
-
}
|
|
335
|
-
if (typeof options?.limit === "number" && (options.limit < 1 || options.limit > 100)) {
|
|
336
|
-
result.error = "Limit must be between 1 and 100.";
|
|
337
|
-
return result;
|
|
338
|
-
}
|
|
339
|
-
if (typeof options?.offset === "number" && options.offset < 0) {
|
|
340
|
-
result.error = "Offset value is invalid. Only positive values are allowed.";
|
|
360
|
+
result.error = "Selector must be between 1 and 63 characters.";
|
|
341
361
|
return result;
|
|
342
362
|
}
|
|
363
|
+
result.error = validateLimit(options?.limit, 100) || validateOffset(options?.offset);
|
|
364
|
+
if (result.error) return result;
|
|
343
365
|
const payload = {
|
|
344
366
|
selector: options?.selector,
|
|
345
367
|
status: options?.status,
|
|
@@ -354,7 +376,10 @@ class Emails {
|
|
|
354
376
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
355
377
|
});
|
|
356
378
|
}
|
|
357
|
-
}).catch(() =>
|
|
379
|
+
}).catch((error) => {
|
|
380
|
+
result.error = getResultError(result, error, "Failed to fetch DKIM keys.");
|
|
381
|
+
return null;
|
|
382
|
+
});
|
|
358
383
|
if (!response) return result;
|
|
359
384
|
result.data = clean(response.keys.map((key) => ({
|
|
360
385
|
algorithm: key.algorithm,
|
|
@@ -394,6 +419,7 @@ class Emails {
|
|
|
394
419
|
};
|
|
395
420
|
await this.mailchannels.patch(`/tx/v1/domains/${domain}/dkim-keys/${options.selector}`, {
|
|
396
421
|
body: payload,
|
|
422
|
+
ignoreResponseError: true,
|
|
397
423
|
onResponse: async ({ response }) => {
|
|
398
424
|
if (response.ok) {
|
|
399
425
|
result.success = true;
|
|
@@ -405,7 +431,7 @@ class Emails {
|
|
|
405
431
|
});
|
|
406
432
|
}
|
|
407
433
|
}).catch((error) => {
|
|
408
|
-
result.error =
|
|
434
|
+
result.error = getResultError(result, error, "Failed to update DKIM key.");
|
|
409
435
|
});
|
|
410
436
|
return result;
|
|
411
437
|
}
|
|
@@ -450,9 +476,7 @@ class Emails {
|
|
|
450
476
|
});
|
|
451
477
|
}
|
|
452
478
|
}).catch((error) => {
|
|
453
|
-
|
|
454
|
-
result.error = error instanceof Error ? error.message : "Failed to rotate DKIM key.";
|
|
455
|
-
}
|
|
479
|
+
result.error = getResultError(result, error, "Failed to rotate DKIM key.");
|
|
456
480
|
return null;
|
|
457
481
|
});
|
|
458
482
|
if (!response) return result;
|
|
@@ -525,6 +549,8 @@ class Webhooks {
|
|
|
525
549
|
[ErrorCode.Conflict]: `Endpoint '${endpoint}' is already enrolled to receive notifications.`
|
|
526
550
|
});
|
|
527
551
|
}
|
|
552
|
+
}).catch((error) => {
|
|
553
|
+
result.error = getResultError(result, error, "Failed to enroll webhook.");
|
|
528
554
|
});
|
|
529
555
|
return result;
|
|
530
556
|
}
|
|
@@ -543,9 +569,7 @@ class Webhooks {
|
|
|
543
569
|
result.error = getStatusError(response2);
|
|
544
570
|
}
|
|
545
571
|
}).catch((error) => {
|
|
546
|
-
|
|
547
|
-
result.error = error instanceof Error ? error.message : "Failed to fetch webhooks.";
|
|
548
|
-
}
|
|
572
|
+
result.error = getResultError(result, error, "Failed to fetch webhooks.");
|
|
549
573
|
return null;
|
|
550
574
|
});
|
|
551
575
|
if (!response) return result;
|
|
@@ -571,6 +595,8 @@ class Webhooks {
|
|
|
571
595
|
}
|
|
572
596
|
result.success = true;
|
|
573
597
|
}
|
|
598
|
+
}).catch((error) => {
|
|
599
|
+
result.error = getResultError(result, error, "Failed to delete webhooks.");
|
|
574
600
|
});
|
|
575
601
|
return result;
|
|
576
602
|
}
|
|
@@ -589,13 +615,16 @@ class Webhooks {
|
|
|
589
615
|
query: {
|
|
590
616
|
id
|
|
591
617
|
},
|
|
592
|
-
onResponseError: ({ response: response2 }) => {
|
|
618
|
+
onResponseError: async ({ response: response2 }) => {
|
|
593
619
|
result.error = getStatusError(response2, {
|
|
594
620
|
[ErrorCode.BadRequest]: "Bad Request.",
|
|
595
621
|
[ErrorCode.NotFound]: `The key '${id}' is not found.`
|
|
596
622
|
});
|
|
597
623
|
}
|
|
598
|
-
}).catch(() =>
|
|
624
|
+
}).catch((error) => {
|
|
625
|
+
result.error = getResultError(result, error, "Failed to get signing key.");
|
|
626
|
+
return null;
|
|
627
|
+
});
|
|
599
628
|
if (!response) return result;
|
|
600
629
|
result.data = clean({ key: response.key });
|
|
601
630
|
return result;
|
|
@@ -619,13 +648,16 @@ class Webhooks {
|
|
|
619
648
|
body: {
|
|
620
649
|
request_id: requestId
|
|
621
650
|
},
|
|
622
|
-
onResponseError: ({ response: response2 }) => {
|
|
651
|
+
onResponseError: async ({ response: response2 }) => {
|
|
623
652
|
result.error = getStatusError(response2, {
|
|
624
653
|
[ErrorCode.BadRequest]: "Bad Request.",
|
|
625
654
|
[ErrorCode.NotFound]: "No webhooks found for the account."
|
|
626
655
|
});
|
|
627
656
|
}
|
|
628
|
-
}).catch(() =>
|
|
657
|
+
}).catch((error) => {
|
|
658
|
+
result.error = getResultError(result, error, "Failed to validate webhooks.");
|
|
659
|
+
return null;
|
|
660
|
+
});
|
|
629
661
|
if (!response) return result;
|
|
630
662
|
result.data = clean({
|
|
631
663
|
allPassed: response.all_passed,
|
|
@@ -670,13 +702,16 @@ class SubAccounts {
|
|
|
670
702
|
company_name: companyName,
|
|
671
703
|
handle
|
|
672
704
|
},
|
|
673
|
-
onResponseError: ({ response: response2 }) => {
|
|
705
|
+
onResponseError: async ({ response: response2 }) => {
|
|
674
706
|
result.error = getStatusError(response2, {
|
|
675
707
|
[ErrorCode.Forbidden]: "The parent account does not have permission to create sub-accounts.",
|
|
676
708
|
[ErrorCode.Conflict]: `Sub-account with handle '${handle}' already exists.`
|
|
677
709
|
});
|
|
678
710
|
}
|
|
679
|
-
}).catch(() =>
|
|
711
|
+
}).catch((error) => {
|
|
712
|
+
result.error = getResultError(result, error, "Failed to create sub-account.");
|
|
713
|
+
return null;
|
|
714
|
+
});
|
|
680
715
|
if (!response) return result;
|
|
681
716
|
result.data = clean({
|
|
682
717
|
companyName: response.company_name,
|
|
@@ -696,23 +731,15 @@ class SubAccounts {
|
|
|
696
731
|
*/
|
|
697
732
|
async list(options) {
|
|
698
733
|
const result = { data: null, error: null };
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
return result;
|
|
702
|
-
}
|
|
703
|
-
if (typeof options?.offset === "number" && options.offset < 0) {
|
|
704
|
-
result.error = "Offset must be greater than or equal to 0.";
|
|
705
|
-
return result;
|
|
706
|
-
}
|
|
734
|
+
result.error = validateLimit(options?.limit, 1e3) || validateOffset(options?.offset);
|
|
735
|
+
if (result.error) return result;
|
|
707
736
|
const response = await this.mailchannels.get("/tx/v1/sub-account", {
|
|
708
737
|
query: options,
|
|
709
738
|
onResponseError: async ({ response: response2 }) => {
|
|
710
739
|
result.error = getStatusError(response2);
|
|
711
740
|
}
|
|
712
741
|
}).catch((error) => {
|
|
713
|
-
|
|
714
|
-
result.error = error instanceof Error ? error.message : "Failed to fetch sub-accounts.";
|
|
715
|
-
}
|
|
742
|
+
result.error = getResultError(result, error, "Failed to fetch sub-accounts.");
|
|
716
743
|
return null;
|
|
717
744
|
});
|
|
718
745
|
if (!response) return result;
|
|
@@ -746,6 +773,8 @@ class SubAccounts {
|
|
|
746
773
|
}
|
|
747
774
|
result.success = true;
|
|
748
775
|
}
|
|
776
|
+
}).catch((error) => {
|
|
777
|
+
result.error = getResultError(result, error, "Failed to delete sub-account.");
|
|
749
778
|
});
|
|
750
779
|
return result;
|
|
751
780
|
}
|
|
@@ -775,6 +804,8 @@ class SubAccounts {
|
|
|
775
804
|
[ErrorCode.NotFound]: `The specified sub-account '${handle}' does not exist.`
|
|
776
805
|
});
|
|
777
806
|
}
|
|
807
|
+
}).catch((error) => {
|
|
808
|
+
result.error = getResultError(result, error, "Failed to suspend sub-account.");
|
|
778
809
|
});
|
|
779
810
|
return result;
|
|
780
811
|
}
|
|
@@ -805,6 +836,8 @@ class SubAccounts {
|
|
|
805
836
|
[ErrorCode.NotFound]: `The specified sub-account '${handle}' does not exist.`
|
|
806
837
|
});
|
|
807
838
|
}
|
|
839
|
+
}).catch((error) => {
|
|
840
|
+
result.error = getResultError(result, error, "Failed to activate sub-account.");
|
|
808
841
|
});
|
|
809
842
|
return result;
|
|
810
843
|
}
|
|
@@ -831,7 +864,10 @@ class SubAccounts {
|
|
|
831
864
|
[ErrorCode.UnprocessableEntity]: "You have reached the limit of API keys you can create for this sub-account."
|
|
832
865
|
});
|
|
833
866
|
}
|
|
834
|
-
}).catch(() =>
|
|
867
|
+
}).catch((error) => {
|
|
868
|
+
result.error = getResultError(result, error, "Failed to create sub-account API key.");
|
|
869
|
+
return null;
|
|
870
|
+
});
|
|
835
871
|
if (!response) return result;
|
|
836
872
|
result.data = clean({
|
|
837
873
|
id: response.id,
|
|
@@ -855,21 +891,18 @@ class SubAccounts {
|
|
|
855
891
|
result.error = "No handle provided.";
|
|
856
892
|
return result;
|
|
857
893
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
return result;
|
|
861
|
-
}
|
|
862
|
-
if (typeof options?.offset === "number" && options.offset < 0) {
|
|
863
|
-
result.error = "Offset must be greater than or equal to 0.";
|
|
864
|
-
return result;
|
|
865
|
-
}
|
|
894
|
+
result.error = validateLimit(options?.limit, 1e3) || validateOffset(options?.offset);
|
|
895
|
+
if (result.error) return result;
|
|
866
896
|
const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/api-key`, {
|
|
867
897
|
onResponseError: async ({ response: response2 }) => {
|
|
868
898
|
result.error = getStatusError(response2, {
|
|
869
899
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
870
900
|
});
|
|
871
901
|
}
|
|
872
|
-
}).catch(() =>
|
|
902
|
+
}).catch((error) => {
|
|
903
|
+
result.error = getResultError(result, error, "Failed to fetch sub-account API keys.");
|
|
904
|
+
return null;
|
|
905
|
+
});
|
|
873
906
|
if (!response) return result;
|
|
874
907
|
result.data = clean(response.map((key) => ({
|
|
875
908
|
id: key.id,
|
|
@@ -904,6 +937,8 @@ class SubAccounts {
|
|
|
904
937
|
[ErrorCode.BadRequest]: "Missing or invalid API key ID."
|
|
905
938
|
});
|
|
906
939
|
}
|
|
940
|
+
}).catch((error) => {
|
|
941
|
+
result.error = getResultError(result, error, "Failed to delete sub-account API key.");
|
|
907
942
|
});
|
|
908
943
|
return result;
|
|
909
944
|
}
|
|
@@ -930,7 +965,10 @@ class SubAccounts {
|
|
|
930
965
|
[ErrorCode.UnprocessableEntity]: "You have reached the limit of SMTP passwords you can create for this sub-account."
|
|
931
966
|
});
|
|
932
967
|
}
|
|
933
|
-
}).catch(() =>
|
|
968
|
+
}).catch((error) => {
|
|
969
|
+
result.error = getResultError(result, error, "Failed to create sub-account SMTP password.");
|
|
970
|
+
return null;
|
|
971
|
+
});
|
|
934
972
|
if (!response) return result;
|
|
935
973
|
result.data = clean({
|
|
936
974
|
enabled: response.enabled,
|
|
@@ -960,7 +998,10 @@ class SubAccounts {
|
|
|
960
998
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
961
999
|
});
|
|
962
1000
|
}
|
|
963
|
-
}).catch(() =>
|
|
1001
|
+
}).catch((error) => {
|
|
1002
|
+
result.error = getResultError(result, error, "Failed to fetch sub-account SMTP passwords.");
|
|
1003
|
+
return null;
|
|
1004
|
+
});
|
|
964
1005
|
if (!response) return result;
|
|
965
1006
|
result.data = clean(response.map((password) => ({
|
|
966
1007
|
enabled: password.enabled,
|
|
@@ -996,6 +1037,8 @@ class SubAccounts {
|
|
|
996
1037
|
[ErrorCode.BadRequest]: "Missing or invalid SMTP password ID."
|
|
997
1038
|
});
|
|
998
1039
|
}
|
|
1040
|
+
}).catch((error) => {
|
|
1041
|
+
result.error = getResultError(result, error, "Failed to delete sub-account SMTP password.");
|
|
999
1042
|
});
|
|
1000
1043
|
return result;
|
|
1001
1044
|
}
|
|
@@ -1020,7 +1063,10 @@ class SubAccounts {
|
|
|
1020
1063
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
1021
1064
|
});
|
|
1022
1065
|
}
|
|
1023
|
-
}).catch(() =>
|
|
1066
|
+
}).catch((error) => {
|
|
1067
|
+
result.error = getResultError(result, error, "Failed to fetch sub-account limit.");
|
|
1068
|
+
return null;
|
|
1069
|
+
});
|
|
1024
1070
|
if (!response) return result;
|
|
1025
1071
|
result.data = clean(response);
|
|
1026
1072
|
return result;
|
|
@@ -1054,6 +1100,8 @@ class SubAccounts {
|
|
|
1054
1100
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
1055
1101
|
});
|
|
1056
1102
|
}
|
|
1103
|
+
}).catch((error) => {
|
|
1104
|
+
result.error = getResultError(result, error, "Failed to set sub-account limit.");
|
|
1057
1105
|
});
|
|
1058
1106
|
return result;
|
|
1059
1107
|
}
|
|
@@ -1083,6 +1131,8 @@ class SubAccounts {
|
|
|
1083
1131
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
1084
1132
|
});
|
|
1085
1133
|
}
|
|
1134
|
+
}).catch((error) => {
|
|
1135
|
+
result.error = getResultError(result, error, "Failed to delete sub-account limit.");
|
|
1086
1136
|
});
|
|
1087
1137
|
return result;
|
|
1088
1138
|
}
|
|
@@ -1107,7 +1157,10 @@ class SubAccounts {
|
|
|
1107
1157
|
[ErrorCode.NotFound]: `Sub-account with handle '${handle}' not found.`
|
|
1108
1158
|
});
|
|
1109
1159
|
}
|
|
1110
|
-
}).catch(() =>
|
|
1160
|
+
}).catch((error) => {
|
|
1161
|
+
result.error = getResultError(result, error, "Failed to fetch sub-account usage.");
|
|
1162
|
+
return null;
|
|
1163
|
+
});
|
|
1111
1164
|
if (!response) return result;
|
|
1112
1165
|
result.data = clean({
|
|
1113
1166
|
endDate: response.period_end_date,
|
|
@@ -1118,9 +1171,6 @@ class SubAccounts {
|
|
|
1118
1171
|
}
|
|
1119
1172
|
}
|
|
1120
1173
|
|
|
1121
|
-
const mapBuckets = (arr) => {
|
|
1122
|
-
return arr.map(({ count, period_start }) => ({ count, periodStart: period_start }));
|
|
1123
|
-
};
|
|
1124
1174
|
class Metrics {
|
|
1125
1175
|
constructor(mailchannels) {
|
|
1126
1176
|
this.mailchannels = mailchannels;
|
|
@@ -1149,9 +1199,7 @@ class Metrics {
|
|
|
1149
1199
|
});
|
|
1150
1200
|
}
|
|
1151
1201
|
}).catch((error) => {
|
|
1152
|
-
|
|
1153
|
-
result.error = error instanceof Error ? error.message : "Failed to fetch engagement metrics.";
|
|
1154
|
-
}
|
|
1202
|
+
result.error = getResultError(result, error, "Failed to fetch engagement metrics.");
|
|
1155
1203
|
return null;
|
|
1156
1204
|
});
|
|
1157
1205
|
if (!response) return result;
|
|
@@ -1195,9 +1243,7 @@ class Metrics {
|
|
|
1195
1243
|
});
|
|
1196
1244
|
}
|
|
1197
1245
|
}).catch((error) => {
|
|
1198
|
-
|
|
1199
|
-
result.error = error instanceof Error ? error.message : "Failed to fetch performance metrics.";
|
|
1200
|
-
}
|
|
1246
|
+
result.error = getResultError(result, error, "Failed to fetch performance metrics.");
|
|
1201
1247
|
return null;
|
|
1202
1248
|
});
|
|
1203
1249
|
if (!response) return result;
|
|
@@ -1239,9 +1285,7 @@ class Metrics {
|
|
|
1239
1285
|
});
|
|
1240
1286
|
}
|
|
1241
1287
|
}).catch((error) => {
|
|
1242
|
-
|
|
1243
|
-
result.error = error instanceof Error ? error.message : "Failed to fetch recipient behaviour metrics.";
|
|
1244
|
-
}
|
|
1288
|
+
result.error = getResultError(result, error, "Failed to fetch recipient behaviour metrics.");
|
|
1245
1289
|
return null;
|
|
1246
1290
|
});
|
|
1247
1291
|
if (!response) return result;
|
|
@@ -1281,9 +1325,7 @@ class Metrics {
|
|
|
1281
1325
|
});
|
|
1282
1326
|
}
|
|
1283
1327
|
}).catch((error) => {
|
|
1284
|
-
|
|
1285
|
-
result.error = error instanceof Error ? error.message : "Failed to fetch volume metrics.";
|
|
1286
|
-
}
|
|
1328
|
+
result.error = getResultError(result, error, "Failed to fetch volume metrics.");
|
|
1287
1329
|
return null;
|
|
1288
1330
|
});
|
|
1289
1331
|
if (!response) return result;
|
|
@@ -1316,9 +1358,7 @@ class Metrics {
|
|
|
1316
1358
|
result.error = getStatusError(response2);
|
|
1317
1359
|
}
|
|
1318
1360
|
}).catch((error) => {
|
|
1319
|
-
|
|
1320
|
-
result.error = error instanceof Error ? error.message : "Failed to fetch usage metrics.";
|
|
1321
|
-
}
|
|
1361
|
+
result.error = getResultError(result, error, "Failed to fetch usage metrics.");
|
|
1322
1362
|
return null;
|
|
1323
1363
|
});
|
|
1324
1364
|
if (!response) return result;
|
|
@@ -1341,6 +1381,8 @@ class Metrics {
|
|
|
1341
1381
|
*/
|
|
1342
1382
|
async senders(type, options) {
|
|
1343
1383
|
const result = { data: null, error: null };
|
|
1384
|
+
result.error = validateLimit(options?.limit, 1e3) || validateOffset(options?.offset);
|
|
1385
|
+
if (result.error) return result;
|
|
1344
1386
|
const response = await this.mailchannels.get(`/tx/v1/metrics/senders/${type}`, {
|
|
1345
1387
|
query: {
|
|
1346
1388
|
start_time: options?.startTime,
|
|
@@ -1355,9 +1397,7 @@ class Metrics {
|
|
|
1355
1397
|
});
|
|
1356
1398
|
}
|
|
1357
1399
|
}).catch((error) => {
|
|
1358
|
-
|
|
1359
|
-
result.error = error instanceof Error ? error.message : "Failed to fetch senders metrics.";
|
|
1360
|
-
}
|
|
1400
|
+
result.error = getResultError(result, error, "Failed to fetch senders metrics.");
|
|
1361
1401
|
return null;
|
|
1362
1402
|
});
|
|
1363
1403
|
if (!response) return result;
|
|
@@ -1413,6 +1453,8 @@ class Suppressions {
|
|
|
1413
1453
|
[ErrorCode.PayloadTooLarge]: "Payload too large. The request exceeds the maximum allowed total of 1000 suppression entries for the parent account and/or its sub-accounts."
|
|
1414
1454
|
});
|
|
1415
1455
|
}
|
|
1456
|
+
}).catch((error) => {
|
|
1457
|
+
result.error = getResultError(result, error, "Failed to create suppression entries.");
|
|
1416
1458
|
});
|
|
1417
1459
|
return result;
|
|
1418
1460
|
}
|
|
@@ -1442,6 +1484,8 @@ class Suppressions {
|
|
|
1442
1484
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1443
1485
|
});
|
|
1444
1486
|
}
|
|
1487
|
+
}).catch((error) => {
|
|
1488
|
+
result.error = getResultError(result, error, "Failed to delete suppression entry.");
|
|
1445
1489
|
});
|
|
1446
1490
|
return result;
|
|
1447
1491
|
}
|
|
@@ -1456,14 +1500,8 @@ class Suppressions {
|
|
|
1456
1500
|
*/
|
|
1457
1501
|
async list(options) {
|
|
1458
1502
|
const result = { data: null, error: null };
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
return result;
|
|
1462
|
-
}
|
|
1463
|
-
if (typeof options?.offset === "number" && options.offset < 0) {
|
|
1464
|
-
result.error = "Offset must be greater than or equal to 0.";
|
|
1465
|
-
return result;
|
|
1466
|
-
}
|
|
1503
|
+
result.error = validateLimit(options?.limit, 1e3) || validateOffset(options?.offset);
|
|
1504
|
+
if (result.error) return result;
|
|
1467
1505
|
const payload = {
|
|
1468
1506
|
recipient: options?.recipient,
|
|
1469
1507
|
source: options?.source,
|
|
@@ -1479,7 +1517,10 @@ class Suppressions {
|
|
|
1479
1517
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1480
1518
|
});
|
|
1481
1519
|
}
|
|
1482
|
-
}).catch(() =>
|
|
1520
|
+
}).catch((error) => {
|
|
1521
|
+
result.error = getResultError(result, error, "Failed to fetch suppression entries.");
|
|
1522
|
+
return null;
|
|
1523
|
+
});
|
|
1483
1524
|
if (!response) return result;
|
|
1484
1525
|
result.data = clean(response.suppression_list.map((entry) => ({
|
|
1485
1526
|
createdAt: entry.created_at,
|
|
@@ -1526,9 +1567,7 @@ class Domains {
|
|
|
1526
1567
|
});
|
|
1527
1568
|
}
|
|
1528
1569
|
}).catch((error) => {
|
|
1529
|
-
|
|
1530
|
-
result.error = error instanceof Error ? error.message : "Failed to provision domain.";
|
|
1531
|
-
}
|
|
1570
|
+
result.error = getResultError(result, error, "Failed to provision domain.");
|
|
1532
1571
|
return null;
|
|
1533
1572
|
});
|
|
1534
1573
|
result.data = clean(response);
|
|
@@ -1579,9 +1618,7 @@ class Domains {
|
|
|
1579
1618
|
});
|
|
1580
1619
|
}
|
|
1581
1620
|
}).catch((error) => {
|
|
1582
|
-
|
|
1583
|
-
result.error = error instanceof Error ? error.message : "Failed to provision domains.";
|
|
1584
|
-
}
|
|
1621
|
+
result.error = getResultError(result, error, "Failed to provision domains.");
|
|
1585
1622
|
return null;
|
|
1586
1623
|
});
|
|
1587
1624
|
if (!response) return result;
|
|
@@ -1599,20 +1636,17 @@ class Domains {
|
|
|
1599
1636
|
*/
|
|
1600
1637
|
async list(options) {
|
|
1601
1638
|
const result = { data: null, error: null };
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
return result;
|
|
1605
|
-
}
|
|
1606
|
-
if (typeof options?.offset === "number" && options.offset < 0) {
|
|
1607
|
-
result.error = "Offset must be greater than or equal to 0.";
|
|
1608
|
-
return result;
|
|
1609
|
-
}
|
|
1639
|
+
result.error = validateLimit(options?.limit, 5e3) || validateOffset(options?.offset);
|
|
1640
|
+
if (result.error) return result;
|
|
1610
1641
|
const response = await this.mailchannels.get("/inbound/v1/domains", {
|
|
1611
1642
|
query: options,
|
|
1612
1643
|
onResponseError: async ({ response: response2 }) => {
|
|
1613
1644
|
result.error = getStatusError(response2);
|
|
1614
1645
|
}
|
|
1615
|
-
}).catch(() =>
|
|
1646
|
+
}).catch((error) => {
|
|
1647
|
+
result.error = getResultError(result, error, "Failed to fetch domains.");
|
|
1648
|
+
return null;
|
|
1649
|
+
});
|
|
1616
1650
|
if (!response) return result;
|
|
1617
1651
|
result.data = clean({
|
|
1618
1652
|
domains: response.domains,
|
|
@@ -1647,6 +1681,8 @@ class Domains {
|
|
|
1647
1681
|
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
1648
1682
|
});
|
|
1649
1683
|
}
|
|
1684
|
+
}).catch((error) => {
|
|
1685
|
+
result.error = getResultError(result, error, "Failed to delete domain.");
|
|
1650
1686
|
});
|
|
1651
1687
|
return result;
|
|
1652
1688
|
}
|
|
@@ -1682,7 +1718,10 @@ class Domains {
|
|
|
1682
1718
|
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
1683
1719
|
});
|
|
1684
1720
|
}
|
|
1685
|
-
}).catch(() =>
|
|
1721
|
+
}).catch((error) => {
|
|
1722
|
+
result.error = getResultError(result, error, "Failed to add domain list entry.");
|
|
1723
|
+
return null;
|
|
1724
|
+
});
|
|
1686
1725
|
if (!response) return result;
|
|
1687
1726
|
result.data = clean({
|
|
1688
1727
|
action: response.action,
|
|
@@ -1718,7 +1757,10 @@ class Domains {
|
|
|
1718
1757
|
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
1719
1758
|
});
|
|
1720
1759
|
}
|
|
1721
|
-
}).catch(() =>
|
|
1760
|
+
}).catch((error) => {
|
|
1761
|
+
result.error = getResultError(result, error, "Failed to fetch domain list entries.");
|
|
1762
|
+
return null;
|
|
1763
|
+
});
|
|
1722
1764
|
if (!response) return result;
|
|
1723
1765
|
result.data = clean(response.map(({ action, item, item_type }) => ({
|
|
1724
1766
|
action,
|
|
@@ -1764,6 +1806,8 @@ class Domains {
|
|
|
1764
1806
|
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
1765
1807
|
});
|
|
1766
1808
|
}
|
|
1809
|
+
}).catch((error) => {
|
|
1810
|
+
result.error = getResultError(result, error, "Failed to delete domain list entry.");
|
|
1767
1811
|
});
|
|
1768
1812
|
return result;
|
|
1769
1813
|
}
|
|
@@ -1790,7 +1834,10 @@ class Domains {
|
|
|
1790
1834
|
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
1791
1835
|
});
|
|
1792
1836
|
}
|
|
1793
|
-
}).catch(() =>
|
|
1837
|
+
}).catch((error) => {
|
|
1838
|
+
result.error = getResultError(result, error, "Failed to create login link.");
|
|
1839
|
+
return null;
|
|
1840
|
+
});
|
|
1794
1841
|
if (!response) return result;
|
|
1795
1842
|
result.data = clean({
|
|
1796
1843
|
link: response.loginLink
|
|
@@ -1837,6 +1884,8 @@ class Domains {
|
|
|
1837
1884
|
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
1838
1885
|
});
|
|
1839
1886
|
}
|
|
1887
|
+
}).catch((error) => {
|
|
1888
|
+
result.error = getResultError(result, error, "Failed to set downstream address.");
|
|
1840
1889
|
});
|
|
1841
1890
|
return result;
|
|
1842
1891
|
}
|
|
@@ -1856,14 +1905,8 @@ class Domains {
|
|
|
1856
1905
|
result.error = "No domain provided.";
|
|
1857
1906
|
return result;
|
|
1858
1907
|
}
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
return result;
|
|
1862
|
-
}
|
|
1863
|
-
if (typeof options?.offset === "number" && options.offset < 0) {
|
|
1864
|
-
result.error = "Offset must be greater than or equal to 0.";
|
|
1865
|
-
return result;
|
|
1866
|
-
}
|
|
1908
|
+
result.error = validateLimit(options?.limit) || validateOffset(options?.offset);
|
|
1909
|
+
if (result.error) return result;
|
|
1867
1910
|
const response = await this.mailchannels.get(`/inbound/v1/domains/${domain}/downstream-address`, {
|
|
1868
1911
|
query: options,
|
|
1869
1912
|
onResponseError: async ({ response: response2 }) => {
|
|
@@ -1872,7 +1915,10 @@ class Domains {
|
|
|
1872
1915
|
[ErrorCode.NotFound]: `The domain '${domain}' was not found.`
|
|
1873
1916
|
});
|
|
1874
1917
|
}
|
|
1875
|
-
}).catch(() =>
|
|
1918
|
+
}).catch((error) => {
|
|
1919
|
+
result.error = getResultError(result, error, "Failed to list downstream addresses.");
|
|
1920
|
+
return null;
|
|
1921
|
+
});
|
|
1876
1922
|
if (!response) return result;
|
|
1877
1923
|
result.data = clean(response.records);
|
|
1878
1924
|
return result;
|
|
@@ -1910,6 +1956,8 @@ class Domains {
|
|
|
1910
1956
|
[ErrorCode.NotFound]: "The domain does not exist."
|
|
1911
1957
|
});
|
|
1912
1958
|
}
|
|
1959
|
+
}).catch((error) => {
|
|
1960
|
+
result.error = getResultError(result, error, "Failed to update domain API key.");
|
|
1913
1961
|
});
|
|
1914
1962
|
return result;
|
|
1915
1963
|
}
|
|
@@ -1941,7 +1989,10 @@ class Domains {
|
|
|
1941
1989
|
[ErrorCode.BadRequest]: "Bad Request."
|
|
1942
1990
|
});
|
|
1943
1991
|
}
|
|
1944
|
-
}).catch(() =>
|
|
1992
|
+
}).catch((error) => {
|
|
1993
|
+
result.error = getResultError(result, error, "Failed to create login links.");
|
|
1994
|
+
return null;
|
|
1995
|
+
});
|
|
1945
1996
|
if (!response) return result;
|
|
1946
1997
|
result.data = clean(response);
|
|
1947
1998
|
return result;
|
|
@@ -1976,7 +2027,10 @@ class Lists {
|
|
|
1976
2027
|
onResponseError: async ({ response: response2 }) => {
|
|
1977
2028
|
result.error = getStatusError(response2);
|
|
1978
2029
|
}
|
|
1979
|
-
}).catch(() =>
|
|
2030
|
+
}).catch((error) => {
|
|
2031
|
+
result.error = getResultError(result, error, "Failed to add list entry.");
|
|
2032
|
+
return null;
|
|
2033
|
+
});
|
|
1980
2034
|
if (!response) return result;
|
|
1981
2035
|
result.data = clean({
|
|
1982
2036
|
action: response.action,
|
|
@@ -2004,7 +2058,10 @@ class Lists {
|
|
|
2004
2058
|
onResponseError: async ({ response: response2 }) => {
|
|
2005
2059
|
result.error = getStatusError(response2);
|
|
2006
2060
|
}
|
|
2007
|
-
}).catch(() =>
|
|
2061
|
+
}).catch((error) => {
|
|
2062
|
+
result.error = getResultError(result, error, "Failed to fetch list entries.");
|
|
2063
|
+
return null;
|
|
2064
|
+
});
|
|
2008
2065
|
if (!response) return result;
|
|
2009
2066
|
result.data = clean(response.map(({ action, item, item_type }) => ({
|
|
2010
2067
|
action,
|
|
@@ -2042,6 +2099,8 @@ class Lists {
|
|
|
2042
2099
|
}
|
|
2043
2100
|
result.error = getStatusError(response);
|
|
2044
2101
|
}
|
|
2102
|
+
}).catch((error) => {
|
|
2103
|
+
result.error = getResultError(result, error, "Failed to delete list entry.");
|
|
2045
2104
|
});
|
|
2046
2105
|
return result;
|
|
2047
2106
|
}
|
|
@@ -2084,7 +2143,10 @@ class Users {
|
|
|
2084
2143
|
[ErrorCode.BadRequest]: `The email address '${email}' is invalid.`
|
|
2085
2144
|
});
|
|
2086
2145
|
}
|
|
2087
|
-
}).catch(() =>
|
|
2146
|
+
}).catch((error) => {
|
|
2147
|
+
result.error = getResultError(result, error, "Failed to create user.");
|
|
2148
|
+
return null;
|
|
2149
|
+
});
|
|
2088
2150
|
if (!response) return result;
|
|
2089
2151
|
result.data = clean({
|
|
2090
2152
|
email: response.recipient.email_address,
|
|
@@ -2130,7 +2192,10 @@ class Users {
|
|
|
2130
2192
|
[ErrorCode.NotFound]: `The recipient '${email}' was not found.`
|
|
2131
2193
|
});
|
|
2132
2194
|
}
|
|
2133
|
-
}).catch(() =>
|
|
2195
|
+
}).catch((error) => {
|
|
2196
|
+
result.error = getResultError(result, error, "Failed to add user list entry.");
|
|
2197
|
+
return null;
|
|
2198
|
+
});
|
|
2134
2199
|
if (!response) return result;
|
|
2135
2200
|
result.data = clean({
|
|
2136
2201
|
action: response.action,
|
|
@@ -2166,7 +2231,10 @@ class Users {
|
|
|
2166
2231
|
[ErrorCode.NotFound]: `The recipient '${email}' was not found.`
|
|
2167
2232
|
});
|
|
2168
2233
|
}
|
|
2169
|
-
}).catch(() =>
|
|
2234
|
+
}).catch((error) => {
|
|
2235
|
+
result.error = getResultError(result, error, "Failed to fetch user list entries.");
|
|
2236
|
+
return null;
|
|
2237
|
+
});
|
|
2170
2238
|
if (!response) return result;
|
|
2171
2239
|
result.data = clean(response.map(({ action, item, item_type }) => ({
|
|
2172
2240
|
action,
|
|
@@ -2212,6 +2280,8 @@ class Users {
|
|
|
2212
2280
|
[ErrorCode.NotFound]: `The recipient '${email}' was not found.`
|
|
2213
2281
|
});
|
|
2214
2282
|
}
|
|
2283
|
+
}).catch((error) => {
|
|
2284
|
+
result.error = getResultError(result, error, "Failed to delete user list entry.");
|
|
2215
2285
|
});
|
|
2216
2286
|
return result;
|
|
2217
2287
|
}
|
|
@@ -2240,6 +2310,8 @@ class Service {
|
|
|
2240
2310
|
}
|
|
2241
2311
|
result.error = getStatusError(response);
|
|
2242
2312
|
}
|
|
2313
|
+
}).catch((error) => {
|
|
2314
|
+
result.error = getResultError(result, error, "Failed to fetch service status.");
|
|
2243
2315
|
});
|
|
2244
2316
|
return result;
|
|
2245
2317
|
}
|
|
@@ -2260,9 +2332,7 @@ class Service {
|
|
|
2260
2332
|
});
|
|
2261
2333
|
}
|
|
2262
2334
|
}).catch((error) => {
|
|
2263
|
-
|
|
2264
|
-
result.error = error instanceof Error ? error.message : "Failed to fetch subscriptions.";
|
|
2265
|
-
}
|
|
2335
|
+
result.error = getResultError(result, error, "Failed to fetch subscriptions.");
|
|
2266
2336
|
return null;
|
|
2267
2337
|
});
|
|
2268
2338
|
if (!response) return result;
|
|
@@ -2288,6 +2358,7 @@ class Service {
|
|
|
2288
2358
|
report_type: type
|
|
2289
2359
|
},
|
|
2290
2360
|
body: payload,
|
|
2361
|
+
ignoreResponseError: true,
|
|
2291
2362
|
onResponse: async ({ response }) => {
|
|
2292
2363
|
if (response.ok) {
|
|
2293
2364
|
result.success = true;
|
|
@@ -2295,6 +2366,8 @@ class Service {
|
|
|
2295
2366
|
}
|
|
2296
2367
|
result.error = getStatusError(response);
|
|
2297
2368
|
}
|
|
2369
|
+
}).catch((error) => {
|
|
2370
|
+
result.error = getResultError(result, error, "Failed to submit report.");
|
|
2298
2371
|
});
|
|
2299
2372
|
return result;
|
|
2300
2373
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mailchannels-sdk",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Node.js SDK to integrate MailChannels API into your JavaScript or TypeScript server-side applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
"@eslint/compat": "^2.0.0",
|
|
41
41
|
"@stylistic/eslint-plugin": "^5.6.1",
|
|
42
42
|
"@types/markdown-it": "^14.1.2",
|
|
43
|
-
"@types/node": "^
|
|
44
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
45
|
-
"@typescript-eslint/parser": "^8.
|
|
46
|
-
"@vitest/coverage-v8": "^4.0.
|
|
43
|
+
"@types/node": "^25.0.1",
|
|
44
|
+
"@typescript-eslint/eslint-plugin": "^8.49.0",
|
|
45
|
+
"@typescript-eslint/parser": "^8.49.0",
|
|
46
|
+
"@vitest/coverage-v8": "^4.0.15",
|
|
47
47
|
"changelogen": "^0.6.2",
|
|
48
48
|
"eslint": "^9.39.1",
|
|
49
49
|
"eslint-plugin-import-x": "^4.16.1",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"vitepress": "^1.6.4",
|
|
55
55
|
"vitepress-plugin-group-icons": "^1.6.5",
|
|
56
56
|
"vitepress-plugin-llms": "^1.9.3",
|
|
57
|
-
"vitest": "^4.0.
|
|
57
|
+
"vitest": "^4.0.15"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
60
|
"build": "unbuild",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"test:types": "tsc --noEmit",
|
|
66
66
|
"test:watch": "vitest watch",
|
|
67
67
|
"docs:dev": "vitepress dev docs",
|
|
68
|
-
"docs:build": "(git fetch --unshallow || git fetch --all) && vitepress build docs",
|
|
68
|
+
"docs:build": "(git fetch --unshallow -q || git fetch --all -q) && vitepress build docs",
|
|
69
69
|
"docs:preview": "vitepress preview docs",
|
|
70
70
|
"docs:snippets": "jiti docs/scripts/snippets"
|
|
71
71
|
}
|