dkimkit 0.0.0-stage → 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.
Files changed (128) hide show
  1. package/README.md +153 -2
  2. package/dist/src/cli.d.ts +2 -0
  3. package/dist/src/cli.js +398 -0
  4. package/dist/src/cli.js.map +1 -0
  5. package/dist/src/dns-host.d.ts +10 -0
  6. package/dist/src/dns-host.js +31 -0
  7. package/dist/src/dns-host.js.map +1 -0
  8. package/dist/src/errors.d.ts +17 -0
  9. package/dist/src/errors.js +51 -0
  10. package/dist/src/errors.js.map +1 -0
  11. package/dist/src/hosted/api.d.ts +95 -0
  12. package/dist/src/hosted/api.js +781 -0
  13. package/dist/src/hosted/api.js.map +1 -0
  14. package/dist/src/hosted/auth.d.ts +70 -0
  15. package/dist/src/hosted/auth.js +121 -0
  16. package/dist/src/hosted/auth.js.map +1 -0
  17. package/dist/src/hosted/billing-dodo.d.ts +58 -0
  18. package/dist/src/hosted/billing-dodo.js +139 -0
  19. package/dist/src/hosted/billing-dodo.js.map +1 -0
  20. package/dist/src/hosted/billing-stripe.d.ts +82 -0
  21. package/dist/src/hosted/billing-stripe.js +205 -0
  22. package/dist/src/hosted/billing-stripe.js.map +1 -0
  23. package/dist/src/hosted/billing.d.ts +114 -0
  24. package/dist/src/hosted/billing.js +173 -0
  25. package/dist/src/hosted/billing.js.map +1 -0
  26. package/dist/src/hosted/cli.d.ts +2 -0
  27. package/dist/src/hosted/cli.js +305 -0
  28. package/dist/src/hosted/cli.js.map +1 -0
  29. package/dist/src/hosted/crypto.d.ts +22 -0
  30. package/dist/src/hosted/crypto.js +55 -0
  31. package/dist/src/hosted/crypto.js.map +1 -0
  32. package/dist/src/hosted/index.d.ts +7 -0
  33. package/dist/src/hosted/index.js +11 -0
  34. package/dist/src/hosted/index.js.map +1 -0
  35. package/dist/src/hosted/markdown.d.ts +16 -0
  36. package/dist/src/hosted/markdown.js +168 -0
  37. package/dist/src/hosted/markdown.js.map +1 -0
  38. package/dist/src/hosted/net.d.ts +9 -0
  39. package/dist/src/hosted/net.js +60 -0
  40. package/dist/src/hosted/net.js.map +1 -0
  41. package/dist/src/hosted/postgres-store.d.ts +62 -0
  42. package/dist/src/hosted/postgres-store.js +387 -0
  43. package/dist/src/hosted/postgres-store.js.map +1 -0
  44. package/dist/src/hosted/serialize.d.ts +144 -0
  45. package/dist/src/hosted/serialize.js +126 -0
  46. package/dist/src/hosted/serialize.js.map +1 -0
  47. package/dist/src/hosted/service.d.ts +166 -0
  48. package/dist/src/hosted/service.js +582 -0
  49. package/dist/src/hosted/service.js.map +1 -0
  50. package/dist/src/hosted/site.d.ts +54 -0
  51. package/dist/src/hosted/site.js +472 -0
  52. package/dist/src/hosted/site.js.map +1 -0
  53. package/dist/src/hosted/sqlite-store.d.ts +45 -0
  54. package/dist/src/hosted/sqlite-store.js +351 -0
  55. package/dist/src/hosted/sqlite-store.js.map +1 -0
  56. package/dist/src/hosted/store.d.ts +257 -0
  57. package/dist/src/hosted/store.js +181 -0
  58. package/dist/src/hosted/store.js.map +1 -0
  59. package/dist/src/hosted/widget.d.ts +46 -0
  60. package/dist/src/hosted/widget.js +130 -0
  61. package/dist/src/hosted/widget.js.map +1 -0
  62. package/dist/src/hosted/worker.d.ts +45 -0
  63. package/dist/src/hosted/worker.js +142 -0
  64. package/dist/src/hosted/worker.js.map +1 -0
  65. package/dist/src/http.d.ts +14 -0
  66. package/dist/src/http.js +54 -0
  67. package/dist/src/http.js.map +1 -0
  68. package/dist/src/index.d.ts +19 -0
  69. package/dist/src/index.js +19 -0
  70. package/dist/src/index.js.map +1 -0
  71. package/dist/src/orchestrator.d.ts +70 -0
  72. package/dist/src/orchestrator.js +69 -0
  73. package/dist/src/orchestrator.js.map +1 -0
  74. package/dist/src/propagation.d.ts +30 -0
  75. package/dist/src/propagation.js +76 -0
  76. package/dist/src/propagation.js.map +1 -0
  77. package/dist/src/providers/dns/aws.d.ts +28 -0
  78. package/dist/src/providers/dns/aws.js +103 -0
  79. package/dist/src/providers/dns/aws.js.map +1 -0
  80. package/dist/src/providers/dns/cloudflare.d.ts +26 -0
  81. package/dist/src/providers/dns/cloudflare.js +97 -0
  82. package/dist/src/providers/dns/cloudflare.js.map +1 -0
  83. package/dist/src/providers/dns/manual.d.ts +12 -0
  84. package/dist/src/providers/dns/manual.js +18 -0
  85. package/dist/src/providers/dns/manual.js.map +1 -0
  86. package/dist/src/providers/dns/route53.d.ts +34 -0
  87. package/dist/src/providers/dns/route53.js +176 -0
  88. package/dist/src/providers/dns/route53.js.map +1 -0
  89. package/dist/src/providers/email/factory.d.ts +29 -0
  90. package/dist/src/providers/email/factory.js +67 -0
  91. package/dist/src/providers/email/factory.js.map +1 -0
  92. package/dist/src/providers/email/mailgun.d.ts +36 -0
  93. package/dist/src/providers/email/mailgun.js +141 -0
  94. package/dist/src/providers/email/mailgun.js.map +1 -0
  95. package/dist/src/providers/email/postmark.d.ts +37 -0
  96. package/dist/src/providers/email/postmark.js +121 -0
  97. package/dist/src/providers/email/postmark.js.map +1 -0
  98. package/dist/src/providers/email/sendgrid.d.ts +31 -0
  99. package/dist/src/providers/email/sendgrid.js +109 -0
  100. package/dist/src/providers/email/sendgrid.js.map +1 -0
  101. package/dist/src/providers/email/ses.d.ts +39 -0
  102. package/dist/src/providers/email/ses.js +123 -0
  103. package/dist/src/providers/email/ses.js.map +1 -0
  104. package/dist/src/providers/factory.d.ts +14 -0
  105. package/dist/src/providers/factory.js +32 -0
  106. package/dist/src/providers/factory.js.map +1 -0
  107. package/dist/src/records.d.ts +35 -0
  108. package/dist/src/records.js +109 -0
  109. package/dist/src/records.js.map +1 -0
  110. package/dist/src/server.d.ts +67 -0
  111. package/dist/src/server.js +301 -0
  112. package/dist/src/server.js.map +1 -0
  113. package/dist/src/types.d.ts +75 -0
  114. package/dist/src/types.js +2 -0
  115. package/dist/src/types.js.map +1 -0
  116. package/dist/src/xml.d.ts +6 -0
  117. package/dist/src/xml.js +23 -0
  118. package/dist/src/xml.js.map +1 -0
  119. package/docs/billing.md +43 -0
  120. package/docs/getting-started.md +57 -0
  121. package/docs/hosted-api.md +192 -0
  122. package/docs/self-hosting.md +148 -0
  123. package/docs/widget.md +121 -0
  124. package/package.json +63 -4
  125. package/web/dashboard.html +390 -0
  126. package/web/index.html +697 -0
  127. package/web/widget-frame.html +480 -0
  128. package/web/widget.js +91 -0
