@aglyn/shared-util-email 1.0.0-beta.200 → 1.0.0-beta.202

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 (41) hide show
  1. package/package.json +3 -3
  2. package/src/index.d.ts +6 -0
  3. package/src/index.js +16 -0
  4. package/src/index.js.map +1 -1
  5. package/src/lib/email-deliverability.d.ts +200 -0
  6. package/src/lib/email-deliverability.js +461 -0
  7. package/src/lib/email-deliverability.js.map +1 -0
  8. package/src/lib/email-delivery-events.d.ts +16 -39
  9. package/src/lib/email-delivery-events.js +28 -12
  10. package/src/lib/email-delivery-events.js.map +1 -1
  11. package/src/lib/email-linkify.d.ts +24 -0
  12. package/src/lib/email-linkify.js +73 -0
  13. package/src/lib/email-linkify.js.map +1 -0
  14. package/src/lib/email-render.js +6 -1
  15. package/src/lib/email-render.js.map +1 -1
  16. package/src/lib/hosted-page-screen.d.ts +60 -0
  17. package/src/lib/hosted-page-screen.js +314 -0
  18. package/src/lib/hosted-page-screen.js.map +1 -0
  19. package/src/lib/mail-bounce.d.ts +42 -0
  20. package/src/lib/mail-bounce.js +108 -0
  21. package/src/lib/mail-bounce.js.map +1 -0
  22. package/src/lib/mail-gateway.d.ts +161 -0
  23. package/src/lib/mail-gateway.js +330 -0
  24. package/src/lib/mail-gateway.js.map +1 -0
  25. package/src/lib/outbound-phishing-screen.d.ts +248 -0
  26. package/src/lib/outbound-phishing-screen.js +769 -0
  27. package/src/lib/outbound-phishing-screen.js.map +1 -0
  28. package/src/lib/outbound-screen-gate.d.ts +95 -0
  29. package/src/lib/outbound-screen-gate.js +109 -0
  30. package/src/lib/outbound-screen-gate.js.map +1 -0
  31. package/src/lib/send-email.d.ts +79 -1
  32. package/src/lib/send-email.js +153 -10
  33. package/src/lib/send-email.js.map +1 -1
  34. package/src/lib/sending-domain.d.ts +22 -1
  35. package/src/lib/sending-domain.js +0 -95
  36. package/src/lib/sending-domain.js.map +1 -1
  37. package/src/lib/system-email-catalog.js +400 -0
  38. package/src/lib/system-email-catalog.js.map +1 -1
  39. package/src/lib/text-email-html.d.ts +38 -0
  40. package/src/lib/text-email-html.js +3 -44
  41. package/src/lib/text-email-html.js.map +1 -1
