groundmail 0.29.0 → 0.29.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/dist/authjs.d.ts +1 -1
- package/dist/authjs.js +1 -1
- package/dist/better-auth.d.ts +1 -1
- package/dist/better-auth.js +1 -1
- package/dist/{chunk-ZH3KZR7I.js → chunk-PMQDWLBY.js} +42 -7
- package/dist/clerk.d.ts +1 -1
- package/dist/clerk.js +1 -1
- package/dist/{client-B9Pa_09D.d.ts → client-BkJCGT45.d.ts} +7 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/payload.d.ts +1 -1
- package/dist/payload.js +1 -1
- package/package.json +2 -2
package/dist/authjs.d.ts
CHANGED
package/dist/authjs.js
CHANGED
package/dist/better-auth.d.ts
CHANGED
package/dist/better-auth.js
CHANGED
|
@@ -70,6 +70,13 @@ function isSpfRecord(value) {
|
|
|
70
70
|
return value.trim().toLowerCase().startsWith("v=spf1");
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
// src/dns/dmarc.ts
|
|
74
|
+
var KEPT_DMARC_DETAIL = "You already have a DMARC record. Nothing to change.";
|
|
75
|
+
var ALREADY_SET_DETAIL = "Already set. Nothing to change.";
|
|
76
|
+
function isDmarcValue(value) {
|
|
77
|
+
return /^v=dmarc1\b/i.test(value.trim().replace(/^"/, ""));
|
|
78
|
+
}
|
|
79
|
+
|
|
73
80
|
// src/dns/cloudflare.ts
|
|
74
81
|
function relativeName(fqdn2, zone) {
|
|
75
82
|
const domain = zone.replace(/\.$/, "").toLowerCase();
|
|
@@ -125,6 +132,10 @@ function createCloudflareProvider(options) {
|
|
|
125
132
|
const recordType = record.type.toUpperCase();
|
|
126
133
|
const name = relativeName(record.host, zoneName);
|
|
127
134
|
const absoluteName = name === "@" ? zoneName : `${name}.${zoneName}`;
|
|
135
|
+
if (record.already_set) {
|
|
136
|
+
results.push({ purpose: record.purpose, host: record.host, action: "unchanged", detail: ALREADY_SET_DETAIL });
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
128
139
|
if (recordType === "CNAME") {
|
|
129
140
|
const target = record.value.replace(/\.$/, "").toLowerCase();
|
|
130
141
|
const existingList2 = await cf(
|
|
@@ -266,10 +277,13 @@ function createCloudflareProvider(options) {
|
|
|
266
277
|
});
|
|
267
278
|
continue;
|
|
268
279
|
}
|
|
280
|
+
if (record.purpose === "DMARC" && (existingList || []).some((r) => isDmarcValue(r.content))) {
|
|
281
|
+
results.push({ purpose: record.purpose, host: record.host, action: "unchanged", detail: KEPT_DMARC_DETAIL });
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
269
284
|
const replaceable = (existingList || []).find((r) => {
|
|
270
285
|
if (record.purpose === "Domain ownership") return r.content.includes("groundmail-verify=");
|
|
271
286
|
if (record.purpose === "DKIM") return r.content.toLowerCase().includes("v=dkim1");
|
|
272
|
-
if (record.purpose === "DMARC") return r.content.toLowerCase().includes("v=dmarc1");
|
|
273
287
|
return false;
|
|
274
288
|
});
|
|
275
289
|
if (replaceable) {
|
|
@@ -329,7 +343,6 @@ function ownedTxt(purpose, value) {
|
|
|
329
343
|
const lower = value.toLowerCase();
|
|
330
344
|
if (purpose === "Domain ownership") return lower.includes("groundmail-verify=");
|
|
331
345
|
if (purpose === "DKIM") return lower.includes("v=dkim1");
|
|
332
|
-
if (purpose === "DMARC") return lower.includes("v=dmarc1");
|
|
333
346
|
return false;
|
|
334
347
|
}
|
|
335
348
|
async function applyToRecordStore(zone, records, store) {
|
|
@@ -341,6 +354,10 @@ async function applyToRecordStore(zone, records, store) {
|
|
|
341
354
|
const name = relativeName2(record.host, zone);
|
|
342
355
|
const value = normalizeValue(type, record.value);
|
|
343
356
|
const done = (action, detail) => results.push({ purpose: record.purpose, host: record.host, action, ...detail ? { detail } : {} });
|
|
357
|
+
if (record.already_set) {
|
|
358
|
+
done("unchanged", ALREADY_SET_DETAIL);
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
344
361
|
if (type === "CNAME") {
|
|
345
362
|
const existing = at("CNAME", name)[0];
|
|
346
363
|
if (existing && normalizeValue(type, existing.value) === value) {
|
|
@@ -388,6 +405,10 @@ async function applyToRecordStore(zone, records, store) {
|
|
|
388
405
|
done("unchanged");
|
|
389
406
|
continue;
|
|
390
407
|
}
|
|
408
|
+
if (record.purpose === "DMARC" && sameName.some((row) => isDmarcValue(normalizeValue(type, row.value)))) {
|
|
409
|
+
done("unchanged", KEPT_DMARC_DETAIL);
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
391
412
|
const replaceable = sameName.find((row) => ownedTxt(record.purpose, normalizeValue(type, row.value)));
|
|
392
413
|
if (replaceable) {
|
|
393
414
|
await store.update(replaceable, { name, type, value });
|
|
@@ -521,6 +542,10 @@ function createGoDaddyKeyProvider(options) {
|
|
|
521
542
|
for (const record of records) {
|
|
522
543
|
const recordType = record.type.toUpperCase();
|
|
523
544
|
const name = relativeName3(record.host, zone);
|
|
545
|
+
if (record.already_set) {
|
|
546
|
+
results.push({ purpose: record.purpose, host: record.host, action: "unchanged", detail: ALREADY_SET_DETAIL });
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
524
549
|
if (recordType === "CNAME") {
|
|
525
550
|
const target = record.value.replace(/\.$/, "");
|
|
526
551
|
const existing2 = await gd(
|
|
@@ -622,10 +647,13 @@ function createGoDaddyKeyProvider(options) {
|
|
|
622
647
|
results.push({ purpose: record.purpose, host: record.host, action: "unchanged" });
|
|
623
648
|
continue;
|
|
624
649
|
}
|
|
650
|
+
if (record.purpose === "DMARC" && list.some((r) => isDmarcValue(r.data))) {
|
|
651
|
+
results.push({ purpose: record.purpose, host: record.host, action: "unchanged", detail: KEPT_DMARC_DETAIL });
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
625
654
|
const replaceableIndex = list.findIndex((r) => {
|
|
626
655
|
if (record.purpose === "Domain ownership") return r.data.includes("groundmail-verify=");
|
|
627
656
|
if (record.purpose === "DKIM") return r.data.toLowerCase().includes("v=dkim1");
|
|
628
|
-
if (record.purpose === "DMARC") return r.data.toLowerCase().includes("v=dmarc1");
|
|
629
657
|
return false;
|
|
630
658
|
});
|
|
631
659
|
const next = replaceableIndex >= 0 ? list.map(
|
|
@@ -913,6 +941,10 @@ function createVercelProvider(options) {
|
|
|
913
941
|
for (const record of records) {
|
|
914
942
|
const recordType = record.type.toUpperCase();
|
|
915
943
|
const name = relativeName4(record.host, zone);
|
|
944
|
+
if (record.already_set) {
|
|
945
|
+
results.push({ purpose: record.purpose, host: record.host, action: "unchanged", detail: ALREADY_SET_DETAIL });
|
|
946
|
+
continue;
|
|
947
|
+
}
|
|
916
948
|
if (recordType === "CNAME") {
|
|
917
949
|
const target = record.value.replace(/\.$/, "").toLowerCase();
|
|
918
950
|
const existing = cached.find(
|
|
@@ -978,7 +1010,7 @@ function createVercelProvider(options) {
|
|
|
978
1010
|
}
|
|
979
1011
|
if (record.purpose === "SPF") {
|
|
980
1012
|
const spfRecords = cached.filter(
|
|
981
|
-
(row) => row.type === "TXT" && row.name ===
|
|
1013
|
+
(row) => row.type === "TXT" && row.name === name && isSpfRecord(row.value)
|
|
982
1014
|
);
|
|
983
1015
|
const existing = spfRecords[0];
|
|
984
1016
|
const includeHost = record.value.match(/include:([^\s]+)/i)?.[1] || "spf.groundmail.dev";
|
|
@@ -994,7 +1026,7 @@ function createVercelProvider(options) {
|
|
|
994
1026
|
} else {
|
|
995
1027
|
await vercel(`/v1/domains/records/${existing.id}`, {
|
|
996
1028
|
method: "PATCH",
|
|
997
|
-
body: JSON.stringify({ type: "TXT", name
|
|
1029
|
+
body: JSON.stringify({ type: "TXT", name, value: merged, ttl: 60 })
|
|
998
1030
|
});
|
|
999
1031
|
results.push({
|
|
1000
1032
|
purpose: record.purpose,
|
|
@@ -1006,7 +1038,7 @@ function createVercelProvider(options) {
|
|
|
1006
1038
|
} else {
|
|
1007
1039
|
await vercel(`/v2/domains/${encodeURIComponent(zone)}/records`, {
|
|
1008
1040
|
method: "POST",
|
|
1009
|
-
body: JSON.stringify({ type: "TXT", name
|
|
1041
|
+
body: JSON.stringify({ type: "TXT", name, value: merged, ttl: 60 })
|
|
1010
1042
|
});
|
|
1011
1043
|
results.push({
|
|
1012
1044
|
purpose: record.purpose,
|
|
@@ -1024,10 +1056,13 @@ function createVercelProvider(options) {
|
|
|
1024
1056
|
results.push({ purpose: record.purpose, host: record.host, action: "unchanged" });
|
|
1025
1057
|
continue;
|
|
1026
1058
|
}
|
|
1059
|
+
if (record.purpose === "DMARC" && existingList.some((row) => isDmarcValue(row.value))) {
|
|
1060
|
+
results.push({ purpose: record.purpose, host: record.host, action: "unchanged", detail: KEPT_DMARC_DETAIL });
|
|
1061
|
+
continue;
|
|
1062
|
+
}
|
|
1027
1063
|
const replaceable = existingList.find((row) => {
|
|
1028
1064
|
if (record.purpose === "Domain ownership") return row.value.includes("groundmail-verify=");
|
|
1029
1065
|
if (record.purpose === "DKIM") return row.value.toLowerCase().includes("v=dkim1");
|
|
1030
|
-
if (record.purpose === "DMARC") return row.value.toLowerCase().includes("v=dmarc1");
|
|
1031
1066
|
return false;
|
|
1032
1067
|
});
|
|
1033
1068
|
if (replaceable) {
|
package/dist/clerk.d.ts
CHANGED
package/dist/clerk.js
CHANGED
|
@@ -958,6 +958,11 @@ type DnsRecord = {
|
|
|
958
958
|
purpose: string;
|
|
959
959
|
/** MX preference when `type` is `MX`. Mail hostname is in `value`. */
|
|
960
960
|
priority?: number;
|
|
961
|
+
/**
|
|
962
|
+
* The domain already publishes a record that does this job (its own DMARC record).
|
|
963
|
+
* `value` is what is published now; nothing to add or change, and apply leaves it alone.
|
|
964
|
+
*/
|
|
965
|
+
already_set?: boolean;
|
|
961
966
|
};
|
|
962
967
|
type CreateDomainParams = {
|
|
963
968
|
name: string;
|
|
@@ -1015,9 +1020,11 @@ type VerifyDomainResult = {
|
|
|
1015
1020
|
verified: boolean;
|
|
1016
1021
|
checks?: {
|
|
1017
1022
|
ownership: boolean;
|
|
1023
|
+
/** True when SPF needs nothing more. Always true for a main domain (example.com), which needs no SPF change. */
|
|
1018
1024
|
spf: boolean;
|
|
1019
1025
|
dkim: boolean;
|
|
1020
1026
|
dmarc: boolean;
|
|
1027
|
+
return_path?: boolean;
|
|
1021
1028
|
};
|
|
1022
1029
|
auto?: boolean;
|
|
1023
1030
|
inbound_configured?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as ApiErrorBody, D as DnsRecord } from './client-
|
|
2
|
-
export { a as ApiErrorCode, b as ApiKey, c as ApiKeyScope, d as ApplyDnsParams, e as ApplyDnsRecordResult, f as ApplyDnsResult, g as Asset, h as Automation, i as AutomationResult, j as AutomationRun, k as AutomationRunResponse, l as AutomationRunStatus, m as AutomationRunStep, n as AutomationRunStepStatus, o as AutomationStatus, p as AutomationStep, q as AutomationTrigger, B as BatchSendEmailResult, r as BounceType, C as CategoryOptOut, s as CategoryOptOutCategory, t as CategoryUnsubscribedErrorDetails, u as ConnectTwilioParams, v as ContactWebhookEventPayload, w as CreateApiKeyParams, x as CreateApiKeyResult, y as CreateAutomationParams, z as CreateDomainParams, E as CreateDomainResult, F as CreateMigrationParams, H as CreateSuppressionParams, I as CreateTemplateParams, J as CreateTenantParams, K as CreateWebhookParams, L as DEFAULT_API_BASE_URL, M as DailyRampLimitErrorDetails, N as DeleteTenantResult, O as DeliverabilityBucket, P as DeliverabilityDomainInsight, Q as DeliverabilityInsightsResult, R as DeliverabilityMailServerRelay, T as DeliverabilityWindow, U as DmarcDailyPoint, V as DmarcMonitoring, W as DmarcMonitoringDisabled, X as DmarcReadiness, Y as DmarcReadinessVerdict, Z as DmarcSource, _ as DmarcSourceStatus, $ as DmarcStatus, a0 as DnsProviderName, a1 as Domain, a2 as DomainBimiAnalysis, a3 as DomainDetailResult, a4 as DomainDmarcAnalysis, a5 as DomainDnsAnalysis, a6 as DomainStatus, a7 as DomainTlsMode, a8 as DomainTrackingDns, a9 as DomainTrackingStatus, aa as EmailListItem, ab as EmailWebhookEventPayload, ac as EmailWebhookEventType, ad as EnableDmarcParams, ae as EnableDmarcResult, af as EnrollmentSkip, ag as EventRecord, G as Groundmail, ah as GroundmailAccessToken, ai as GroundmailOptions, aj as GroundmailTokenOptions, ak as GroundmailTokenProvider, al as HeldBackErrorDetails, am as HeldBackReason, an as HeldBackRecipient, ao as InReplyToEmail, ap as ListApiKeysParams, aq as ListApiKeysResult, ar as ListAutomationRunsParams, as as ListAutomationRunsResult, at as ListAutomationsResult, au as ListCategoryOptOutsParams, av as ListCategoryOptOutsResult, aw as ListDomainsParams, ax as ListDomainsResult, ay as ListEmailsParams, az as ListEmailsResult, aA as ListReceivedEmailsParams, aB as ListReceivedEmailsResult, aC as ListSuppressionsParams, aD as ListSuppressionsResult, aE as ListTemplatesParams, aF as ListTemplatesResult, aG as ListTenantDomainsResult, aH as ListTenantsParams, aI as ListTenantsResult, aJ as ListTestInboxEmailsParams, aK as ListTestInboxEmailsResult, aL as MailServerSigningStatus, aM as MailStream, aN as Migration, aO as MigrationDnsChange, aP as MigrationDnsPlan, aQ as MigrationDomainItem, aR as MigrationItemStatus, aS as MigrationProvider, aT as MigrationResource, aU as MigrationSummary, aV as MigrationTemplateItem, aW as MigrationTotals, aX as MigrationWebhookItem, aY as ReceivedEmail, aZ as ReceivedEmailAttachment, a_ as ReceivedEmailAttachmentMeta, a$ as RemoteApplyDnsCredentials, b0 as RemoveCategoryOptOutOptions, b1 as RunMigrationOptions, b2 as SendEmailParams, S as SendEmailResult, b3 as StreamDeliverability, b4 as SuppressedErrorDetails, b5 as Suppression, b6 as TEST_INBOX_WAIT_DEFAULT_MS, b7 as TEST_INBOX_WAIT_MAX_SECONDS, b8 as Template, b9 as TemplateFormat, ba as TemplateLintWarning, bb as TemplateStarter, bc as TemplateStatus, bd as TemplateVersion, be as Tenant, bf as TenantPauseSource, bg as TenantRamp, bh as TenantStats, bi as TenantStatsResult, bj as TenantStatus, bk as TenantStreamPolicy, bl as TestEmailWebhookParams, bm as TestEmailWebhookResult, bn as TestInbox, bo as TestInboxAddressOptions, bp as TestInboxEmail, bq as TestInboxEmailDetail, br as TestInboxHeader, bs as TestInboxResource, bt as TestInboxUsage, bu as TriggerEventParams, bv as TriggerEventResult, bw as TwilioIntegrationResponse, bx as TwilioIntegrationStatus, by as UpdateAutomationParams, bz as UpdateDomainParams, bA as UpdateEmailParams, bB as UpdateTemplateParams, bC as UpdateTenantParams, bD as UpdateWebhookParams, bE as ValidateEmailResult, bF as ValidationReason, bG as ValidationVerdict, bH as VerifyDomainResult, bI as WaitForTestEmailParams, bJ as WebhookEmailTag, bK as WebhookEndpoint, bL as WebhookEndpointWithSecret, bM as WebhookEventType, bN as mergeMigrations, bO as normalizeAccessToken } from './client-
|
|
1
|
+
import { A as ApiErrorBody, D as DnsRecord } from './client-BkJCGT45.js';
|
|
2
|
+
export { a as ApiErrorCode, b as ApiKey, c as ApiKeyScope, d as ApplyDnsParams, e as ApplyDnsRecordResult, f as ApplyDnsResult, g as Asset, h as Automation, i as AutomationResult, j as AutomationRun, k as AutomationRunResponse, l as AutomationRunStatus, m as AutomationRunStep, n as AutomationRunStepStatus, o as AutomationStatus, p as AutomationStep, q as AutomationTrigger, B as BatchSendEmailResult, r as BounceType, C as CategoryOptOut, s as CategoryOptOutCategory, t as CategoryUnsubscribedErrorDetails, u as ConnectTwilioParams, v as ContactWebhookEventPayload, w as CreateApiKeyParams, x as CreateApiKeyResult, y as CreateAutomationParams, z as CreateDomainParams, E as CreateDomainResult, F as CreateMigrationParams, H as CreateSuppressionParams, I as CreateTemplateParams, J as CreateTenantParams, K as CreateWebhookParams, L as DEFAULT_API_BASE_URL, M as DailyRampLimitErrorDetails, N as DeleteTenantResult, O as DeliverabilityBucket, P as DeliverabilityDomainInsight, Q as DeliverabilityInsightsResult, R as DeliverabilityMailServerRelay, T as DeliverabilityWindow, U as DmarcDailyPoint, V as DmarcMonitoring, W as DmarcMonitoringDisabled, X as DmarcReadiness, Y as DmarcReadinessVerdict, Z as DmarcSource, _ as DmarcSourceStatus, $ as DmarcStatus, a0 as DnsProviderName, a1 as Domain, a2 as DomainBimiAnalysis, a3 as DomainDetailResult, a4 as DomainDmarcAnalysis, a5 as DomainDnsAnalysis, a6 as DomainStatus, a7 as DomainTlsMode, a8 as DomainTrackingDns, a9 as DomainTrackingStatus, aa as EmailListItem, ab as EmailWebhookEventPayload, ac as EmailWebhookEventType, ad as EnableDmarcParams, ae as EnableDmarcResult, af as EnrollmentSkip, ag as EventRecord, G as Groundmail, ah as GroundmailAccessToken, ai as GroundmailOptions, aj as GroundmailTokenOptions, ak as GroundmailTokenProvider, al as HeldBackErrorDetails, am as HeldBackReason, an as HeldBackRecipient, ao as InReplyToEmail, ap as ListApiKeysParams, aq as ListApiKeysResult, ar as ListAutomationRunsParams, as as ListAutomationRunsResult, at as ListAutomationsResult, au as ListCategoryOptOutsParams, av as ListCategoryOptOutsResult, aw as ListDomainsParams, ax as ListDomainsResult, ay as ListEmailsParams, az as ListEmailsResult, aA as ListReceivedEmailsParams, aB as ListReceivedEmailsResult, aC as ListSuppressionsParams, aD as ListSuppressionsResult, aE as ListTemplatesParams, aF as ListTemplatesResult, aG as ListTenantDomainsResult, aH as ListTenantsParams, aI as ListTenantsResult, aJ as ListTestInboxEmailsParams, aK as ListTestInboxEmailsResult, aL as MailServerSigningStatus, aM as MailStream, aN as Migration, aO as MigrationDnsChange, aP as MigrationDnsPlan, aQ as MigrationDomainItem, aR as MigrationItemStatus, aS as MigrationProvider, aT as MigrationResource, aU as MigrationSummary, aV as MigrationTemplateItem, aW as MigrationTotals, aX as MigrationWebhookItem, aY as ReceivedEmail, aZ as ReceivedEmailAttachment, a_ as ReceivedEmailAttachmentMeta, a$ as RemoteApplyDnsCredentials, b0 as RemoveCategoryOptOutOptions, b1 as RunMigrationOptions, b2 as SendEmailParams, S as SendEmailResult, b3 as StreamDeliverability, b4 as SuppressedErrorDetails, b5 as Suppression, b6 as TEST_INBOX_WAIT_DEFAULT_MS, b7 as TEST_INBOX_WAIT_MAX_SECONDS, b8 as Template, b9 as TemplateFormat, ba as TemplateLintWarning, bb as TemplateStarter, bc as TemplateStatus, bd as TemplateVersion, be as Tenant, bf as TenantPauseSource, bg as TenantRamp, bh as TenantStats, bi as TenantStatsResult, bj as TenantStatus, bk as TenantStreamPolicy, bl as TestEmailWebhookParams, bm as TestEmailWebhookResult, bn as TestInbox, bo as TestInboxAddressOptions, bp as TestInboxEmail, bq as TestInboxEmailDetail, br as TestInboxHeader, bs as TestInboxResource, bt as TestInboxUsage, bu as TriggerEventParams, bv as TriggerEventResult, bw as TwilioIntegrationResponse, bx as TwilioIntegrationStatus, by as UpdateAutomationParams, bz as UpdateDomainParams, bA as UpdateEmailParams, bB as UpdateTemplateParams, bC as UpdateTenantParams, bD as UpdateWebhookParams, bE as ValidateEmailResult, bF as ValidationReason, bG as ValidationVerdict, bH as VerifyDomainResult, bI as WaitForTestEmailParams, bJ as WebhookEmailTag, bK as WebhookEndpoint, bL as WebhookEndpointWithSecret, bM as WebhookEventType, bN as mergeMigrations, bO as normalizeAccessToken } from './client-BkJCGT45.js';
|
|
3
3
|
|
|
4
4
|
type GroundmailErrorOptions = {
|
|
5
5
|
code?: string;
|
package/dist/index.js
CHANGED
package/dist/payload.d.ts
CHANGED
package/dist/payload.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groundmail",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.1",
|
|
4
4
|
"description": "Groundmail transactional email API client",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"prepublishOnly": "npm run build && npm run build:nodemailer-bridge"
|
|
90
90
|
},
|
|
91
91
|
"dependencies": {
|
|
92
|
-
"groundmail-nodemailer": "0.15.
|
|
92
|
+
"groundmail-nodemailer": "0.15.18"
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
95
|
"@types/nodemailer": "^8.0.1",
|