@@ -0,0 +1,121 @@
1
+ import { ConfigError } from '../../errors.js';
2
+ import { requestJson } from '../../http.js';
3
+ import { normalizeName } from '../../records.js';
4
+ export const POSTMARK_INBOUND_MX = 'inbound.postmarkapp.com';
5
+ const DEFAULT_RETURN_PATH_CNAME = 'pm.mtasv.net';
6
+ /**
7
+ * Postmark sender signatures for domains: a DKIM TXT record and a custom
8
+ * Return-Path CNAME. SPF is no longer required by Postmark.
9
+ */
10
+ export class PostmarkProvider {
11
+ opts;
12
+ name = 'postmark';
13
+ label = 'Postmark';
14
+ supportsInbound = true;
15
+ baseUrl;
16
+ fetchImpl;
17
+ constructor(opts) {
18
+ this.opts = opts;
19
+ this.baseUrl = opts.baseUrl ?? 'https://api.postmarkapp.com';
20
+ this.fetchImpl = opts.fetch ?? fetch;
21
+ }
22
+ defaultInboundHostname(domain) {
23
+ return `parse.${normalizeName(domain)}`;
24
+ }
25
+ async findDomain(domain) {
26
+ const wanted = normalizeName(domain);
27
+ for (let offset = 0;; offset += 500) {
28
+ const page = await this.request('GET', `/domains?count=500&offset=${offset}`);
29
+ const hit = page.Domains.find((d) => normalizeName(d.Name) === wanted);
30
+ if (hit)
31
+ return this.getDomain(String(hit.ID));
32
+ if (page.Domains.length === 0 || offset + page.Domains.length >= page.TotalCount)
33
+ return null;
34
+ }
35
+ }
36
+ async ensureDomain(domain, opts = {}) {
37
+ const existing = await this.findDomain(domain);
38
+ if (existing)
39
+ return existing;
40
+ const name = normalizeName(domain);
41
+ const created = await this.request('POST', '/domains', {
42
+ body: { Name: name, ReturnPathDomain: `${opts.subdomain ?? 'pm-bounces'}.${name}` },
43
+ });
44
+ return toAuthentication(created, true);
45
+ }
46
+ async getDomain(id) {
47
+ return toAuthentication(await this.request('GET', `/domains/${encodeURIComponent(id)}`), false);
48
+ }
49
+ async deleteDomain(id) {
50
+ await this.request('DELETE', `/domains/${encodeURIComponent(id)}`);
51
+ }
52
+ async validateDomain(id) {
53
+ await this.request('PUT', `/domains/${encodeURIComponent(id)}/verifyDkim`);
54
+ // Both calls return the domain; the second has both up-to-date flags.
55
+ const d = await this.request('PUT', `/domains/${encodeURIComponent(id)}/verifyReturnPath`);
56
+ const dkim = Boolean(d.DKIMVerified);
57
+ const returnPath = Boolean(d.ReturnPathDomainVerified);
58
+ return {
59
+ valid: dkim && returnPath,
60
+ results: [
61
+ { record: 'dkim', valid: dkim, ...(dkim ? {} : { reason: 'DKIM record not found yet' }) },
62
+ { record: 'return_path', valid: returnPath, ...(returnPath ? {} : { reason: 'Return-Path CNAME not found yet' }) },
63
+ ],
64
+ };
65
+ }
66
+ inboundRecords(hostname) {
67
+ return [{ type: 'MX', name: normalizeName(hostname), value: POSTMARK_INBOUND_MX, priority: 10, purpose: 'inbound_mx' }];
68
+ }
69
+ async ensureInboundParse(opts) {
70
+ const server = await this.inboundServer();
71
+ const hostname = normalizeName(opts.hostname);
72
+ const current = server.InboundDomain ? normalizeName(server.InboundDomain) : '';
73
+ if (current && current !== hostname) {
74
+ throw new ConfigError(`Postmark server ${server.ID} already receives mail for ${current}. Postmark allows one inbound domain per server, so use another server for ${hostname}.`);
75
+ }
76
+ if (current === hostname && server.InboundHookUrl === opts.url)
77
+ return 'unchanged';
78
+ await this.request('PUT', `/servers/${server.ID}`, { body: { InboundDomain: hostname, InboundHookUrl: opts.url } });
79
+ return current ? 'updated' : 'created';
80
+ }
81
+ async deleteInboundParse(hostname) {
82
+ if (!this.opts.inboundServerId)
83
+ return;
84
+ const server = await this.inboundServer();
85
+ if (server.InboundDomain && normalizeName(server.InboundDomain) === normalizeName(hostname)) {
86
+ await this.request('PUT', `/servers/${server.ID}`, { body: { InboundDomain: '', InboundHookUrl: '' } });
87
+ }
88
+ }
89
+ inboundServer() {
90
+ if (!this.opts.inboundServerId)
91
+ throw new ConfigError('Receiving email with Postmark needs the id of the Postmark server to receive on.');
92
+ return this.request('GET', `/servers/${encodeURIComponent(this.opts.inboundServerId)}`);
93
+ }
94
+ request(method, path, opts = {}) {
95
+ return requestJson(this.fetchImpl, 'Postmark', this.baseUrl + path, { ...opts, method, headers: { 'X-Postmark-Account-Token': this.opts.accountToken } }, (body) => body?.Message);
96
+ }
97
+ }
98
+ function toAuthentication(d, created) {
99
+ const domain = normalizeName(d.Name);
100
+ // Postmark may give hosts relative to the domain.
101
+ const qualify = (host) => {
102
+ const h = normalizeName(host);
103
+ return h === domain || h.endsWith(`.${domain}`) ? h : `${h}.${domain}`;
104
+ };
105
+ const records = [];
106
+ // A new or rotating key is published at the pending host first.
107
+ const dkimHost = d.DKIMPendingHost || d.DKIMHost;
108
+ const dkimValue = d.DKIMPendingTextValue || d.DKIMTextValue;
109
+ if (dkimHost && dkimValue)
110
+ records.push({ type: 'TXT', name: qualify(dkimHost), value: dkimValue, purpose: 'dkim' });
111
+ if (d.ReturnPathDomain) {
112
+ records.push({
113
+ type: 'CNAME',
114
+ name: qualify(d.ReturnPathDomain),
115
+ value: d.ReturnPathDomainCNAMEValue || DEFAULT_RETURN_PATH_CNAME,
116
+ purpose: 'return_path',
117
+ });
118
+ }
119
+ return { id: String(d.ID), domain, valid: Boolean(d.DKIMVerified && d.ReturnPathDomainVerified), records, created };
120
+ }
121
+ //# sourceMappingURL=postmark.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postmark.js","sourceRoot":"","sources":["../../../../src/providers/email/postmark.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { type FetchLike } from '../../http.js';
2
+ import type { DnsRecord, DomainAuthentication, EmailProvider, InboundAction, InboundParseOptions, ValidationResult } from '../../types.js';
3
+ export declare const SENDGRID_INBOUND_MX = "mx.sendgrid.net";
4
+ export interface SendGridOptions {
5
+ apiKey: string;
6
+ /** Act on behalf of a subuser — how multi-tenant apps keep each customer's domains separate. */
7
+ onBehalfOf?: string;
8
+ baseUrl?: string;
9
+ fetch?: FetchLike;
10
+ }
11
+ export declare class SendGridProvider implements EmailProvider {
12
+ private readonly opts;
13
+ readonly name = "sendgrid";
14
+ readonly label = "SendGrid";
15
+ readonly supportsInbound = true;
16
+ defaultInboundHostname(domain: string): string;
17
+ private readonly baseUrl;
18
+ private readonly fetchImpl;
19
+ constructor(opts: SendGridOptions);
20
+ findDomain(domain: string, subdomain?: string): Promise<DomainAuthentication | null>;
21
+ ensureDomain(domain: string, opts?: {
22
+ subdomain?: string;
23
+ }): Promise<DomainAuthentication>;
24
+ getDomain(id: string): Promise<DomainAuthentication>;
25
+ deleteDomain(id: string): Promise<void>;
26
+ validateDomain(id: string): Promise<ValidationResult>;
27
+ inboundRecords(hostname: string): DnsRecord[];
28
+ ensureInboundParse(opts: InboundParseOptions): Promise<InboundAction>;
29
+ deleteInboundParse(hostname: string): Promise<void>;
30
+ private request;
31
+ }
@@ -0,0 +1,109 @@
1
+ import { requestJson } from '../../http.js';
2
+ import { isRecordType, normalizeName } from '../../records.js';
3
+ export const SENDGRID_INBOUND_MX = 'mx.sendgrid.net';
4
+ export class SendGridProvider {
5
+ opts;
6
+ name = 'sendgrid';
7
+ label = 'SendGrid';
8
+ supportsInbound = true;
9
+ defaultInboundHostname(domain) {
10
+ return `parse.${normalizeName(domain)}`;
11
+ }
12
+ baseUrl;
13
+ fetchImpl;
14
+ constructor(opts) {
15
+ this.opts = opts;
16
+ this.baseUrl = opts.baseUrl ?? 'https://api.sendgrid.com/v3';
17
+ this.fetchImpl = opts.fetch ?? fetch;
18
+ }
19
+ async findDomain(domain, subdomain) {
20
+ // `domain` is a search filter on SendGrid's side, so match exactly here.
21
+ const wanted = normalizeName(domain);
22
+ const list = await this.request(`/whitelabel/domains?domain=${encodeURIComponent(wanted)}&limit=100`);
23
+ const match = list.find((d) => normalizeName(d.domain) === wanted &&
24
+ (!subdomain || d.subdomain?.toLowerCase() === subdomain.toLowerCase()));
25
+ return match ? toAuthentication(match, false) : null;
26
+ }
27
+ async ensureDomain(domain, opts = {}) {
28
+ const existing = await this.findDomain(domain, opts.subdomain);
29
+ if (existing)
30
+ return existing;
31
+ const created = await this.request('/whitelabel/domains', {
32
+ method: 'POST',
33
+ body: { domain: normalizeName(domain), subdomain: opts.subdomain, automatic_security: true },
34
+ });
35
+ return toAuthentication(created, true);
36
+ }
37
+ async getDomain(id) {
38
+ return toAuthentication(await this.request(`/whitelabel/domains/${encodeURIComponent(id)}`), false);
39
+ }
40
+ async deleteDomain(id) {
41
+ await this.request(`/whitelabel/domains/${encodeURIComponent(id)}`, { method: 'DELETE' });
42
+ }
43
+ async validateDomain(id) {
44
+ const res = await this.request(`/whitelabel/domains/${encodeURIComponent(id)}/validate`, {
45
+ method: 'POST',
46
+ });
47
+ return {
48
+ valid: res.valid,
49
+ results: Object.entries(res.validation_results ?? {}).map(([record, r]) => ({
50
+ record,
51
+ valid: r.valid,
52
+ ...(r.reason ? { reason: r.reason } : {}),
53
+ })),
54
+ };
55
+ }
56
+ inboundRecords(hostname) {
57
+ return [{ type: 'MX', name: normalizeName(hostname), value: SENDGRID_INBOUND_MX, priority: 10, purpose: 'inbound_mx' }];
58
+ }
59
+ async ensureInboundParse(opts) {
60
+ const hostname = normalizeName(opts.hostname);
61
+ const desired = { url: opts.url, spam_check: opts.spamCheck ?? false, send_raw: opts.sendRaw ?? false };
62
+ const { result = [] } = await this.request('/user/parse/settings');
63
+ const existing = result.find((s) => normalizeName(s.hostname) === hostname);
64
+ if (!existing) {
65
+ await this.request('/user/parse/settings', { method: 'POST', body: { hostname, ...desired } });
66
+ return 'created';
67
+ }
68
+ if (existing.url === desired.url &&
69
+ Boolean(existing.spam_check) === desired.spam_check &&
70
+ Boolean(existing.send_raw) === desired.send_raw) {
71
+ return 'unchanged';
72
+ }
73
+ await this.request(`/user/parse/settings/${encodeURIComponent(hostname)}`, { method: 'PATCH', body: desired });
74
+ return 'updated';
75
+ }
76
+ async deleteInboundParse(hostname) {
77
+ await this.request(`/user/parse/settings/${encodeURIComponent(normalizeName(hostname))}`, { method: 'DELETE' });
78
+ }
79
+ request(path, opts = {}) {
80
+ const headers = { Authorization: `Bearer ${this.opts.apiKey}` };
81
+ if (this.opts.onBehalfOf)
82
+ headers['on-behalf-of'] = this.opts.onBehalfOf;
83
+ return requestJson(this.fetchImpl, 'SendGrid', this.baseUrl + path, { ...opts, headers }, describeSendGridError);
84
+ }
85
+ }
86
+ function toAuthentication(d, created) {
87
+ const records = Object.entries(d.dns ?? {}).flatMap(([purpose, entry]) => {
88
+ const type = entry.type.toUpperCase();
89
+ if (!isRecordType(type))
90
+ return [];
91
+ return [
92
+ {
93
+ type,
94
+ name: normalizeName(entry.host),
95
+ value: entry.data,
96
+ purpose,
97
+ ...(type === 'MX' ? { priority: 10 } : {}),
98
+ },
99
+ ];
100
+ });
101
+ return { id: String(d.id), domain: d.domain, valid: d.valid, records, created };
102
+ }
103
+ function describeSendGridError(body) {
104
+ const errors = body?.errors;
105
+ if (!Array.isArray(errors) || errors.length === 0)
106
+ return undefined;
107
+ return errors.map((e) => (e.field ? `${e.message} (${e.field})` : e.message)).join('; ');
108
+ }
109
+ //# sourceMappingURL=sendgrid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sendgrid.js","sourceRoot":"","sources":["../../../../src/providers/email/sendgrid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAuC,MAAM,eAAe,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAU/D,MAAM,CAAC,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;AAsCrD,MAAM,OAAO,gBAAgB;IAWE;IAVpB,IAAI,GAAG,UAAU,CAAC;IAClB,KAAK,GAAG,UAAU,CAAC;IACnB,eAAe,GAAG,IAAI,CAAC;IAEhC,sBAAsB,CAAC,MAAc;QACnC,OAAO,SAAS,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;IAC1C,CAAC;IACgB,OAAO,CAAS;IAChB,SAAS,CAAY;IAEtC,YAA6B,IAAqB;QAArB,SAAI,GAAJ,IAAI,CAAiB;QAChD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,6BAA6B,CAAC;QAC7D,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,MAAc,EAAE,SAAkB;QACjD,yEAAyE;QACzE,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAa,8BAA8B,kBAAkB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAClH,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CACrB,CAAC,CAAC,EAAE,EAAE,CACJ,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,MAAM;YAClC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,SAAS,CAAC,WAAW,EAAE,CAAC,CACzE,CAAC;QACF,OAAO,KAAK,CAAC,CAAC,CAAC,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,MAAc,EAAE,OAA+B,EAAE;QAClE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/D,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAC9B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAW,qBAAqB,EAAE;YAClE,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,EAAE,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,kBAAkB,EAAE,IAAI,EAAE;SAC7F,CAAC,CAAC;QACH,OAAO,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,EAAU;QACxB,OAAO,gBAAgB,CAAC,MAAM,IAAI,CAAC,OAAO,CAAW,uBAAuB,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;IAChH,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,EAAU;QAC3B,MAAM,IAAI,CAAC,OAAO,CAAC,uBAAuB,kBAAkB,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC5F,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,EAAU;QAC7B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAAe,uBAAuB,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE;YACrG,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;QACH,OAAO;YACL,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC1E,MAAM;gBACN,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC1C,CAAC,CAAC;SACJ,CAAC;IACJ,CAAC;IAED,cAAc,CAAC,QAAgB;QAC7B,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;IAC1H,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,IAAyB;QAChD,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,CAAC,SAAS,IAAI,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,IAAI,KAAK,EAAE,CAAC;QACxG,MAAM,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,MAAM,IAAI,CAAC,OAAO,CAAgC,sBAAsB,CAAC,CAAC;QAClG,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,CAAC;QAE5E,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,GAAG,OAAO,EAAE,EAAE,CAAC,CAAC;YAC/F,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IACE,QAAQ,CAAC,GAAG,KAAK,OAAO,CAAC,GAAG;YAC5B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC,UAAU;YACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,OAAO,CAAC,QAAQ,EAC/C,CAAC;YACD,OAAO,WAAW,CAAC;QACrB,CAAC;QACD,MAAM,IAAI,CAAC,OAAO,CAAC,wBAAwB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/G,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,QAAgB;QACvC,MAAM,IAAI,CAAC,OAAO,CAAC,wBAAwB,kBAAkB,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAClH,CAAC;IAEO,OAAO,CAAI,IAAY,EAAE,OAAuB,EAAE;QACxD,MAAM,OAAO,GAA2B,EAAE,aAAa,EAAE,UAAU,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACxF,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC;QACzE,OAAO,WAAW,CAAI,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,EAAE,qBAAqB,CAAC,CAAC;IACtH,CAAC;CACF;AAED,SAAS,gBAAgB,CAAC,CAAW,EAAE,OAAgB;IACrD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,EAAe,EAAE;QACpF,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACtC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QACnC,OAAO;YACL;gBACE,IAAI;gBACJ,IAAI,EAAE,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC;gBAC/B,KAAK,EAAE,KAAK,CAAC,IAAI;gBACjB,OAAO;gBACP,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC3C;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAClF,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAa;IAC1C,MAAM,MAAM,GAAI,IAA+E,EAAE,MAAM,CAAC;IACxG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACpE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC3F,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { type FetchLike } from '../../http.js';
2
+ import type { DnsRecord, DomainAuthentication, EmailProvider, InboundAction, ValidationResult } from '../../types.js';
3
+ import { type AwsCredentials } from '../dns/aws.js';
4
+ export interface SesOptions {
5
+ credentials: AwsCredentials;
6
+ /** The SES region, e.g. us-east-1. */
7
+ region: string;
8
+ baseUrl?: string;
9
+ fetch?: FetchLike;
10
+ }
11
+ /**
12
+ * Amazon SES (API v2) domain identities with Easy DKIM (three CNAMEs) and a
13
+ * custom MAIL FROM subdomain (an MX for bounces and an SPF TXT). SES checks DNS
14
+ * by itself, so "validate" reads the current status.
15
+ */
16
+ export declare class SesProvider implements EmailProvider {
17
+ private readonly opts;
18
+ readonly name = "ses";
19
+ readonly label = "Amazon SES";
20
+ readonly supportsInbound = false;
21
+ private readonly baseUrl;
22
+ private readonly fetchImpl;
23
+ constructor(opts: SesOptions);
24
+ defaultInboundHostname(domain: string): string;
25
+ findDomain(domain: string): Promise<DomainAuthentication | null>;
26
+ /** `subdomain` is the MAIL FROM label (default "mail", giving mail.<domain>). */
27
+ ensureDomain(domain: string, opts?: {
28
+ subdomain?: string;
29
+ }): Promise<DomainAuthentication>;
30
+ getDomain(id: string): Promise<DomainAuthentication>;
31
+ deleteDomain(id: string): Promise<void>;
32
+ validateDomain(id: string): Promise<ValidationResult>;
33
+ inboundRecords(): DnsRecord[];
34
+ ensureInboundParse(): Promise<InboundAction>;
35
+ deleteInboundParse(): Promise<void>;
36
+ private identity;
37
+ private toAuthentication;
38
+ private request;
39
+ }
@@ -0,0 +1,123 @@
1
+ import { ApiError, ConfigError } from '../../errors.js';
2
+ import { requestText } from '../../http.js';
3
+ import { normalizeName } from '../../records.js';
4
+ import { signRequest } from '../dns/aws.js';
5
+ const INBOUND_UNSUPPORTED = 'Receiving email with Amazon SES isn’t supported yet. Use SendGrid, Postmark or Mailgun for inbound mail.';
6
+ /**
7
+ * Amazon SES (API v2) domain identities with Easy DKIM (three CNAMEs) and a
8
+ * custom MAIL FROM subdomain (an MX for bounces and an SPF TXT). SES checks DNS
9
+ * by itself, so "validate" reads the current status.
10
+ */
11
+ export class SesProvider {
12
+ opts;
13
+ name = 'ses';
14
+ label = 'Amazon SES';
15
+ supportsInbound = false;
16
+ baseUrl;
17
+ fetchImpl;
18
+ constructor(opts) {
19
+ this.opts = opts;
20
+ if (!/^[a-z]{2}(-[a-z]+)+-\d$/.test(opts.region))
21
+ throw new ConfigError(`"${opts.region}" isn’t an AWS region (e.g. us-east-1).`);
22
+ this.baseUrl = opts.baseUrl ?? `https://email.${opts.region}.amazonaws.com`;
23
+ this.fetchImpl = opts.fetch ?? fetch;
24
+ }
25
+ defaultInboundHostname(domain) {
26
+ return `parse.${normalizeName(domain)}`;
27
+ }
28
+ async findDomain(domain) {
29
+ const name = normalizeName(domain);
30
+ try {
31
+ return this.toAuthentication(name, await this.identity(name), false);
32
+ }
33
+ catch (err) {
34
+ if (err instanceof ApiError && err.status === 404)
35
+ return null;
36
+ throw err;
37
+ }
38
+ }
39
+ /** `subdomain` is the MAIL FROM label (default "mail", giving mail.<domain>). */
40
+ async ensureDomain(domain, opts = {}) {
41
+ const existing = await this.findDomain(domain);
42
+ if (existing)
43
+ return existing;
44
+ const name = normalizeName(domain);
45
+ await this.request('POST', '/v2/email/identities', { EmailIdentity: name });
46
+ await this.request('PUT', `/v2/email/identities/${encodeURIComponent(name)}/mail-from`, {
47
+ MailFromDomain: `${opts.subdomain ?? 'mail'}.${name}`,
48
+ // Until the MX is in place, SES falls back to its own MAIL FROM rather than rejecting mail.
49
+ BehaviorOnMxFailure: 'USE_DEFAULT_VALUE',
50
+ });
51
+ return this.toAuthentication(name, await this.identity(name), true);
52
+ }
53
+ async getDomain(id) {
54
+ return this.toAuthentication(id, await this.identity(id), false);
55
+ }
56
+ async deleteDomain(id) {
57
+ await this.request('DELETE', `/v2/email/identities/${encodeURIComponent(id)}`);
58
+ }
59
+ async validateDomain(id) {
60
+ const identity = await this.identity(id);
61
+ const dkimStatus = identity.DkimAttributes?.Status ?? 'NOT_STARTED';
62
+ const dkim = dkimStatus === 'SUCCESS';
63
+ const results = [{ record: 'dkim', valid: dkim, ...(dkim ? {} : { reason: `DKIM status: ${dkimStatus.toLowerCase().replace(/_/g, ' ')}` }) }];
64
+ const mailFrom = identity.MailFromAttributes;
65
+ if (mailFrom?.MailFromDomain) {
66
+ const ok = mailFrom.MailFromDomainStatus === 'SUCCESS';
67
+ const status = (mailFrom.MailFromDomainStatus ?? 'PENDING').toLowerCase().replace(/_/g, ' ');
68
+ results.push({ record: 'mail_from', valid: ok, ...(ok ? {} : { reason: `MAIL FROM status: ${status}` }) });
69
+ }
70
+ // Sending is allowed once the identity (via DKIM) verifies; MAIL FROM falls back until its MX is found.
71
+ return { valid: Boolean(identity.VerifiedForSendingStatus) && dkim, results };
72
+ }
73
+ inboundRecords() {
74
+ throw new ConfigError(INBOUND_UNSUPPORTED);
75
+ }
76
+ async ensureInboundParse() {
77
+ throw new ConfigError(INBOUND_UNSUPPORTED);
78
+ }
79
+ async deleteInboundParse() { }
80
+ identity(name) {
81
+ return this.request('GET', `/v2/email/identities/${encodeURIComponent(name)}`);
82
+ }
83
+ toAuthentication(domain, identity, created) {
84
+ const records = (identity.DkimAttributes?.Tokens ?? []).map((token, i) => ({
85
+ type: 'CNAME',
86
+ name: `${token}._domainkey.${domain}`,
87
+ value: `${token}.dkim.amazonses.com`,
88
+ purpose: `dkim${i + 1}`,
89
+ }));
90
+ const mailFrom = identity.MailFromAttributes?.MailFromDomain;
91
+ if (mailFrom) {
92
+ const host = normalizeName(mailFrom);
93
+ records.push({ type: 'MX', name: host, value: `feedback-smtp.${this.opts.region}.amazonses.com`, priority: 10, purpose: 'mail_from_mx' });
94
+ records.push({
95
+ type: 'TXT',
96
+ name: host,
97
+ value: 'v=spf1 include:amazonses.com ~all',
98
+ purpose: 'mail_from_spf',
99
+ note: 'If this name already has an SPF record (v=spf1 …), add include:amazonses.com to it instead of creating a second one.',
100
+ });
101
+ }
102
+ return { id: domain, domain, valid: Boolean(identity.VerifiedForSendingStatus), records, created };
103
+ }
104
+ async request(method, path, body) {
105
+ const url = this.baseUrl + path;
106
+ const payload = body === undefined ? undefined : JSON.stringify(body);
107
+ const text = await requestText(this.fetchImpl, 'Amazon SES', url, () => ({
108
+ method,
109
+ body: payload,
110
+ headers: signRequest({ method, url, body: payload, headers: payload ? { 'content-type': 'application/json' } : {} }, this.opts.credentials, this.opts.region, 'ses'),
111
+ }), (t) => {
112
+ try {
113
+ const b = JSON.parse(t);
114
+ return b.message ?? b.Message;
115
+ }
116
+ catch {
117
+ return undefined;
118
+ }
119
+ });
120
+ return (text ? JSON.parse(text) : {});
121
+ }
122
+ }
123
+ //# sourceMappingURL=ses.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ses.js","sourceRoot":"","sources":["../../../../src/providers/email/ses.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAkB,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAE,WAAW,EAAuB,MAAM,eAAe,CAAC;AAiBjE,MAAM,mBAAmB,GAAG,0GAA0G,CAAC;AAEvI;;;;GAIG;AACH,MAAM,OAAO,WAAW;IAOO;IANpB,IAAI,GAAG,KAAK,CAAC;IACb,KAAK,GAAG,YAAY,CAAC;IACrB,eAAe,GAAG,KAAK,CAAC;IAChB,OAAO,CAAS;IAChB,SAAS,CAAY;IAEtC,YAA6B,IAAgB;QAAhB,SAAI,GAAJ,IAAI,CAAY;QAC3C,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,WAAW,CAAC,IAAI,IAAI,CAAC,MAAM,yCAAyC,CAAC,CAAC;QAClI,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,iBAAiB,IAAI,CAAC,MAAM,gBAAgB,CAAC;QAC5E,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC;IACvC,CAAC;IAED,sBAAsB,CAAC,MAAc;QACnC,OAAO,SAAS,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,MAAc;QAC7B,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QACnC,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;QACvE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;gBAAE,OAAO,IAAI,CAAC;YAC/D,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,iFAAiF;IACjF,KAAK,CAAC,YAAY,CAAC,MAAc,EAAE,OAA+B,EAAE;QAClE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAC/C,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAC9B,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QACnC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,sBAAsB,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5E,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,wBAAwB,kBAAkB,CAAC,IAAI,CAAC,YAAY,EAAE;YACtF,cAAc,EAAE,GAAG,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,IAAI,EAAE;YACrD,4FAA4F;YAC5F,mBAAmB,EAAE,mBAAmB;SACzC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,EAAU;QACxB,OAAO,IAAI,CAAC,gBAAgB,CAAC,EAAE,EAAE,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;IACnE,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,EAAU;QAC3B,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,wBAAwB,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACjF,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,EAAU;QAC7B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACzC,MAAM,UAAU,GAAG,QAAQ,CAAC,cAAc,EAAE,MAAM,IAAI,aAAa,CAAC;QACpE,MAAM,IAAI,GAAG,UAAU,KAAK,SAAS,CAAC;QACtC,MAAM,OAAO,GAAG,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,gBAAgB,UAAU,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9I,MAAM,QAAQ,GAAG,QAAQ,CAAC,kBAAkB,CAAC;QAC7C,IAAI,QAAQ,EAAE,cAAc,EAAE,CAAC;YAC7B,MAAM,EAAE,GAAG,QAAQ,CAAC,oBAAoB,KAAK,SAAS,CAAC;YACvD,MAAM,MAAM,GAAG,CAAC,QAAQ,CAAC,oBAAoB,IAAI,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAC7F,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,qBAAqB,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7G,CAAC;QACD,wGAAwG;QACxG,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAC,IAAI,IAAI,EAAE,OAAO,EAAE,CAAC;IAChF,CAAC;IAED,cAAc;QACZ,MAAM,IAAI,WAAW,CAAC,mBAAmB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,kBAAkB;QACtB,MAAM,IAAI,WAAW,CAAC,mBAAmB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,kBAAkB,KAAmB,CAAC;IAEpC,QAAQ,CAAC,IAAY;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAc,KAAK,EAAE,wBAAwB,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC9F,CAAC;IAEO,gBAAgB,CAAC,MAAc,EAAE,QAAqB,EAAE,OAAgB;QAC9E,MAAM,OAAO,GAAgB,CAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YACtF,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,GAAG,KAAK,eAAe,MAAM,EAAE;YACrC,KAAK,EAAE,GAAG,KAAK,qBAAqB;YACpC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,EAAE;SACxB,CAAC,CAAC,CAAC;QACJ,MAAM,QAAQ,GAAG,QAAQ,CAAC,kBAAkB,EAAE,cAAc,CAAC;QAC7D,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,IAAI,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;YACrC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,iBAAiB,IAAI,CAAC,IAAI,CAAC,MAAM,gBAAgB,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;YAC1I,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,KAAK;gBACX,IAAI,EAAE,IAAI;gBACV,KAAK,EAAE,mCAAmC;gBAC1C,OAAO,EAAE,eAAe;gBACxB,IAAI,EAAE,sHAAsH;aAC7H,CAAC,CAAC;QACL,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IACrG,CAAC;IAEO,KAAK,CAAC,OAAO,CAAI,MAAc,EAAE,IAAY,EAAE,IAAc;QACnE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QAChC,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACtE,MAAM,IAAI,GAAG,MAAM,WAAW,CAC5B,IAAI,CAAC,SAAS,EACd,YAAY,EACZ,GAAG,EACH,GAAG,EAAE,CAAC,CAAC;YACL,MAAM;YACN,IAAI,EAAE,OAAO;YACb,OAAO,EAAE,WAAW,CAClB,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAC9F,IAAI,CAAC,IAAI,CAAC,WAAW,EACrB,IAAI,CAAC,IAAI,CAAC,MAAM,EAChB,KAAK,CACN;SACF,CAAC,EACF,CAAC,CAAC,EAAE,EAAE;YACJ,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAA2C,CAAC;gBAClE,OAAO,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,SAAS,CAAC;YACnB,CAAC;QACH,CAAC,CACF,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAM,CAAC;IAC7C,CAAC;CACF"}
@@ -0,0 +1,14 @@
1
+ import type { DnsProvider, DnsRecord } from '../types.js';
2
+ import { type AwsCredentials } from './dns/aws.js';
3
+ export declare const DNS_PROVIDERS: readonly ["cloudflare", "route53", "manual"];
4
+ export type DnsProviderName = (typeof DNS_PROVIDERS)[number];
5
+ export interface DnsProviderConfig {
6
+ cloudflareApiToken?: string;
7
+ /** Explicit AWS keys; otherwise environment or shared credentials file. */
8
+ aws?: Partial<AwsCredentials>;
9
+ awsProfile?: string;
10
+ route53HostedZoneId?: string;
11
+ onManualRecords?: (records: DnsRecord[]) => Promise<void>;
12
+ env?: NodeJS.ProcessEnv;
13
+ }
14
+ export declare function createDnsProvider(name: string, config?: DnsProviderConfig): DnsProvider;
@@ -0,0 +1,32 @@
1
+ import { ConfigError } from '../errors.js';
2
+ import { loadAwsCredentials } from './dns/aws.js';
3
+ import { CloudflareDns } from './dns/cloudflare.js';
4
+ import { ManualDns } from './dns/manual.js';
5
+ import { Route53Dns } from './dns/route53.js';
6
+ export const DNS_PROVIDERS = ['cloudflare', 'route53', 'manual'];
7
+ export function createDnsProvider(name, config = {}) {
8
+ const env = config.env ?? process.env;
9
+ switch (name) {
10
+ case 'cloudflare': {
11
+ const apiToken = config.cloudflareApiToken || env.CLOUDFLARE_API_TOKEN;
12
+ if (!apiToken)
13
+ throw new ConfigError('Cloudflare needs an API token (CLOUDFLARE_API_TOKEN).');
14
+ return new CloudflareDns({ apiToken });
15
+ }
16
+ case 'route53': {
17
+ const { accessKeyId, secretAccessKey, sessionToken } = config.aws ?? {};
18
+ if (Boolean(accessKeyId) !== Boolean(secretAccessKey)) {
19
+ throw new ConfigError('Route53 needs both an AWS access key id and a secret access key.');
20
+ }
21
+ const credentials = accessKeyId && secretAccessKey
22
+ ? { accessKeyId, secretAccessKey, ...(sessionToken ? { sessionToken } : {}) }
23
+ : () => loadAwsCredentials({ profile: config.awsProfile || undefined, env });
24
+ return new Route53Dns({ credentials, hostedZoneId: config.route53HostedZoneId || undefined });
25
+ }
26
+ case 'manual':
27
+ return new ManualDns(config.onManualRecords ?? (async () => { }));
28
+ default:
29
+ throw new ConfigError(`Unknown DNS provider "${name}". Use one of: ${DNS_PROVIDERS.join(', ')}.`);
30
+ }
31
+ }
32
+ //# sourceMappingURL=factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/providers/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,EAAE,kBAAkB,EAAuB,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,YAAY,EAAE,SAAS,EAAE,QAAQ,CAAU,CAAC;AAa1E,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,SAA4B,EAAE;IAC5E,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACtC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,MAAM,QAAQ,GAAG,MAAM,CAAC,kBAAkB,IAAI,GAAG,CAAC,oBAAoB,CAAC;YACvE,IAAI,CAAC,QAAQ;gBAAE,MAAM,IAAI,WAAW,CAAC,uDAAuD,CAAC,CAAC;YAC9F,OAAO,IAAI,aAAa,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;QACzC,CAAC;QACD,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;YACxE,IAAI,OAAO,CAAC,WAAW,CAAC,KAAK,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC;gBACtD,MAAM,IAAI,WAAW,CAAC,kEAAkE,CAAC,CAAC;YAC5F,CAAC;YACD,MAAM,WAAW,GACf,WAAW,IAAI,eAAe;gBAC5B,CAAC,CAAC,EAAE,WAAW,EAAE,eAAe,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;gBAC7E,CAAC,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,UAAU,IAAI,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;YACjF,OAAO,IAAI,UAAU,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,CAAC,mBAAmB,IAAI,SAAS,EAAE,CAAC,CAAC;QAChG,CAAC;QACD,KAAK,QAAQ;YACX,OAAO,IAAI,SAAS,CAAC,MAAM,CAAC,eAAe,IAAI,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC,CAAC,CAAC;QACnE;YACE,MAAM,IAAI,WAAW,CAAC,yBAAyB,IAAI,kBAAkB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtG,CAAC;AACH,CAAC"}
@@ -0,0 +1,35 @@
1
+ import type { DnsRecord, PlannedChange, RecordType } from './types.js';
2
+ export declare function isRecordType(type: string): type is RecordType;
3
+ /** A normalized hostname with at least two labels (IDNs must be punycode). */
4
+ export declare function isValidDomain(name: string): boolean;
5
+ export declare function normalizeName(name: string): string;
6
+ /** Hostnames compare case-insensitively without trailing dots; TXT values compare with quoting and chunking removed. */
7
+ export declare function normalizeValue(type: RecordType, value: string): string;
8
+ export declare function sameValue(type: RecordType, a: string, b: string): boolean;
9
+ /** A record already present at the DNS host, in provider-neutral form. */
10
+ export interface ExistingRecord {
11
+ id: string;
12
+ type: string;
13
+ value: string;
14
+ priority?: number;
15
+ proxied?: boolean;
16
+ }
17
+ export declare function isSpf(value: string): boolean;
18
+ /**
19
+ * Adds the mechanisms of `desired` (e.g. "include:mailgun.org") to an existing
20
+ * SPF record, before its `all`/`redirect` term. Returns null when the existing
21
+ * record already has them all. A name must have exactly one SPF record, so
22
+ * providers' SPF records are merged in rather than added next to the old one.
23
+ */
24
+ export declare function mergeSpf(existing: string, desired: string): {
25
+ value: string;
26
+ added: string[];
27
+ } | null;
28
+ /**
29
+ * Decides what to do with `record` given the records already at its name.
30
+ * Never replaces a record pointing elsewhere unless `overwrite` is set, and never
31
+ * deletes records of other types. `desired` is every record being planned, so
32
+ * a provider's own multi-value sets (e.g. two MX hosts) don't conflict with each other.
33
+ */
34
+ export declare function decideChange(record: DnsRecord, existing: ExistingRecord[], overwrite?: boolean, desired?: DnsRecord[]): PlannedChange;
35
+ export declare function describeRecord(record: DnsRecord): string;