groundmail 0.26.0 → 0.27.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 +1 -1
- 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-E6G7G2PM.js → chunk-RLIF6XKO.js} +324 -14
- package/dist/clerk.d.ts +1 -1
- package/dist/clerk.js +1 -1
- package/dist/{client-DAetJZCf.d.ts → client-Btlt7wMg.d.ts} +20 -8
- package/dist/index.d.ts +7 -3
- 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/README.md
CHANGED
|
@@ -83,7 +83,7 @@ https://docs.groundmail.dev/email-testing
|
|
|
83
83
|
|
|
84
84
|
If you send email for your own customers, create one tenant per customer. Each tenant has its
|
|
85
85
|
own domains, suppression list, stats, pause switch and daily limit, so one customer's bounces
|
|
86
|
-
or complaints never affect the others. Tenants are included on
|
|
86
|
+
or complaints never affect the others. Tenants are included on pay as you go and the Dedicated plan.
|
|
87
87
|
|
|
88
88
|
```ts
|
|
89
89
|
await tx.tenants.create({ external_id: 'cus_123', name: 'Acme', daily_limit: 5000 })
|
package/dist/authjs.d.ts
CHANGED
package/dist/authjs.js
CHANGED
package/dist/better-auth.d.ts
CHANGED
package/dist/better-auth.js
CHANGED
|
@@ -71,9 +71,9 @@ function isSpfRecord(value) {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// src/dns/cloudflare.ts
|
|
74
|
-
function relativeName(
|
|
74
|
+
function relativeName(fqdn2, zone) {
|
|
75
75
|
const domain = zone.replace(/\.$/, "").toLowerCase();
|
|
76
|
-
const host =
|
|
76
|
+
const host = fqdn2.replace(/\.$/, "").toLowerCase();
|
|
77
77
|
if (host === domain) return "@";
|
|
78
78
|
if (host.endsWith(`.${domain}`)) return host.slice(0, -(domain.length + 1));
|
|
79
79
|
return host;
|
|
@@ -302,9 +302,9 @@ function createCloudflareProvider(options) {
|
|
|
302
302
|
}
|
|
303
303
|
|
|
304
304
|
// src/dns/godaddy.ts
|
|
305
|
-
function relativeName2(
|
|
305
|
+
function relativeName2(fqdn2, zone) {
|
|
306
306
|
const domain = zone.replace(/\.$/, "").toLowerCase();
|
|
307
|
-
const host =
|
|
307
|
+
const host = fqdn2.replace(/\.$/, "").toLowerCase();
|
|
308
308
|
if (host === domain) return "@";
|
|
309
309
|
if (host.endsWith(`.${domain}`)) return host.slice(0, -(domain.length + 1));
|
|
310
310
|
return host;
|
|
@@ -471,7 +471,7 @@ function createGoDaddyProvider(options) {
|
|
|
471
471
|
};
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
-
// src/dns/
|
|
474
|
+
// src/dns/record-store.ts
|
|
475
475
|
function zoneNameCandidates2(domain) {
|
|
476
476
|
const labels = domain.replace(/\.$/, "").toLowerCase().split(".").filter(Boolean);
|
|
477
477
|
const candidates = [];
|
|
@@ -480,9 +480,296 @@ function zoneNameCandidates2(domain) {
|
|
|
480
480
|
}
|
|
481
481
|
return candidates;
|
|
482
482
|
}
|
|
483
|
-
function relativeName3(
|
|
483
|
+
function relativeName3(fqdn2, zone) {
|
|
484
484
|
const domain = zone.replace(/\.$/, "").toLowerCase();
|
|
485
|
-
const host =
|
|
485
|
+
const host = fqdn2.replace(/\.$/, "").toLowerCase();
|
|
486
|
+
if (host === domain) return "";
|
|
487
|
+
if (host.endsWith(`.${domain}`)) return host.slice(0, -(domain.length + 1));
|
|
488
|
+
return host;
|
|
489
|
+
}
|
|
490
|
+
function fqdn(name, zone) {
|
|
491
|
+
return name ? `${name}.${zone}` : zone;
|
|
492
|
+
}
|
|
493
|
+
function normalizeValue(type, value) {
|
|
494
|
+
const trimmed = value.trim();
|
|
495
|
+
if (type === "TXT") return trimmed.replace(/^"([\s\S]*)"$/, "$1").replace(/"\s+"/g, "");
|
|
496
|
+
return trimmed.replace(/\.$/, "").toLowerCase();
|
|
497
|
+
}
|
|
498
|
+
function ownedTxt(purpose, value) {
|
|
499
|
+
const lower = value.toLowerCase();
|
|
500
|
+
if (purpose === "Domain ownership") return lower.includes("groundmail-verify=");
|
|
501
|
+
if (purpose === "DKIM") return lower.includes("v=dkim1");
|
|
502
|
+
if (purpose === "DMARC") return lower.includes("v=dmarc1");
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
async function applyToRecordStore(zone, records, store) {
|
|
506
|
+
const results = [];
|
|
507
|
+
let cached = await store.list();
|
|
508
|
+
const at = (type, name) => cached.filter((row) => row.type.toUpperCase() === type && row.name === name);
|
|
509
|
+
for (const record of records) {
|
|
510
|
+
const type = record.type.toUpperCase();
|
|
511
|
+
const name = relativeName3(record.host, zone);
|
|
512
|
+
const value = normalizeValue(type, record.value);
|
|
513
|
+
const done = (action, detail) => results.push({ purpose: record.purpose, host: record.host, action, ...detail ? { detail } : {} });
|
|
514
|
+
if (type === "CNAME") {
|
|
515
|
+
const existing = at("CNAME", name)[0];
|
|
516
|
+
if (existing && normalizeValue(type, existing.value) === value) {
|
|
517
|
+
done("unchanged");
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
if (existing) {
|
|
521
|
+
await store.update(existing, { name, type, value });
|
|
522
|
+
done("updated");
|
|
523
|
+
} else {
|
|
524
|
+
await store.create({ name, type, value });
|
|
525
|
+
done("created");
|
|
526
|
+
}
|
|
527
|
+
} else if (type === "MX") {
|
|
528
|
+
const priority = record.priority ?? 10;
|
|
529
|
+
const sameName = at("MX", name);
|
|
530
|
+
if (sameName.some((row) => normalizeValue(type, row.value) === value && (row.priority ?? 10) === priority)) {
|
|
531
|
+
done("unchanged");
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
await store.create({ name, type, value, priority });
|
|
535
|
+
done(sameName.length > 0 ? "merged" : "created");
|
|
536
|
+
} else if (type !== "TXT") {
|
|
537
|
+
done("unchanged", `Skipped unsupported type ${record.type}`);
|
|
538
|
+
continue;
|
|
539
|
+
} else if (record.purpose === "SPF") {
|
|
540
|
+
const existing = at("TXT", name).find((row) => isSpfRecord(normalizeValue(type, row.value)));
|
|
541
|
+
const includeHost = record.value.match(/include:([^\s"]+)/i)?.[1] || "spf.groundmail.dev";
|
|
542
|
+
const current = existing ? normalizeValue(type, existing.value) : void 0;
|
|
543
|
+
const merged = mergeSpfInclude(current, includeHost);
|
|
544
|
+
if (existing && current?.trim() === merged.trim()) {
|
|
545
|
+
done("unchanged", "SPF already includes Groundmail");
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
if (existing) {
|
|
549
|
+
await store.update(existing, { name, type, value: merged });
|
|
550
|
+
done("merged", merged);
|
|
551
|
+
} else {
|
|
552
|
+
await store.create({ name, type, value: merged });
|
|
553
|
+
done("created", merged);
|
|
554
|
+
}
|
|
555
|
+
} else {
|
|
556
|
+
const sameName = at("TXT", name);
|
|
557
|
+
if (sameName.some((row) => normalizeValue(type, row.value) === value)) {
|
|
558
|
+
done("unchanged");
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
const replaceable = sameName.find((row) => ownedTxt(record.purpose, normalizeValue(type, row.value)));
|
|
562
|
+
if (replaceable) {
|
|
563
|
+
await store.update(replaceable, { name, type, value });
|
|
564
|
+
done("updated");
|
|
565
|
+
} else {
|
|
566
|
+
await store.create({ name, type, value });
|
|
567
|
+
done("created");
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
cached = await store.list();
|
|
571
|
+
}
|
|
572
|
+
return results;
|
|
573
|
+
}
|
|
574
|
+
async function resolveZone(domain, hostLabel, lookup) {
|
|
575
|
+
for (const candidate of zoneNameCandidates2(domain)) {
|
|
576
|
+
const info = await lookup(candidate);
|
|
577
|
+
if (info !== null) return { zone: candidate, info };
|
|
578
|
+
}
|
|
579
|
+
throw new Error(`No ${hostLabel} DNS zone found for ${domain}. The domain must use ${hostLabel} nameservers.`);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// src/dns/digitalocean.ts
|
|
583
|
+
function createDigitalOceanProvider(options) {
|
|
584
|
+
const fetchImpl = options.fetchImpl || fetch;
|
|
585
|
+
async function digitalocean(path, init) {
|
|
586
|
+
const response = await fetchImpl(`https://api.digitalocean.com/v2${path}`, {
|
|
587
|
+
...init,
|
|
588
|
+
headers: { Authorization: `Bearer ${options.apiToken}`, "Content-Type": "application/json", ...init?.headers || {} }
|
|
589
|
+
});
|
|
590
|
+
const body = await response.json().catch(() => ({}));
|
|
591
|
+
if (!response.ok && response.status !== 404) {
|
|
592
|
+
if (response.status === 403) {
|
|
593
|
+
throw new Error("DigitalOcean refused the DNS change. Reconnect DigitalOcean under Integrations to allow DNS.");
|
|
594
|
+
}
|
|
595
|
+
throw new Error(body.message || `DigitalOcean HTTP ${response.status}`);
|
|
596
|
+
}
|
|
597
|
+
return { status: response.status, body };
|
|
598
|
+
}
|
|
599
|
+
return {
|
|
600
|
+
name: "digitalocean",
|
|
601
|
+
async apply(domain, records) {
|
|
602
|
+
const { zone } = await resolveZone(domain, "DigitalOcean", async (candidate) => {
|
|
603
|
+
const { status } = await digitalocean(`/domains/${encodeURIComponent(candidate)}`);
|
|
604
|
+
return status === 404 ? null : true;
|
|
605
|
+
});
|
|
606
|
+
const path = `/domains/${encodeURIComponent(zone)}/records`;
|
|
607
|
+
const body = (record) => ({
|
|
608
|
+
type: record.type,
|
|
609
|
+
name: record.name || "@",
|
|
610
|
+
data: record.type === "TXT" ? record.value : `${record.value}.`,
|
|
611
|
+
ttl: 1800,
|
|
612
|
+
...record.priority != null ? { priority: record.priority } : {}
|
|
613
|
+
});
|
|
614
|
+
const write = async (method, suffix, record) => {
|
|
615
|
+
const { status } = await digitalocean(`${path}${suffix}`, { method, body: JSON.stringify(body(record)) });
|
|
616
|
+
if (status === 404) throw new Error(`DigitalOcean lost the ${zone} zone mid-update`);
|
|
617
|
+
};
|
|
618
|
+
return applyToRecordStore(zone, records, {
|
|
619
|
+
async list() {
|
|
620
|
+
const rows = [];
|
|
621
|
+
for (let page = 1; page <= 20; page++) {
|
|
622
|
+
const { body: list } = await digitalocean(`${path}?per_page=200&page=${page}`);
|
|
623
|
+
rows.push(...list.domain_records || []);
|
|
624
|
+
if (!list.links?.pages?.next) break;
|
|
625
|
+
}
|
|
626
|
+
return rows.map((row) => ({
|
|
627
|
+
id: String(row.id),
|
|
628
|
+
name: row.name === "@" ? "" : row.name.toLowerCase(),
|
|
629
|
+
type: row.type,
|
|
630
|
+
value: row.data,
|
|
631
|
+
priority: row.priority ?? void 0
|
|
632
|
+
}));
|
|
633
|
+
},
|
|
634
|
+
create: (record) => write("POST", "", record),
|
|
635
|
+
update: (existing, record) => write("PUT", `/${existing.id}`, record)
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// src/dns/dnsimple.ts
|
|
642
|
+
function createDnsimpleProvider(options) {
|
|
643
|
+
const fetchImpl = options.fetchImpl || fetch;
|
|
644
|
+
const base = `https://api.dnsimple.com/v2/${encodeURIComponent(options.accountId)}`;
|
|
645
|
+
async function dnsimple(path, init) {
|
|
646
|
+
const response = await fetchImpl(`${base}${path}`, {
|
|
647
|
+
...init,
|
|
648
|
+
headers: {
|
|
649
|
+
Authorization: `Bearer ${options.apiToken}`,
|
|
650
|
+
"Content-Type": "application/json",
|
|
651
|
+
Accept: "application/json",
|
|
652
|
+
...init?.headers || {}
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
const body = await response.json().catch(() => ({}));
|
|
656
|
+
if (!response.ok && response.status !== 404) throw new Error(body.message || `DNSimple HTTP ${response.status}`);
|
|
657
|
+
return { status: response.status, body };
|
|
658
|
+
}
|
|
659
|
+
return {
|
|
660
|
+
name: "dnsimple",
|
|
661
|
+
async apply(domain, records) {
|
|
662
|
+
const { zone } = await resolveZone(domain, "DNSimple", async (candidate) => {
|
|
663
|
+
const { status } = await dnsimple(`/zones/${encodeURIComponent(candidate)}`);
|
|
664
|
+
return status === 404 ? null : true;
|
|
665
|
+
});
|
|
666
|
+
const path = `/zones/${encodeURIComponent(zone)}/records`;
|
|
667
|
+
const body = (record) => ({
|
|
668
|
+
name: record.name,
|
|
669
|
+
type: record.type,
|
|
670
|
+
content: record.value,
|
|
671
|
+
ttl: 3600,
|
|
672
|
+
...record.priority != null ? { priority: record.priority } : {}
|
|
673
|
+
});
|
|
674
|
+
const write = async (method, suffix, record) => {
|
|
675
|
+
const { status } = await dnsimple(`${path}${suffix}`, { method, body: JSON.stringify(body(record)) });
|
|
676
|
+
if (status === 404) throw new Error(`DNSimple lost the ${zone} zone mid-update`);
|
|
677
|
+
};
|
|
678
|
+
return applyToRecordStore(zone, records, {
|
|
679
|
+
async list() {
|
|
680
|
+
const rows = [];
|
|
681
|
+
for (let page = 1; page <= 20; page++) {
|
|
682
|
+
const { body: list } = await dnsimple(`${path}?per_page=100&page=${page}`);
|
|
683
|
+
rows.push(...list.data || []);
|
|
684
|
+
if ((list.pagination?.total_pages ?? 1) <= page) break;
|
|
685
|
+
}
|
|
686
|
+
return rows.filter((row) => !row.system_record).map((row) => ({
|
|
687
|
+
id: String(row.id),
|
|
688
|
+
name: row.name.toLowerCase(),
|
|
689
|
+
type: row.type,
|
|
690
|
+
value: row.content,
|
|
691
|
+
priority: row.priority ?? void 0
|
|
692
|
+
}));
|
|
693
|
+
},
|
|
694
|
+
create: (record) => write("POST", "", record),
|
|
695
|
+
update: (existing, record) => write("PATCH", `/${existing.id}`, record)
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// src/dns/netlify.ts
|
|
702
|
+
function createNetlifyProvider(options) {
|
|
703
|
+
const fetchImpl = options.fetchImpl || fetch;
|
|
704
|
+
async function netlify(path, init) {
|
|
705
|
+
const response = await fetchImpl(`https://api.netlify.com/api/v1${path}`, {
|
|
706
|
+
...init,
|
|
707
|
+
headers: { Authorization: `Bearer ${options.apiToken}`, "Content-Type": "application/json", ...init?.headers || {} }
|
|
708
|
+
});
|
|
709
|
+
const body = await response.json().catch(() => ({}));
|
|
710
|
+
if (!response.ok) throw new Error(body.message || `Netlify HTTP ${response.status}`);
|
|
711
|
+
return body;
|
|
712
|
+
}
|
|
713
|
+
return {
|
|
714
|
+
name: "netlify",
|
|
715
|
+
async apply(domain, records) {
|
|
716
|
+
const zones = await netlify("/dns_zones");
|
|
717
|
+
const { zone, info } = await resolveZone(
|
|
718
|
+
domain,
|
|
719
|
+
"Netlify",
|
|
720
|
+
async (candidate) => zones.find((z) => z.name.toLowerCase() === candidate) ?? null
|
|
721
|
+
);
|
|
722
|
+
const body = (record) => ({
|
|
723
|
+
type: record.type,
|
|
724
|
+
hostname: fqdn(record.name, zone),
|
|
725
|
+
value: record.value,
|
|
726
|
+
ttl: 3600,
|
|
727
|
+
...record.priority != null ? { priority: record.priority } : {}
|
|
728
|
+
});
|
|
729
|
+
const create = async (record) => {
|
|
730
|
+
await netlify(`/dns_zones/${info.id}/dns_records`, { method: "POST", body: JSON.stringify(body(record)) });
|
|
731
|
+
};
|
|
732
|
+
return applyToRecordStore(zone, records, {
|
|
733
|
+
async list() {
|
|
734
|
+
const rows = await netlify(`/dns_zones/${info.id}/dns_records`);
|
|
735
|
+
return rows.filter((row) => !row.managed).map((row) => ({
|
|
736
|
+
id: row.id,
|
|
737
|
+
name: relativeName3(row.hostname, zone),
|
|
738
|
+
type: row.type,
|
|
739
|
+
value: row.value,
|
|
740
|
+
priority: row.priority ?? void 0
|
|
741
|
+
}));
|
|
742
|
+
},
|
|
743
|
+
create,
|
|
744
|
+
// TXT: add the new value before removing the old, so SPF never disappears. A
|
|
745
|
+
// name can only hold one CNAME, so that one goes the other way round.
|
|
746
|
+
async update(existing, record) {
|
|
747
|
+
const remove = () => netlify(`/dns_zones/${info.id}/dns_records/${existing.id}`, { method: "DELETE" });
|
|
748
|
+
if (record.type === "CNAME") {
|
|
749
|
+
await remove();
|
|
750
|
+
await create(record);
|
|
751
|
+
} else {
|
|
752
|
+
await create(record);
|
|
753
|
+
await remove();
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// src/dns/vercel.ts
|
|
762
|
+
function zoneNameCandidates3(domain) {
|
|
763
|
+
const labels = domain.replace(/\.$/, "").toLowerCase().split(".").filter(Boolean);
|
|
764
|
+
const candidates = [];
|
|
765
|
+
for (let i = 0; i <= labels.length - 2; i++) {
|
|
766
|
+
candidates.push(labels.slice(i).join("."));
|
|
767
|
+
}
|
|
768
|
+
return candidates;
|
|
769
|
+
}
|
|
770
|
+
function relativeName4(fqdn2, zone) {
|
|
771
|
+
const domain = zone.replace(/\.$/, "").toLowerCase();
|
|
772
|
+
const host = fqdn2.replace(/\.$/, "").toLowerCase();
|
|
486
773
|
if (host === domain) return "";
|
|
487
774
|
if (host.endsWith(`.${domain}`)) return host.slice(0, -(domain.length + 1));
|
|
488
775
|
return host;
|
|
@@ -530,7 +817,7 @@ function createVercelProvider(options) {
|
|
|
530
817
|
}
|
|
531
818
|
async function resolveDomain(domain) {
|
|
532
819
|
const errors = [];
|
|
533
|
-
for (const candidate of
|
|
820
|
+
for (const candidate of zoneNameCandidates3(domain)) {
|
|
534
821
|
try {
|
|
535
822
|
await listRecords(candidate);
|
|
536
823
|
return candidate;
|
|
@@ -554,7 +841,7 @@ function createVercelProvider(options) {
|
|
|
554
841
|
};
|
|
555
842
|
for (const record of records) {
|
|
556
843
|
const recordType = record.type.toUpperCase();
|
|
557
|
-
const name =
|
|
844
|
+
const name = relativeName4(record.host, zone);
|
|
558
845
|
if (recordType === "CNAME") {
|
|
559
846
|
const target = record.value.replace(/\.$/, "").toLowerCase();
|
|
560
847
|
const existing = cached.find(
|
|
@@ -726,6 +1013,24 @@ function createDnsProvider(provider, credentials, fetchImpl) {
|
|
|
726
1013
|
fetchImpl
|
|
727
1014
|
});
|
|
728
1015
|
}
|
|
1016
|
+
if (provider === "netlify") {
|
|
1017
|
+
const apiToken = credentials.netlifyApiToken;
|
|
1018
|
+
if (!apiToken) throw new Error("Missing Netlify token. Set NETLIFY_AUTH_TOKEN or pass netlifyApiToken.");
|
|
1019
|
+
return createNetlifyProvider({ apiToken, fetchImpl });
|
|
1020
|
+
}
|
|
1021
|
+
if (provider === "digitalocean") {
|
|
1022
|
+
const apiToken = credentials.digitaloceanApiToken;
|
|
1023
|
+
if (!apiToken) throw new Error("Missing DigitalOcean token. Set DIGITALOCEAN_TOKEN or pass digitaloceanApiToken.");
|
|
1024
|
+
return createDigitalOceanProvider({ apiToken, fetchImpl });
|
|
1025
|
+
}
|
|
1026
|
+
if (provider === "dnsimple") {
|
|
1027
|
+
const apiToken = credentials.dnsimpleApiToken;
|
|
1028
|
+
const accountId = credentials.dnsimpleAccountId;
|
|
1029
|
+
if (!apiToken || !accountId) {
|
|
1030
|
+
throw new Error("Missing DNSimple credentials. Set DNSIMPLE_TOKEN and DNSIMPLE_ACCOUNT_ID.");
|
|
1031
|
+
}
|
|
1032
|
+
return createDnsimpleProvider({ apiToken, accountId, fetchImpl });
|
|
1033
|
+
}
|
|
729
1034
|
throw new Error(`Unsupported DNS provider: ${provider}`);
|
|
730
1035
|
}
|
|
731
1036
|
async function applyDnsRecords(input) {
|
|
@@ -1059,8 +1364,9 @@ var DomainsResource = class {
|
|
|
1059
1364
|
});
|
|
1060
1365
|
}
|
|
1061
1366
|
/**
|
|
1062
|
-
* Apply DNS using the public API
|
|
1063
|
-
*
|
|
1367
|
+
* Apply DNS using the public API with the DNS host connected in the dashboard (Cloudflare,
|
|
1368
|
+
* Vercel, GoDaddy, Netlify, DigitalOcean or DNSimple). GoDaddy also accepts one-time
|
|
1369
|
+
* credentials on the request.
|
|
1064
1370
|
*/
|
|
1065
1371
|
async apply(idOrName, options = {}) {
|
|
1066
1372
|
return this.client.request(`/domains/${encodeURIComponent(idOrName)}`, {
|
|
@@ -1073,8 +1379,8 @@ var DomainsResource = class {
|
|
|
1073
1379
|
});
|
|
1074
1380
|
}
|
|
1075
1381
|
/**
|
|
1076
|
-
* Fetch Groundmail DNS records, then write them at Cloudflare, Vercel,
|
|
1077
|
-
* using the caller's provider credentials (never sent to Groundmail).
|
|
1382
|
+
* Fetch Groundmail DNS records, then write them at Cloudflare, Vercel, GoDaddy, Netlify,
|
|
1383
|
+
* DigitalOcean or DNSimple using the caller's provider credentials (never sent to Groundmail).
|
|
1078
1384
|
*/
|
|
1079
1385
|
async applyDns(params) {
|
|
1080
1386
|
const detail = await this.get(params.idOrName);
|
|
@@ -1092,7 +1398,11 @@ var DomainsResource = class {
|
|
|
1092
1398
|
godaddyApiKey: params.godaddyApiKey,
|
|
1093
1399
|
godaddyApiSecret: params.godaddyApiSecret,
|
|
1094
1400
|
vercelApiToken: params.vercelApiToken,
|
|
1095
|
-
vercelTeamId: params.vercelTeamId
|
|
1401
|
+
vercelTeamId: params.vercelTeamId,
|
|
1402
|
+
netlifyApiToken: params.netlifyApiToken,
|
|
1403
|
+
digitaloceanApiToken: params.digitaloceanApiToken,
|
|
1404
|
+
dnsimpleApiToken: params.dnsimpleApiToken,
|
|
1405
|
+
dnsimpleAccountId: params.dnsimpleAccountId
|
|
1096
1406
|
},
|
|
1097
1407
|
fetchImpl: this.client.fetchImpl
|
|
1098
1408
|
});
|
package/dist/clerk.d.ts
CHANGED
package/dist/clerk.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
type ApiErrorCode = 'validation_error' | 'unauthorized' | 'forbidden' | 'not_found' | 'conflict' | 'plan_limit'
|
|
2
2
|
/** 429: the send would go over a new account's daily ramp-up limit; see `details.resets_at`. */
|
|
3
3
|
| 'daily_ramp_limit'
|
|
4
|
-
/** 403: the request names a tenant, but tenants are not enabled for this account (
|
|
4
|
+
/** 403: the request names a tenant, but tenants are not enabled for this account (tenants are included on pay as you go and the Dedicated plan). */
|
|
5
5
|
| 'tenants_not_enabled'
|
|
6
6
|
/** 403: the send uses `category: 'newsletter'`, but newsletter sending isn't enabled on this account (approved accounts only). */
|
|
7
7
|
| 'newsletter_not_enabled' | 'domain_limit' | 'idempotency_conflict' | 'rate_limited'
|
|
@@ -1013,7 +1013,7 @@ type UpdateDomainParams = {
|
|
|
1013
1013
|
/** What to do with received mail at or above the threshold. */
|
|
1014
1014
|
inbound_spam_action?: 'flag' | 'drop';
|
|
1015
1015
|
};
|
|
1016
|
-
type DnsProviderName = 'cloudflare' | 'godaddy' | 'vercel';
|
|
1016
|
+
type DnsProviderName = 'cloudflare' | 'godaddy' | 'vercel' | 'netlify' | 'digitalocean' | 'dnsimple';
|
|
1017
1017
|
type RemoteApplyDnsCredentials = {
|
|
1018
1018
|
/** Inline Cloudflare token instead of the stored dashboard integration. */
|
|
1019
1019
|
apiToken?: string;
|
|
@@ -1027,6 +1027,13 @@ type RemoteApplyDnsCredentials = {
|
|
|
1027
1027
|
godaddyApiSecret?: string;
|
|
1028
1028
|
vercelApiToken?: string;
|
|
1029
1029
|
vercelTeamId?: string;
|
|
1030
|
+
/** Inline token for netlify, digitalocean or dnsimple instead of the stored connection. */
|
|
1031
|
+
netlifyApiToken?: string;
|
|
1032
|
+
digitaloceanApiToken?: string;
|
|
1033
|
+
dnsimpleApiToken?: string;
|
|
1034
|
+
/** DNSimple account the inline token belongs to. */
|
|
1035
|
+
dnsimpleAccountId?: string;
|
|
1036
|
+
accountId?: string;
|
|
1030
1037
|
};
|
|
1031
1038
|
type ApplyDnsParams = {
|
|
1032
1039
|
provider: DnsProviderName;
|
|
@@ -1038,6 +1045,10 @@ type ApplyDnsParams = {
|
|
|
1038
1045
|
godaddyApiSecret?: string;
|
|
1039
1046
|
vercelApiToken?: string;
|
|
1040
1047
|
vercelTeamId?: string;
|
|
1048
|
+
netlifyApiToken?: string;
|
|
1049
|
+
digitaloceanApiToken?: string;
|
|
1050
|
+
dnsimpleApiToken?: string;
|
|
1051
|
+
dnsimpleAccountId?: string;
|
|
1041
1052
|
};
|
|
1042
1053
|
type ApplyDnsRecordResult = {
|
|
1043
1054
|
purpose: string;
|
|
@@ -1514,7 +1525,7 @@ type DmarcMonitoringDisabled = {
|
|
|
1514
1525
|
type DmarcStatus = DmarcMonitoring | DmarcMonitoringDisabled;
|
|
1515
1526
|
type EnableDmarcParams = {
|
|
1516
1527
|
/** Also write the record through a DNS provider connected under Settings → Integrations. */
|
|
1517
|
-
apply?:
|
|
1528
|
+
apply?: DnsProviderName;
|
|
1518
1529
|
digest_enabled?: boolean;
|
|
1519
1530
|
};
|
|
1520
1531
|
type EnableDmarcResult = {
|
|
@@ -1561,16 +1572,17 @@ declare class DomainsResource {
|
|
|
1561
1572
|
ok: boolean;
|
|
1562
1573
|
}>;
|
|
1563
1574
|
/**
|
|
1564
|
-
* Apply DNS using the public API
|
|
1565
|
-
*
|
|
1575
|
+
* Apply DNS using the public API with the DNS host connected in the dashboard (Cloudflare,
|
|
1576
|
+
* Vercel, GoDaddy, Netlify, DigitalOcean or DNSimple). GoDaddy also accepts one-time
|
|
1577
|
+
* credentials on the request.
|
|
1566
1578
|
*/
|
|
1567
1579
|
apply(idOrName: string, options?: {
|
|
1568
|
-
provider?:
|
|
1580
|
+
provider?: DnsProviderName;
|
|
1569
1581
|
credentials?: RemoteApplyDnsCredentials;
|
|
1570
1582
|
}): Promise<ApplyDnsResult>;
|
|
1571
1583
|
/**
|
|
1572
|
-
* Fetch Groundmail DNS records, then write them at Cloudflare, Vercel,
|
|
1573
|
-
* using the caller's provider credentials (never sent to Groundmail).
|
|
1584
|
+
* Fetch Groundmail DNS records, then write them at Cloudflare, Vercel, GoDaddy, Netlify,
|
|
1585
|
+
* DigitalOcean or DNSimple using the caller's provider credentials (never sent to Groundmail).
|
|
1574
1586
|
*/
|
|
1575
1587
|
applyDns(params: ApplyDnsParams): Promise<ApplyDnsResult>;
|
|
1576
1588
|
}
|
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 ListApiKeysParams, ap as ListApiKeysResult, aq as ListAutomationRunsParams, ar as ListAutomationRunsResult, as as ListAutomationsResult, at as ListCategoryOptOutsParams, au as ListCategoryOptOutsResult, av as ListDomainsParams, aw as ListDomainsResult, ax as ListEmailsParams, ay as ListEmailsResult, az as ListReceivedEmailsParams, aA as ListReceivedEmailsResult, aB as ListSuppressionsParams, aC as ListSuppressionsResult, aD as ListTemplatesParams, aE as ListTemplatesResult, aF as ListTenantDomainsResult, aG as ListTenantsParams, aH as ListTenantsResult, aI as ListTestInboxEmailsParams, aJ as ListTestInboxEmailsResult, aK as MailServerSigningStatus, aL as MailStream, aM as Migration, aN as MigrationDnsChange, aO as MigrationDnsPlan, aP as MigrationDomainItem, aQ as MigrationItemStatus, aR as MigrationProvider, aS as MigrationResource, aT as MigrationSummary, aU as MigrationTemplateItem, aV as MigrationTotals, aW as MigrationWebhookItem, aX as ReceivedEmail, aY as ReceivedEmailAttachment, aZ as ReceivedEmailAttachmentMeta, a_ as RemoteApplyDnsCredentials, a$ as RemoveCategoryOptOutOptions, b0 as RunMigrationOptions, b1 as SendEmailParams, S as SendEmailResult, b2 as StreamDeliverability, b3 as SuppressedErrorDetails, b4 as Suppression, b5 as TEST_INBOX_WAIT_DEFAULT_MS, b6 as TEST_INBOX_WAIT_MAX_SECONDS, b7 as Template, b8 as TemplateFormat, b9 as TemplateLintWarning, ba as TemplateStarter, bb as TemplateStatus, bc as TemplateVersion, bd as Tenant, be as TenantPauseSource, bf as TenantRamp, bg as TenantStats, bh as TenantStatsResult, bi as TenantStatus, bj as TenantStreamPolicy, bk as TestEmailWebhookParams, bl as TestEmailWebhookResult, bm as TestInbox, bn as TestInboxAddressOptions, bo as TestInboxEmail, bp as TestInboxEmailDetail, bq as TestInboxHeader, br as TestInboxResource, bs as TestInboxUsage, bt as TriggerEventParams, bu as TriggerEventResult, bv as TwilioIntegrationResponse, bw as TwilioIntegrationStatus, bx as UpdateAutomationParams, by as UpdateDomainParams, bz as UpdateEmailParams, bA as UpdateTemplateParams, bB as UpdateTenantParams, bC as UpdateWebhookParams, bD as ValidateEmailResult, bE as ValidationReason, bF as ValidationVerdict, bG as VerifyDomainResult, bH as WaitForTestEmailParams, bI as WebhookEmailTag, bJ as WebhookEndpoint, bK as WebhookEndpointWithSecret, bL as WebhookEventType, bM as mergeMigrations, bN as normalizeAccessToken } from './client-
|
|
1
|
+
import { A as ApiErrorBody, D as DnsRecord } from './client-Btlt7wMg.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 ListApiKeysParams, ap as ListApiKeysResult, aq as ListAutomationRunsParams, ar as ListAutomationRunsResult, as as ListAutomationsResult, at as ListCategoryOptOutsParams, au as ListCategoryOptOutsResult, av as ListDomainsParams, aw as ListDomainsResult, ax as ListEmailsParams, ay as ListEmailsResult, az as ListReceivedEmailsParams, aA as ListReceivedEmailsResult, aB as ListSuppressionsParams, aC as ListSuppressionsResult, aD as ListTemplatesParams, aE as ListTemplatesResult, aF as ListTenantDomainsResult, aG as ListTenantsParams, aH as ListTenantsResult, aI as ListTestInboxEmailsParams, aJ as ListTestInboxEmailsResult, aK as MailServerSigningStatus, aL as MailStream, aM as Migration, aN as MigrationDnsChange, aO as MigrationDnsPlan, aP as MigrationDomainItem, aQ as MigrationItemStatus, aR as MigrationProvider, aS as MigrationResource, aT as MigrationSummary, aU as MigrationTemplateItem, aV as MigrationTotals, aW as MigrationWebhookItem, aX as ReceivedEmail, aY as ReceivedEmailAttachment, aZ as ReceivedEmailAttachmentMeta, a_ as RemoteApplyDnsCredentials, a$ as RemoveCategoryOptOutOptions, b0 as RunMigrationOptions, b1 as SendEmailParams, S as SendEmailResult, b2 as StreamDeliverability, b3 as SuppressedErrorDetails, b4 as Suppression, b5 as TEST_INBOX_WAIT_DEFAULT_MS, b6 as TEST_INBOX_WAIT_MAX_SECONDS, b7 as Template, b8 as TemplateFormat, b9 as TemplateLintWarning, ba as TemplateStarter, bb as TemplateStatus, bc as TemplateVersion, bd as Tenant, be as TenantPauseSource, bf as TenantRamp, bg as TenantStats, bh as TenantStatsResult, bi as TenantStatus, bj as TenantStreamPolicy, bk as TestEmailWebhookParams, bl as TestEmailWebhookResult, bm as TestInbox, bn as TestInboxAddressOptions, bo as TestInboxEmail, bp as TestInboxEmailDetail, bq as TestInboxHeader, br as TestInboxResource, bs as TestInboxUsage, bt as TriggerEventParams, bu as TriggerEventResult, bv as TwilioIntegrationResponse, bw as TwilioIntegrationStatus, bx as UpdateAutomationParams, by as UpdateDomainParams, bz as UpdateEmailParams, bA as UpdateTemplateParams, bB as UpdateTenantParams, bC as UpdateWebhookParams, bD as ValidateEmailResult, bE as ValidationReason, bF as ValidationVerdict, bG as VerifyDomainResult, bH as WaitForTestEmailParams, bI as WebhookEmailTag, bJ as WebhookEndpoint, bK as WebhookEndpointWithSecret, bL as WebhookEventType, bM as mergeMigrations, bN as normalizeAccessToken } from './client-Btlt7wMg.js';
|
|
3
3
|
|
|
4
4
|
type GroundmailErrorOptions = {
|
|
5
5
|
code?: string;
|
|
@@ -80,7 +80,7 @@ declare function isTestApiKey(key: string | null | undefined): boolean;
|
|
|
80
80
|
/** Merge `include:host` into an existing SPF record (or create a minimal one). */
|
|
81
81
|
declare function mergeSpfInclude(existing: string | null | undefined, includeHost: string): string;
|
|
82
82
|
|
|
83
|
-
type DnsProviderName = 'cloudflare' | 'godaddy' | 'vercel';
|
|
83
|
+
type DnsProviderName = 'cloudflare' | 'godaddy' | 'vercel' | 'netlify' | 'digitalocean' | 'dnsimple';
|
|
84
84
|
type ApplyDnsRecordResult = {
|
|
85
85
|
purpose: string;
|
|
86
86
|
host: string;
|
|
@@ -99,6 +99,10 @@ type ApplyDnsCredentials = {
|
|
|
99
99
|
godaddyApiSecret?: string;
|
|
100
100
|
vercelApiToken?: string;
|
|
101
101
|
vercelTeamId?: string;
|
|
102
|
+
netlifyApiToken?: string;
|
|
103
|
+
digitaloceanApiToken?: string;
|
|
104
|
+
dnsimpleApiToken?: string;
|
|
105
|
+
dnsimpleAccountId?: string;
|
|
102
106
|
};
|
|
103
107
|
|
|
104
108
|
declare function applyDnsRecords(input: {
|
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.
|
|
3
|
+
"version": "0.27.0",
|
|
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.15"
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
95
|
"@types/nodemailer": "^8.0.1",
|