@@ -0,0 +1,248 @@
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
+ /** One brand the screen recognizes. */
18
+ export interface PhishingScreenBrand {
19
+ /** Stable id, as stored on a hold. */
20
+ id: string;
21
+ /** How staff read it. */
22
+ label: string;
23
+ /** Copy that names the brand — subject, body, sender. Case-insensitive. */
24
+ mention: RegExp;
25
+ /**
26
+ * Host labels that name the brand. Matched against each dot-separated
27
+ * label of a link's host, whole or hyphen-joined, after leetspeak is
28
+ * folded — never as a bare substring, which is how `amazonaws` or
29
+ * `bookingengine` would read as a brand.
30
+ */
31
+ hostTokens: readonly string[];
32
+ /**
33
+ * Registrable domains the brand genuinely links from. The FIRST is its
34
+ * primary domain, the one a kit embeds in a longer host.
35
+ */
36
+ officialDomains: readonly string[];
37
+ /**
38
+ * The brand's name is an ordinary word (`apple`, `amazon`), so a host
39
+ * label names the brand only when it IS the word — `apple.example.top` —
40
+ * and never hyphen-joined or leet-folded, which `apple-orchard-farm.com`
41
+ * would otherwise be.
42
+ */
43
+ wordToken?: boolean;
44
+ /**
45
+ * Whether `<brand>.<country code>` (and `<brand>.co.<cc>`) is the brand's
46
+ * too — true for the marketplaces that run a site per country.
47
+ */
48
+ countryDomains?: boolean;
49
+ /**
50
+ * Registrable domains the brand runs but where OTHER people's content lives —
51
+ * the platform's own tenant apex, where every site is `<name>.<apex>`. A
52
+ * host there never wears the brand as a disguise, so the lookalike rule
53
+ * skips it; but it is not the brand's own domain either, so a link to a
54
+ * neighbor's site still counts as "somewhere else" to the lure rules, and a
55
+ * workspace hosted there does not own the brand by being hosted there.
56
+ */
57
+ hostedDomains?: readonly string[];
58
+ }
59
+ export declare const PHISHING_SCREEN_BRANDS: readonly PhishingScreenBrand[];
60
+ /**
61
+ * Account-action and credential phrasing — the half of a lure that asks the
62
+ * reader to DO something with an account. Only ever counted beside a brand
63
+ * AND a foreign link; alone, several of these are ordinary transactional copy.
64
+ */
65
+ export declare const PHISHING_LURE_PATTERNS: readonly RegExp[];
66
+ /** A signal the screen found. */
67
+ export type PhishingScreenSignal = {
68
+ code: 'lookalike-link';
69
+ brand: string;
70
+ host: string;
71
+ } | {
72
+ code: 'brand-sender';
73
+ brand: string;
74
+ fromName: string;
75
+ } | {
76
+ code: 'brand-lure-link';
77
+ brand: string;
78
+ lure: string;
79
+ host: string;
80
+ }
81
+ /**
82
+ * A published page asks for a password, a card number or a one-time code
83
+ * in a field that is not one of the platform's own sign-in or checkout
84
+ * elements (`hosted-page-screen.ts`). Strong.
85
+ */
86
+ | {
87
+ code: 'credential-field';
88
+ field: 'password' | 'card' | 'otp';
89
+ label: string;
90
+ }
91
+ /**
92
+ * A published page names a brand that is not the workspace's beside a
93
+ * sign-in, verify or payment call to action (`hosted-page-screen.ts`).
94
+ * Soft.
95
+ */
96
+ | {
97
+ code: 'brand-action-page';
98
+ brand: string;
99
+ action: string;
100
+ };
101
+ export interface PhishingScreenInput {
102
+ subject?: string | null;
103
+ /** The display name in front of the sending address. */
104
+ fromName?: string | null;
105
+ /**
106
+ * The ADDRESS the message leaves from (AGL-3362) — a `From:` domain is a
107
+ * link the recipient never has to click, so its host is read by the
108
+ * lookalike rule exactly like a link's: mail from `support@paypa1.com` is
109
+ * the disguise whoever sends it. Not an own domain for the lure's "is this
110
+ * elsewhere" test either way; it only ever adds a lookalike signal.
111
+ */
112
+ fromAddress?: string | null;
113
+ replyTo?: string | readonly string[] | null;
114
+ preheader?: string | null;
115
+ /**
116
+ * Everything else the recipient reads or clicks: the body, the plain-text
117
+ * part, a design's nodes serialized — any string. Links are found by
118
+ * shape, so a JSON-encoded design is as readable here as HTML.
119
+ */
120
+ bodies?: readonly (string | null | undefined)[];
121
+ /**
122
+ * The workspace's own names — org, site, subdomain. A brand in one of them
123
+ * is the workspace's own, and `brand-sender` / `brand-lure-link` do not
124
+ * fire for it. The lookalike check does not consult names: a workspace
125
+ * NAMED "Poshmark" linking to `poshmark.id63835663.shop` is the incident.
126
+ */
127
+ ownNames?: readonly (string | null | undefined)[];
128
+ /**
129
+ * The workspace's own hosts — its subdomain on the tenant apex, custom
130
+ * domain, sending domain. A link to one of them, or below one, is never
131
+ * the "somewhere else" of a lure. Matched by host, never by registrable
132
+ * domain: every site's subdomain shares the tenant apex, and a link to a
133
+ * NEIGHBOR's site is somewhere else.
134
+ *
135
+ * Not an exemption from the lookalike check. A host shaped like
136
+ * `paypal-secure.com` is a lookalike whoever attached it, and a workspace
137
+ * that attached it has attached the disguise.
138
+ */
139
+ ownDomains?: readonly (string | null | undefined)[];
140
+ }
141
+ export interface PhishingScreenVerdict {
142
+ /** True when the message should be held for staff review. */
143
+ hold: boolean;
144
+ signals: PhishingScreenSignal[];
145
+ }
146
+ /** The registrable domain of a host: `a.b.evil.co.uk` → `evil.co.uk`. */
147
+ export declare function registrableDomain(host: string): string;
148
+ /** Every brand's real domains, for "is this link just the brand itself". */
149
+ declare function isAnyOfficialDomain(registrable: string): boolean;
150
+ /** The brand a host impersonates, or null. */
151
+ export declare function lookalikeBrandForHost(host: string): PhishingScreenBrand | null;
152
+ /** The hosts every link in the text points at, lowercased. */
153
+ export declare function linkHostsIn(text: string): string[];
154
+ /** Lowercased alphanumerics only, so "Wells-Fargo Advisors" contains `wellsfargo`. */
155
+ declare function squash(text: string): string;
156
+ /** Is this brand the workspace's own, by name or by a domain it holds? */
157
+ declare function isOwnBrand(brand: PhishingScreenBrand, ownNames: string, ownRegistrables: ReadonlySet<string>): boolean;
158
+ /** `squash`: lowercased alphanumerics, the form own-name matching reads. */
159
+ export declare const squashScreenText: typeof squash;
160
+ /** Is this brand the workspace's own, by name (squashed) or by a domain it holds? */
161
+ export declare const isWorkspaceOwnBrand: typeof isOwnBrand;
162
+ /** Is this registrable domain one of any listed brand's own? */
163
+ export declare const isAnyOfficialBrandDomain: typeof isAnyOfficialDomain;
164
+ /** Is this registrable domain a social, maps or review link merchants ordinarily carry? */
165
+ export declare function isCommonLinkDomain(registrable: string): boolean;
166
+ /**
167
+ * Screen one outbound message. See the module header for what holds and why.
168
+ */
169
+ export declare function screenOutboundEmail(input: PhishingScreenInput): PhishingScreenVerdict;
170
+ /** The brand's label for a stored id, for the staff surface. */
171
+ export declare function phishingScreenBrandLabel(id: string): string;
172
+ /** One line per signal, in words staff read on the review row. */
173
+ export declare function describePhishingScreenSignals(signals: readonly PhishingScreenSignal[]): string[];
174
+ /** A workspace younger than this many days has the soft rules applied. */
175
+ export declare const OUTBOUND_REVIEW_YOUNG_DAYS = 14;
176
+ /** Which tier a signal belongs to. */
177
+ export type PhishingSignalTier = 'strong' | 'soft';
178
+ /** The signal codes that hold for every workspace. */
179
+ export declare const STRONG_PHISHING_SIGNAL_CODES: ReadonlySet<string>;
180
+ export declare function phishingSignalTier(signal: {
181
+ code: string;
182
+ }): PhishingSignalTier;
183
+ /** Is a workspace of this age (days, `null` = unreadable) screened by the soft rules? */
184
+ export declare function isYoungWorkspaceAge(ageDays: number | null | undefined): boolean;
185
+ export interface OutboundScreenPolicy {
186
+ /**
187
+ * The workspace's age in days, or `null` when its creation date cannot be
188
+ * read — an org that predates the field, which is an EXISTING customer and
189
+ * is not young.
190
+ */
191
+ ageDays: number | null;
192
+ /**
193
+ * The recipient's own act made this message owed: a receipt for their
194
+ * order, a confirmation of their booking, a reset of their password. The
195
+ * soft rules never hold it. The lookalike rule still does — a receipt that
196
+ * links to a Poshmark lookalike is the disguise, not the receipt.
197
+ */
198
+ owed?: boolean;
199
+ }
200
+ /** The signals that HOLD under the tiers, for this workspace and message. */
201
+ export declare function signalsThatHold<T extends {
202
+ code: string;
203
+ }>(signals: readonly T[], policy: OutboundScreenPolicy): T[];
204
+ /**
205
+ * The same brand, lookalike and tier logic applied to a HOST NAME a workspace
206
+ * asks for — a subdomain on the tenant apex, or a custom domain it attaches.
207
+ *
208
+ * Words a brand's impersonator hyphen-joins to its name. A subdomain that
209
+ * joins a brand's word to one of these (`booking-review`, `apple-support`,
210
+ * `amazon-account`) is the incident's shape even where the brand's word is
211
+ * an ordinary one — which the lookalike rule alone deliberately lets pass,
212
+ * so a salon can be `tanyas-booking` and a bakery `apple-pie-co`.
213
+ */
214
+ export declare const BRAND_IMPERSONATION_WORDS: ReadonlySet<string>;
215
+ /**
216
+ * The brand a requested subdomain label wears, or null.
217
+ *
218
+ * Refused for EVERY workspace, at creation and at rename — the tier is
219
+ * strong, because nothing is lost by asking a real bakery to pick
220
+ * `apple-pie-co` instead of `apple-support`, and a brand's name on our apex
221
+ * is a disguise that outlives every later screen. Two shapes:
222
+ *
223
+ * 1. the label is a lookalike host label ({@link lookalikeBrandForHost} on
224
+ * `<label>.<apex>`): `poshmark`, `paypal-secure`, `paypa1`, `appleid`;
225
+ * 2. a brand's word hyphen-joined to an impersonation word
226
+ * ({@link BRAND_IMPERSONATION_WORDS}): `booking-review`, `apple-support`.
227
+ */
228
+ export declare function brandForSubdomainLabel(label: string, apex?: string): PhishingScreenBrand | null;
229
+ /**
230
+ * The platform's own entry in {@link PHISHING_SCREEN_BRANDS} (AGL-3365). A
231
+ * workspace owns every other brand by carrying its name or domain; the
232
+ * platform's is owned only by the platform's staff, because a workspace NAMED
233
+ * for the platform is the cheapest disguise there is.
234
+ */
235
+ export declare const PLATFORM_PHISHING_BRAND_ID = "aglyn";
236
+ /** The platform's brand entry. */
237
+ export declare function platformPhishingBrand(): PhishingScreenBrand;
238
+ /**
239
+ * The brand a display NAME claims to speak for (AGL-3365), or null.
240
+ *
241
+ * For names a stranger reads as an identity — a marketplace publisher, a
242
+ * listing's title — rather than copy. A name that merely mentions a brand
243
+ * ("Sync for PayPal", "Bookings for Airbnb hosts") passes: that is how an
244
+ * integration is named. A name that pairs the brand with a claim of
245
+ * officialness ("PayPal Support", "Aglyn Official Plugins") is the disguise.
246
+ */
247
+ export declare function brandClaimedByName(name: string): PhishingScreenBrand | null;
248
+ export {};