@aglyn/shared-util-email 1.0.0-beta.143
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/LICENSE +201 -0
- package/README.md +104 -0
- package/package.json +37 -0
- package/src/index.d.ts +37 -0
- package/src/index.js +46 -0
- package/src/index.js.map +1 -0
- package/src/lib/email-delivery-events.d.ts +198 -0
- package/src/lib/email-delivery-events.js +310 -0
- package/src/lib/email-delivery-events.js.map +1 -0
- package/src/lib/email-health.d.ts +154 -0
- package/src/lib/email-health.js +264 -0
- package/src/lib/email-health.js.map +1 -0
- package/src/lib/email-media-src.d.ts +33 -0
- package/src/lib/email-media-src.js +113 -0
- package/src/lib/email-media-src.js.map +1 -0
- package/src/lib/email-merge.d.ts +31 -0
- package/src/lib/email-merge.js +36 -0
- package/src/lib/email-merge.js.map +1 -0
- package/src/lib/email-render.d.ts +114 -0
- package/src/lib/email-render.js +293 -0
- package/src/lib/email-render.js.map +1 -0
- package/src/lib/email-revenue-window.d.ts +55 -0
- package/src/lib/email-revenue-window.js +58 -0
- package/src/lib/email-revenue-window.js.map +1 -0
- package/src/lib/host-email-render.d.ts +99 -0
- package/src/lib/host-email-render.js +126 -0
- package/src/lib/host-email-render.js.map +1 -0
- package/src/lib/host-sender.d.ts +86 -0
- package/src/lib/host-sender.js +131 -0
- package/src/lib/host-sender.js.map +1 -0
- package/src/lib/marketing-send.d.ts +466 -0
- package/src/lib/marketing-send.js +459 -0
- package/src/lib/marketing-send.js.map +1 -0
- package/src/lib/platform-sending-domain.d.ts +362 -0
- package/src/lib/platform-sending-domain.js +697 -0
- package/src/lib/platform-sending-domain.js.map +1 -0
- package/src/lib/received-email.d.ts +86 -0
- package/src/lib/received-email.js +124 -0
- package/src/lib/received-email.js.map +1 -0
- package/src/lib/send-ceilings.d.ts +394 -0
- package/src/lib/send-ceilings.js +341 -0
- package/src/lib/send-ceilings.js.map +1 -0
- package/src/lib/send-email.d.ts +385 -0
- package/src/lib/send-email.js +586 -0
- package/src/lib/send-email.js.map +1 -0
- package/src/lib/send-rate.d.ts +298 -0
- package/src/lib/send-rate.js +310 -0
- package/src/lib/send-rate.js.map +1 -0
- package/src/lib/sender-reputation.d.ts +357 -0
- package/src/lib/sender-reputation.js +392 -0
- package/src/lib/sender-reputation.js.map +1 -0
- package/src/lib/sending-domain.d.ts +744 -0
- package/src/lib/sending-domain.js +798 -0
- package/src/lib/sending-domain.js.map +1 -0
- package/src/lib/sending-mailbox.d.ts +132 -0
- package/src/lib/sending-mailbox.js +192 -0
- package/src/lib/sending-mailbox.js.map +1 -0
- package/src/lib/stored-email-nodes.d.ts +38 -0
- package/src/lib/stored-email-nodes.js +78 -0
- package/src/lib/stored-email-nodes.js.map +1 -0
- package/src/lib/svix-signature.d.ts +38 -0
- package/src/lib/svix-signature.js +55 -0
- package/src/lib/svix-signature.js.map +1 -0
- package/src/lib/system-email-catalog.d.ts +170 -0
- package/src/lib/system-email-catalog.js +828 -0
- package/src/lib/system-email-catalog.js.map +1 -0
- package/src/lib/tenant-email-catalog.d.ts +78 -0
- package/src/lib/tenant-email-catalog.js +452 -0
- package/src/lib/tenant-email-catalog.js.map +1 -0
- package/src/lib/text-email-html.d.ts +32 -0
- package/src/lib/text-email-html.js +119 -0
- package/src/lib/text-email-html.js.map +1 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The apex assigned site subdomains hang off — the WEB namespace.
|
|
19
|
+
*
|
|
20
|
+
* The same variable and the same default as `TENANT_APEX` in `@aglyn/aglyn`'s
|
|
21
|
+
* `host-naming.ts`, read again here rather than imported because
|
|
22
|
+
* `@aglyn/shared-util-email` is `scope:shared` and may not depend on the
|
|
23
|
+
* application library. Reading the environment in this module is the idiom
|
|
24
|
+
* `sendingSpfInclude` and `sendingReturnPathHost` already establish one file
|
|
25
|
+
* over: the mail policy layer owns its own configuration reads.
|
|
26
|
+
*
|
|
27
|
+
* `||` not `??`, matching every other env read in this repo — an empty string
|
|
28
|
+
* is a variable somebody set to nothing, not a configured value.
|
|
29
|
+
*/
|
|
30
|
+
export declare function tenantWebApex(): string;
|
|
31
|
+
/**
|
|
32
|
+
* The apex tenant sending domains hang off — the MAIL namespace.
|
|
33
|
+
*
|
|
34
|
+
* `mail.{web apex}` by default, so an operator who sets only
|
|
35
|
+
* `NEXT_PUBLIC_TENANT_DOMAIN` gets a mail namespace inside their own zone
|
|
36
|
+
* rather than inside ours. Overridable on its own for the operator who wants
|
|
37
|
+
* mail in a different zone entirely — a separate registrable domain is the
|
|
38
|
+
* strongest form of the reputation split this feature is about, and there is
|
|
39
|
+
* no reason for the software to be the thing preventing it.
|
|
40
|
+
*
|
|
41
|
+
* Never equal to the web apex. If an operator sets them to the same value the
|
|
42
|
+
* override is ignored: a mail namespace that IS the web namespace re-creates
|
|
43
|
+
* exactly the collision this module chose its shape to avoid, and honoring
|
|
44
|
+
* that setting would be honoring a request to be unsafe.
|
|
45
|
+
*/
|
|
46
|
+
export declare function platformSendingApex(): string;
|
|
47
|
+
/**
|
|
48
|
+
* The mailbox the shared tenant identity sends as.
|
|
49
|
+
*
|
|
50
|
+
* `notifications` rather than `hello`, which is what a site's own domain
|
|
51
|
+
* defaults to. The two are different promises: `hello@northwind.mail.aglyn.app`
|
|
52
|
+
* is a site inviting a reply, and this one is a platform address carrying mail
|
|
53
|
+
* for whichever site the display name in front of it names. Calling it `hello`
|
|
54
|
+
* would invite replies to a mailbox belonging to no site.
|
|
55
|
+
*/
|
|
56
|
+
export declare const SHARED_TENANT_LOCAL_PART = "notifications";
|
|
57
|
+
/**
|
|
58
|
+
* How many shared sending domains exist, absent configuration.
|
|
59
|
+
*
|
|
60
|
+
* FOUR, and the number is a blast-radius decision rather than a capacity one.
|
|
61
|
+
* The pool is FIXED-SIZE — it does not grow with hosts — so its whole cost is
|
|
62
|
+
* four provider domain objects and twelve records in our own zone, whether the
|
|
63
|
+
* deployment has twelve sites or a hundred thousand. That is the property that
|
|
64
|
+
* makes this design survive a scale a per-host domain does not.
|
|
65
|
+
*
|
|
66
|
+
* One would be simpler and is wrong: every site without a domain of its own
|
|
67
|
+
* would share a single reputation, so one compromised site blasting "receipts"
|
|
68
|
+
* takes every other site's password resets down with it. Four caps that at a
|
|
69
|
+
* quarter, for three more domain objects.
|
|
70
|
+
*
|
|
71
|
+
* Not larger, because each member is a reputation that has to be EARNED — one
|
|
72
|
+
* warm domain sending steadily is worth more than eight cold ones — and
|
|
73
|
+
* because each is a provider slot on an account whose allowance is ten on the
|
|
74
|
+
* plan this deployment pays for. Raising it is a real decision with a real
|
|
75
|
+
* cost, so it is a configuration value rather than a constant somebody bumps.
|
|
76
|
+
*/
|
|
77
|
+
export declare const DEFAULT_SHARED_POOL_SIZE = 4;
|
|
78
|
+
/** How many members this deployment's pool has. */
|
|
79
|
+
export declare function sharedPoolSize(): number;
|
|
80
|
+
/**
|
|
81
|
+
* Whether a label names a pool member.
|
|
82
|
+
*
|
|
83
|
+
* By PATTERN rather than by list, so raising the pool size can never hand a
|
|
84
|
+
* tenant a label the pool is about to want. `PLATFORM_MAIL_RESERVED_LABELS`
|
|
85
|
+
* covers the fixed names; this covers a family whose size is configurable, and
|
|
86
|
+
* a blocklist that had to be edited in step with an environment variable is a
|
|
87
|
+
* blocklist that will be forgotten.
|
|
88
|
+
*/
|
|
89
|
+
export declare function isSharedPoolLabel(label: string): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* The pool, in order: `shared1.mail.aglyn.app`, `shared2.…`, and so on.
|
|
92
|
+
*
|
|
93
|
+
* The names say what they are on purpose. A recipient who looks sees an address
|
|
94
|
+
* that is plainly platform infrastructure rather than one pretending to be the
|
|
95
|
+
* merchant's own — which is what the console tells the merchant is happening,
|
|
96
|
+
* and a `From:` that oversold the arrangement would be the one surface
|
|
97
|
+
* contradicting the disclosure.
|
|
98
|
+
*/
|
|
99
|
+
export declare function sharedSendingPool(apex?: string, size?: number): string[];
|
|
100
|
+
/**
|
|
101
|
+
* Which pool member one host sends on. Deterministic, and stored nowhere.
|
|
102
|
+
*
|
|
103
|
+
* ## Rendezvous hashing, and why not `hash % size`
|
|
104
|
+
*
|
|
105
|
+
* The requirement is that a host's sending identity does not move underneath
|
|
106
|
+
* it. Reputation is built by sending steadily from one name, so a reassignment
|
|
107
|
+
* is not a rebalance — it is a reset, and it throws away every day of standing
|
|
108
|
+
* the old name had earned.
|
|
109
|
+
*
|
|
110
|
+
* `hash(hostId) % size` satisfies that only while `size` never changes. Growing
|
|
111
|
+
* a pool from four to eight remaps roughly HALF of all hosts, silently moving
|
|
112
|
+
* half the platform's transactional mail onto cold domains. The operator's
|
|
113
|
+
* reason for growing the pool is usually that one member is in trouble, which
|
|
114
|
+
* makes the moment they act the worst possible moment to shuffle everybody.
|
|
115
|
+
*
|
|
116
|
+
* Rendezvous (highest-random-weight) hashing scores the host against EVERY
|
|
117
|
+
* member and takes the winner. Adding a member moves only the hosts that now
|
|
118
|
+
* score highest on the new one — about 1/size of them — and removing a member
|
|
119
|
+
* moves only that member's own hosts. Nothing else observes the change.
|
|
120
|
+
*
|
|
121
|
+
* That property is why this is worth ten lines instead of a modulo, and it is
|
|
122
|
+
* also why the assignment needs no stored field: there is nothing to pin,
|
|
123
|
+
* because the function does not change its mind. A pinned column would be one
|
|
124
|
+
* more write per host on a path whose whole point is costing nothing per host.
|
|
125
|
+
*
|
|
126
|
+
* Ties break on the domain name so the answer is total rather than dependent on
|
|
127
|
+
* iteration order. A 32-bit collision across a four-member pool is vanishingly
|
|
128
|
+
* unlikely and would still have to resolve to something.
|
|
129
|
+
*/
|
|
130
|
+
export declare function sharedSendingDomainFor(hostId: string | null | undefined, apex?: string, size?: number): string;
|
|
131
|
+
/**
|
|
132
|
+
* The address one host sends TRANSACTIONAL mail on when it has no domain of
|
|
133
|
+
* its own.
|
|
134
|
+
*
|
|
135
|
+
* `''` when the deployment has no usable pool, or when the caller has no host.
|
|
136
|
+
* The caller must treat that as a REFUSAL rather than substituting anything: a
|
|
137
|
+
* caller with no `hostId` does not know which site it is sending for, and the
|
|
138
|
+
* honest answer to that is not "some pool member".
|
|
139
|
+
*
|
|
140
|
+
* ## The address always sits ON a pool member, never under one
|
|
141
|
+
*
|
|
142
|
+
* Under `adkim=s` the `From:` domain and the DKIM `d=` must be the same name,
|
|
143
|
+
* exactly. Each pool member is its own provider domain object with its own key
|
|
144
|
+
* signing for itself, so an address on the member aligns. An address BENEATH a
|
|
145
|
+
* member — a per-host `From:` over a shared key — does not, and the published
|
|
146
|
+
* record's `aspf=r` would let it scrape a DMARC pass off SPF alone until the
|
|
147
|
+
* first recipient forwarded it. There is deliberately no configuration that can
|
|
148
|
+
* produce that shape: the local part is the only part an operator may set.
|
|
149
|
+
*/
|
|
150
|
+
export declare function sharedTenantSendingFrom(hostId: string | null | undefined, apex?: string, size?: number): string;
|
|
151
|
+
/**
|
|
152
|
+
* Whether a domain is one of this deployment's pool members.
|
|
153
|
+
*
|
|
154
|
+
* Used where a surface has a domain and needs to know whether its reputation is
|
|
155
|
+
* pooled — so the disclosure is driven by the same function the send path
|
|
156
|
+
* resolves through, rather than by a second opinion assembled at the surface.
|
|
157
|
+
*/
|
|
158
|
+
export declare function isSharedSendingDomain(domain: string | null | undefined, apex?: string, size?: number): boolean;
|
|
159
|
+
/**
|
|
160
|
+
* ⛔ WHY A DOMAIN MAY NOT BE TORN DOWN, or `null` when it may.
|
|
161
|
+
*
|
|
162
|
+
* Every path that destroys a sending domain — the site delete, the erasure,
|
|
163
|
+
* the orphan reaper — asks this ONE function first, and it lives here because
|
|
164
|
+
* this module is the only place that knows what the pool is. A guard held
|
|
165
|
+
* anywhere else is a guard the next teardown path forgets to consult.
|
|
166
|
+
*
|
|
167
|
+
* `shared-pool` is the answer that matters, and the reason it has to be
|
|
168
|
+
* explicit rather than emergent. A pool member is owned by NO HOST: it is
|
|
169
|
+
* platform infrastructure that every site without a domain of its own sends
|
|
170
|
+
* on, and {@link sharedSendingDomainFor} assigns hosts to it by hash rather
|
|
171
|
+
* than by a stored pointer. So any reaper keyed on "nothing points at this"
|
|
172
|
+
* describes a pool member perfectly, and releasing one takes a quarter of the
|
|
173
|
+
* platform's transactional mail — receipts, password resets, booking
|
|
174
|
+
* confirmations — down with it. Silently: nothing raises an error when a
|
|
175
|
+
* domain merely stops being verified, the sends just start refusing.
|
|
176
|
+
*
|
|
177
|
+
* It is asked of the LABEL as well as of the domain, in three ways, because
|
|
178
|
+
* pool membership answered from the CURRENT pool alone is not enough:
|
|
179
|
+
*
|
|
180
|
+
* - the domain is in the pool this deployment builds today;
|
|
181
|
+
* - the caller's own label is a pool label. {@link platformSendingLabel}
|
|
182
|
+
* refuses to derive a label for a reserved name, so a pool member's domain
|
|
183
|
+
* hands back an empty label, and a caller that then fell back to a label
|
|
184
|
+
* it was handed separately would walk straight past the derivation that
|
|
185
|
+
* was protecting it;
|
|
186
|
+
* - the label UNDER the apex is a pool label, whatever the pool size says.
|
|
187
|
+
* A deployment shrunk from eight members to four still holds `shared5`
|
|
188
|
+
* through `shared8` at the provider, and they are still infrastructure —
|
|
189
|
+
* a membership test against the current pool would hand all four to the
|
|
190
|
+
* reaper on the day somebody lowered the number.
|
|
191
|
+
*
|
|
192
|
+
* `not-our-zone` is the other refusal and a different kind: a customer's own
|
|
193
|
+
* verified domain, which this deployment never provisioned, holds no provider
|
|
194
|
+
* slot for, and must never write DNS into.
|
|
195
|
+
*/
|
|
196
|
+
export declare function sendingDomainTeardownRefusal(domain: string | null | undefined, label?: string | null | undefined, apex?: string, size?: number): 'shared-pool' | 'not-our-zone' | null;
|
|
197
|
+
/**
|
|
198
|
+
* Labels that would name mail infrastructure inside the mail apex.
|
|
199
|
+
*
|
|
200
|
+
* The question this set answers is narrow: which labels, taken by a tenant,
|
|
201
|
+
* would position that tenant to publish records under a name some other part
|
|
202
|
+
* of the system relies on?
|
|
203
|
+
*
|
|
204
|
+
* `send` is the one that matters and the one a pattern check misses.
|
|
205
|
+
* {@link SENDING_SUBDOMAIN} is `send`, so a tenant holding that label holds
|
|
206
|
+
* `send.mail.aglyn.app` — the return-path name of the mail apex itself, and a
|
|
207
|
+
* bounce-routing surface rather than anything a site needs.
|
|
208
|
+
*
|
|
209
|
+
* Names containing a dot or an underscore are NOT in here and do not need to
|
|
210
|
+
* be: {@link LABEL_PATTERN} admits neither character, so `_dmarc` and
|
|
211
|
+
* `resend._domainkey` are unreachable by construction rather than by
|
|
212
|
+
* blocklist. A blocklist entry for a string the grammar cannot produce reads
|
|
213
|
+
* as though the grammar allows it.
|
|
214
|
+
*/
|
|
215
|
+
export declare const PLATFORM_MAIL_RESERVED_LABELS: readonly string[];
|
|
216
|
+
/**
|
|
217
|
+
* Whether a label is one a TENANT may not take inside the mail apex.
|
|
218
|
+
*
|
|
219
|
+
* Two sources, and the second is why this is a function rather than a `Set`
|
|
220
|
+
* lookup: the fixed infrastructure names above, plus the pool family, whose
|
|
221
|
+
* size is a configuration value. A pool grown from four to eight must not be
|
|
222
|
+
* able to want a label a site was handed last week, and a blocklist that had to
|
|
223
|
+
* be edited in step with an environment variable is one that gets forgotten.
|
|
224
|
+
*
|
|
225
|
+
* This is the TENANT policy, not the name grammar. The pool's own builder
|
|
226
|
+
* deliberately does not consult it — `shared3` is reserved precisely so that
|
|
227
|
+
* the pool can have it — which is why {@link mailDomainWithinApex} exists as
|
|
228
|
+
* the shape check underneath both.
|
|
229
|
+
*/
|
|
230
|
+
export declare function isReservedMailLabel(label: string): boolean;
|
|
231
|
+
/**
|
|
232
|
+
* Whether a label is shaped like one, ignoring whether it is reserved or
|
|
233
|
+
* taken. Exported so the claim path can tell "not a label" (never fixable by
|
|
234
|
+
* suffixing) from "taken" (fixable).
|
|
235
|
+
*/
|
|
236
|
+
export declare function isWellFormedMailLabel(label: string): boolean;
|
|
237
|
+
/**
|
|
238
|
+
* The sending domain for one PINNED label, or `''` when the label must not
|
|
239
|
+
* become one.
|
|
240
|
+
*
|
|
241
|
+
* The ONLY place a label is turned into a mail domain. Everything downstream —
|
|
242
|
+
* the Resend domain object, the DNS records written into the zone, the `From:`
|
|
243
|
+
* header — is built from what this returns, so a name this refuses cannot
|
|
244
|
+
* reach any of them.
|
|
245
|
+
*
|
|
246
|
+
* It takes the PINNED label, never a host's live subdomain, and that is the
|
|
247
|
+
* rename guarantee expressed as a type: there is no argument here that changes
|
|
248
|
+
* when a site is renamed. {@link mailLabelCandidate} is the separate, one-time
|
|
249
|
+
* step that proposes a label from a slug.
|
|
250
|
+
*
|
|
251
|
+
* `''` rather than a throw: every caller's correct response to an unusable
|
|
252
|
+
* label is the same one — provision nothing — and a falsy return is what this
|
|
253
|
+
* library's other normalizers already give back for input they will not
|
|
254
|
+
* accept.
|
|
255
|
+
*/
|
|
256
|
+
export declare function platformSendingDomainFor(label: string | null | undefined, apex?: string): string;
|
|
257
|
+
/**
|
|
258
|
+
* Propose a mail label from a site's subdomain. Called ONCE, at provisioning.
|
|
259
|
+
*
|
|
260
|
+
* Its output is pinned and then never recomputed, so this function's result
|
|
261
|
+
* changing later — because the site was renamed, or because a label joined the
|
|
262
|
+
* reserved set — cannot move an existing site's mail. That is deliberate: the
|
|
263
|
+
* value of a sending domain is its age, and a name that can be recomputed is a
|
|
264
|
+
* name that can be lost.
|
|
265
|
+
*
|
|
266
|
+
* `attempt` de-collides. A label already claimed by another site yields
|
|
267
|
+
* `{label}-2`, `-3`, and so on, which is the same shape `suggestSubdomains`
|
|
268
|
+
* uses for a taken web slug — a merchant meeting `northwind-coffee-2` has met
|
|
269
|
+
* that pattern before. Truncated so the suffix never pushes the label past a
|
|
270
|
+
* legal length, because a name that is silently cut at 63 octets is a name
|
|
271
|
+
* whose DNS records do not match its domain.
|
|
272
|
+
*
|
|
273
|
+
* Returns `''` when nothing usable can be built, and the caller must treat
|
|
274
|
+
* that as "this site cannot be provisioned" rather than substituting anything.
|
|
275
|
+
*/
|
|
276
|
+
export declare function mailLabelCandidate(subdomain: string | null | undefined, attempt?: number): string;
|
|
277
|
+
/**
|
|
278
|
+
* Whether a domain is one this deployment provisions and owns, as against a
|
|
279
|
+
* customer's own name.
|
|
280
|
+
*
|
|
281
|
+
* The distinction decides who does the DNS work — a domain inside our apex is
|
|
282
|
+
* written by API and needs nothing from the tenant — and what a delete may
|
|
283
|
+
* remove. A record set inside our zone is ours to clean up; a customer's zone
|
|
284
|
+
* is one we must never write to.
|
|
285
|
+
*
|
|
286
|
+
* The bare apex is deliberately NOT one of these.
|
|
287
|
+
*/
|
|
288
|
+
export declare function isPlatformSendingDomain(domain: string | null | undefined, apex?: string): boolean;
|
|
289
|
+
/**
|
|
290
|
+
* The pinned label a platform sending domain belongs to, or `''`.
|
|
291
|
+
*
|
|
292
|
+
* The inverse of {@link platformSendingDomainFor}, and it re-runs the same
|
|
293
|
+
* refusals rather than merely stripping the suffix. A stored domain is data
|
|
294
|
+
* like any other — it can predate a reserved-label entry or have been written
|
|
295
|
+
* by hand — and a cleanup path that trusted it would derive a label from a
|
|
296
|
+
* name the current rules would never have issued.
|
|
297
|
+
*/
|
|
298
|
+
export declare function platformSendingLabel(domain: string | null | undefined, apex?: string): string;
|
|
299
|
+
/** One record to write into the zone, as a DNS API addresses it. */
|
|
300
|
+
export interface PlatformZoneRecord {
|
|
301
|
+
type: 'TXT' | 'MX' | 'CNAME' | 'CAA';
|
|
302
|
+
/**
|
|
303
|
+
* Name RELATIVE to the ZONE — `send.acme.mail`, not
|
|
304
|
+
* `send.acme.mail.aglyn.app`.
|
|
305
|
+
*
|
|
306
|
+
* Zone APIs address records by their name within the zone, and a name
|
|
307
|
+
* carrying the zone would be created at `…aglyn.app.aglyn.app`. The
|
|
308
|
+
* fully-qualified form is what `sendingDnsRecords` produces for a customer
|
|
309
|
+
* to paste at their own registrar; this is the same record addressed the way
|
|
310
|
+
* an API to our own zone addresses it.
|
|
311
|
+
*
|
|
312
|
+
* The zone is the REGISTRABLE domain (`aglyn.app`), not the mail apex —
|
|
313
|
+
* `mail.aglyn.app` is a name inside that zone, not a zone of its own, so its
|
|
314
|
+
* label is part of every record name here.
|
|
315
|
+
*/
|
|
316
|
+
name: string;
|
|
317
|
+
value: string;
|
|
318
|
+
/** `MX` only. */
|
|
319
|
+
priority?: number;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Turn the records a sending domain requires into records addressed within the
|
|
323
|
+
* zone.
|
|
324
|
+
*
|
|
325
|
+
* Derived from {@link sendingDnsRecords}' output rather than rebuilt, for the
|
|
326
|
+
* reason that function exists at all: the records we WRITE must be the records
|
|
327
|
+
* the verifier LOOKS FOR. Two generators is how a wizard comes to print one
|
|
328
|
+
* target while the check reads another — a check that cannot fail, then a
|
|
329
|
+
* check that cannot pass.
|
|
330
|
+
*
|
|
331
|
+
* Records with no value are dropped. A domain with no issued DKIM key yields
|
|
332
|
+
* no DKIM record, and writing an empty TXT would publish a record that says
|
|
333
|
+
* nothing while looking published.
|
|
334
|
+
*/
|
|
335
|
+
export declare function platformZoneRecords(records: readonly {
|
|
336
|
+
type: 'TXT' | 'MX' | 'CNAME' | 'CAA';
|
|
337
|
+
name: string;
|
|
338
|
+
value: string;
|
|
339
|
+
priority?: number;
|
|
340
|
+
}[], zone?: string): PlatformZoneRecord[];
|
|
341
|
+
/**
|
|
342
|
+
* Every name one sending domain owns inside the zone, for the cleanup path.
|
|
343
|
+
*
|
|
344
|
+
* Derived rather than written out at the call site, because the mail apex is a
|
|
345
|
+
* configuration value: a teardown that assembled `send.{label}.mail` by hand
|
|
346
|
+
* would delete nothing on a deployment whose mail apex is not `mail.…`, and
|
|
347
|
+
* would leave a live signing key in the zone for every site it "cleaned".
|
|
348
|
+
*
|
|
349
|
+
* The DKIM name needs the SELECTOR, which is the provider's choice and not
|
|
350
|
+
* ours — `sendingDkimSelector` proposes one and the provider overwrites it. A
|
|
351
|
+
* caller with no selector gets the other names and must treat the DKIM record
|
|
352
|
+
* as unhandled rather than guessing, since a guessed selector deletes nothing
|
|
353
|
+
* and reports success.
|
|
354
|
+
*/
|
|
355
|
+
export declare function platformZoneNamesFor(label: string, selector?: string | null, apex?: string, zone?: string): string[];
|
|
356
|
+
/**
|
|
357
|
+
* The return-path name for one pinned label, relative to the zone.
|
|
358
|
+
*
|
|
359
|
+
* Used where a caller needs only the bounce name — the record that has to keep
|
|
360
|
+
* resolving after a rename, because bounces arrive after the send.
|
|
361
|
+
*/
|
|
362
|
+
export declare function platformReturnPathName(label: string, apex?: string, zone?: string): string;
|