@aglyn/shared-util-email 1.0.0-beta.270 → 1.0.0-beta.272
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/package.json +3 -3
- package/src/index.d.ts +2 -0
- package/src/index.js +2 -0
- package/src/index.js.map +1 -1
- package/src/lib/mail-provider-resend.d.ts +1 -1
- package/src/lib/mail-provider-resend.js +3 -1
- package/src/lib/mail-provider-resend.js.map +1 -1
- package/src/lib/received-email.d.ts +18 -0
- package/src/lib/received-email.js +22 -1
- package/src/lib/received-email.js.map +1 -1
- package/src/lib/site-care-emails.d.ts +25 -0
- package/src/lib/site-care-emails.js +436 -0
- package/src/lib/site-care-emails.js.map +1 -0
- package/src/lib/site-care-project-emails.d.ts +22 -0
- package/src/lib/site-care-project-emails.js +287 -0
- package/src/lib/site-care-project-emails.js.map +1 -0
- package/src/lib/system-email-catalog.js +215 -2
- package/src/lib/system-email-catalog.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/shared-util-email",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.272",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
28
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.272",
|
|
29
|
+
"@aglyn/shared-util-http": "1.0.0-beta.272",
|
|
30
30
|
"@msgpack/msgpack": "^3.1.3",
|
|
31
31
|
"@swc/helpers": "0.5.23"
|
|
32
32
|
},
|
package/src/index.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ export * from './lib/platform-sending-domain';
|
|
|
28
28
|
export * from './lib/email-health';
|
|
29
29
|
export * from './lib/system-email-catalog';
|
|
30
30
|
export * from './lib/retention-emails';
|
|
31
|
+
export * from './lib/site-care-emails';
|
|
32
|
+
export * from './lib/site-care-project-emails';
|
|
31
33
|
export * from './lib/domain-emails';
|
|
32
34
|
export * from './lib/welcome-email';
|
|
33
35
|
export * from './lib/tenant-email-catalog';
|
package/src/index.js
CHANGED
|
@@ -32,6 +32,8 @@ export * from "./lib/platform-sending-domain.js";
|
|
|
32
32
|
export * from "./lib/email-health.js";
|
|
33
33
|
export * from "./lib/system-email-catalog.js";
|
|
34
34
|
export * from "./lib/retention-emails.js";
|
|
35
|
+
export * from "./lib/site-care-emails.js";
|
|
36
|
+
export * from "./lib/site-care-project-emails.js";
|
|
35
37
|
export * from "./lib/domain-emails.js";
|
|
36
38
|
export * from "./lib/welcome-email.js";
|
|
37
39
|
export * from "./lib/tenant-email-catalog.js";
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/shared/util/email/src/index.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport * from './lib/send-email'\n// Who carries the mail (AGL-3080): the provider contract, and the selection\n// of the deployment's provider from the built-ins and any a plugin\n// registered. The providers themselves are imported by their own paths —\n// `@aglyn/shared-util-email/mail-provider-resend`, `…/mail-provider-webhook`\n// — because nothing outside the seam should name one.\nexport * from './lib/mail-provider'\nexport * from './lib/mail-providers'\nexport * from './lib/send-rate'\nexport * from './lib/sender-reputation'\nexport * from './lib/send-ceilings'\nexport * from './lib/sending-domain'\nexport * from './lib/tracking-host'\nexport * from './lib/sending-mailbox'\nexport * from './lib/host-sender'\nexport * from './lib/platform-sending-domain'\nexport * from './lib/email-health'\nexport * from './lib/system-email-catalog'\nexport * from './lib/retention-emails'\nexport * from './lib/domain-emails'\nexport * from './lib/welcome-email'\nexport * from './lib/tenant-email-catalog'\nexport * from './lib/host-email-render'\nexport * from './lib/email-render'\nexport * from './lib/text-email-html'\nexport * from './lib/marketing-send'\n// The List-Unsubscribe setting sequences and campaigns share (AGL-3307).\nexport * from './lib/list-unsubscribe'\nexport * from './lib/email-delivery-events'\n// Received mail in the platform's vocabulary, which the CRM's capture route\n// files from (AGL-2657); the provider reads it.\n// Its neighbor `./lib/svix-signature` is deliberately NOT re-exported here:\n// it holds a `crypto` HMAC, and this barrel is reached from the browser\n// through `@aglyn/shared-ui-email-campaigns`, so a webhook imports it by its\n// own path — `@aglyn/shared-util-email/svix-signature`.\nexport * from './lib/received-email'\nexport * from './lib/email-revenue-window'\n// Exported so the console drift guard can pin it against @aglyn/aglyn.\nexport * from './lib/email-media-src'\n// Same arrangement, same reason (AGL-1223): a copy of `decodeStoredNodes`\n// this scope:shared lib may not import, pinned by `email-nodes-drift.spec.ts`.\nexport * from './lib/stored-email-nodes'\nexport * from './lib/email-merge'\n// The deliverability engine every send path asks (AGL-3328): the gateway an\n// MX names and its ledger, the bounce reader, and the address verdicts. Pure\n// — the DNS lookup and the store are injected by `@aglyn/tenant-data-admin`,\n// so nothing here reaches `node:dns` from the browser.\nexport * from './lib/mail-gateway'\nexport * from './lib/mail-bounce'\nexport * from './lib/email-deliverability'\n// The sender's side of the same engine (AGL-3328): SPF, DKIM and DMARC for\n// the domain mail leaves from, and whether they align. Pure as well; the\n// lookups are `@aglyn/tenant-data-admin`'s.\nexport * from './lib/sender-readiness'\n// The outbound phishing screen a young workspace's campaigns and automated\n// email pass (AGL-3356). Pure; the hold it leads to is `tenant-data-admin`'s.\nexport * from './lib/outbound-phishing-screen'\n// The send seam's gate for that screen — the store a hold writes is injected\n// by `@aglyn/tenant-data-admin`, like the governor's.\nexport * from './lib/outbound-screen-gate'\n// The same screen read over a published page (AGL-3356): lookalike links and\n// embeds, author-defined credential fields, a brand's call to action.\nexport * from './lib/hosted-page-screen'\n// The reputation lookup every foreign link a page, an email or a redirect\n// points at passes (AGL-3451). Pure; the lookup is injected by\n// `@aglyn/tenant-data-admin`, which holds the credential and the cache.\nexport * from './lib/link-reputation'\n// The site's theme palette as plain data (AGL-3370), so a palette token an\n// author picked resolves in mail. Built without MUI; `shared-ui-theme`'s\n// parity spec holds it equal to the theme.\nexport * from './lib/email-palette'\nexport * from './lib/email-design-chrome'\n"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,cAAc,sBAAkB;AAChC,4EAA4E;AAC5E,mEAAmE;AACnE,yEAAyE;AACzE,6EAA6E;AAC7E,sDAAsD;AACtD,cAAc,yBAAqB;AACnC,cAAc,0BAAsB;AACpC,cAAc,qBAAiB;AAC/B,cAAc,6BAAyB;AACvC,cAAc,yBAAqB;AACnC,cAAc,0BAAsB;AACpC,cAAc,yBAAqB;AACnC,cAAc,2BAAuB;AACrC,cAAc,uBAAmB;AACjC,cAAc,mCAA+B;AAC7C,cAAc,wBAAoB;AAClC,cAAc,gCAA4B;AAC1C,cAAc,4BAAwB;AACtC,cAAc,yBAAqB;AACnC,cAAc,yBAAqB;AACnC,cAAc,gCAA4B;AAC1C,cAAc,6BAAyB;AACvC,cAAc,wBAAoB;AAClC,cAAc,2BAAuB;AACrC,cAAc,0BAAsB;AACpC,yEAAyE;AACzE,cAAc,4BAAwB;AACtC,cAAc,iCAA6B;AAC3C,4EAA4E;AAC5E,gDAAgD;AAChD,4EAA4E;AAC5E,wEAAwE;AACxE,6EAA6E;AAC7E,wDAAwD;AACxD,cAAc,0BAAsB;AACpC,cAAc,gCAA4B;AAC1C,uEAAuE;AACvE,cAAc,2BAAuB;AACrC,0EAA0E;AAC1E,+EAA+E;AAC/E,cAAc,8BAA0B;AACxC,cAAc,uBAAmB;AACjC,4EAA4E;AAC5E,6EAA6E;AAC7E,6EAA6E;AAC7E,uDAAuD;AACvD,cAAc,wBAAoB;AAClC,cAAc,uBAAmB;AACjC,cAAc,gCAA4B;AAC1C,2EAA2E;AAC3E,yEAAyE;AACzE,4CAA4C;AAC5C,cAAc,4BAAwB;AACtC,2EAA2E;AAC3E,8EAA8E;AAC9E,cAAc,oCAAgC;AAC9C,6EAA6E;AAC7E,sDAAsD;AACtD,cAAc,gCAA4B;AAC1C,6EAA6E;AAC7E,sEAAsE;AACtE,cAAc,8BAA0B;AACxC,0EAA0E;AAC1E,+DAA+D;AAC/D,wEAAwE;AACxE,cAAc,2BAAuB;AACrC,2EAA2E;AAC3E,yEAAyE;AACzE,2CAA2C;AAC3C,cAAc,yBAAqB;AACnC,cAAc,+BAA2B"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/shared/util/email/src/index.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport * from './lib/send-email'\n// Who carries the mail (AGL-3080): the provider contract, and the selection\n// of the deployment's provider from the built-ins and any a plugin\n// registered. The providers themselves are imported by their own paths —\n// `@aglyn/shared-util-email/mail-provider-resend`, `…/mail-provider-webhook`\n// — because nothing outside the seam should name one.\nexport * from './lib/mail-provider'\nexport * from './lib/mail-providers'\nexport * from './lib/send-rate'\nexport * from './lib/sender-reputation'\nexport * from './lib/send-ceilings'\nexport * from './lib/sending-domain'\nexport * from './lib/tracking-host'\nexport * from './lib/sending-mailbox'\nexport * from './lib/host-sender'\nexport * from './lib/platform-sending-domain'\nexport * from './lib/email-health'\nexport * from './lib/system-email-catalog'\nexport * from './lib/retention-emails'\nexport * from './lib/site-care-emails'\nexport * from './lib/site-care-project-emails'\nexport * from './lib/domain-emails'\nexport * from './lib/welcome-email'\nexport * from './lib/tenant-email-catalog'\nexport * from './lib/host-email-render'\nexport * from './lib/email-render'\nexport * from './lib/text-email-html'\nexport * from './lib/marketing-send'\n// The List-Unsubscribe setting sequences and campaigns share (AGL-3307).\nexport * from './lib/list-unsubscribe'\nexport * from './lib/email-delivery-events'\n// Received mail in the platform's vocabulary, which the CRM's capture route\n// files from (AGL-2657); the provider reads it.\n// Its neighbor `./lib/svix-signature` is deliberately NOT re-exported here:\n// it holds a `crypto` HMAC, and this barrel is reached from the browser\n// through `@aglyn/shared-ui-email-campaigns`, so a webhook imports it by its\n// own path — `@aglyn/shared-util-email/svix-signature`.\nexport * from './lib/received-email'\nexport * from './lib/email-revenue-window'\n// Exported so the console drift guard can pin it against @aglyn/aglyn.\nexport * from './lib/email-media-src'\n// Same arrangement, same reason (AGL-1223): a copy of `decodeStoredNodes`\n// this scope:shared lib may not import, pinned by `email-nodes-drift.spec.ts`.\nexport * from './lib/stored-email-nodes'\nexport * from './lib/email-merge'\n// The deliverability engine every send path asks (AGL-3328): the gateway an\n// MX names and its ledger, the bounce reader, and the address verdicts. Pure\n// — the DNS lookup and the store are injected by `@aglyn/tenant-data-admin`,\n// so nothing here reaches `node:dns` from the browser.\nexport * from './lib/mail-gateway'\nexport * from './lib/mail-bounce'\nexport * from './lib/email-deliverability'\n// The sender's side of the same engine (AGL-3328): SPF, DKIM and DMARC for\n// the domain mail leaves from, and whether they align. Pure as well; the\n// lookups are `@aglyn/tenant-data-admin`'s.\nexport * from './lib/sender-readiness'\n// The outbound phishing screen a young workspace's campaigns and automated\n// email pass (AGL-3356). Pure; the hold it leads to is `tenant-data-admin`'s.\nexport * from './lib/outbound-phishing-screen'\n// The send seam's gate for that screen — the store a hold writes is injected\n// by `@aglyn/tenant-data-admin`, like the governor's.\nexport * from './lib/outbound-screen-gate'\n// The same screen read over a published page (AGL-3356): lookalike links and\n// embeds, author-defined credential fields, a brand's call to action.\nexport * from './lib/hosted-page-screen'\n// The reputation lookup every foreign link a page, an email or a redirect\n// points at passes (AGL-3451). Pure; the lookup is injected by\n// `@aglyn/tenant-data-admin`, which holds the credential and the cache.\nexport * from './lib/link-reputation'\n// The site's theme palette as plain data (AGL-3370), so a palette token an\n// author picked resolves in mail. Built without MUI; `shared-ui-theme`'s\n// parity spec holds it equal to the theme.\nexport * from './lib/email-palette'\nexport * from './lib/email-design-chrome'\n"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,cAAc,sBAAkB;AAChC,4EAA4E;AAC5E,mEAAmE;AACnE,yEAAyE;AACzE,6EAA6E;AAC7E,sDAAsD;AACtD,cAAc,yBAAqB;AACnC,cAAc,0BAAsB;AACpC,cAAc,qBAAiB;AAC/B,cAAc,6BAAyB;AACvC,cAAc,yBAAqB;AACnC,cAAc,0BAAsB;AACpC,cAAc,yBAAqB;AACnC,cAAc,2BAAuB;AACrC,cAAc,uBAAmB;AACjC,cAAc,mCAA+B;AAC7C,cAAc,wBAAoB;AAClC,cAAc,gCAA4B;AAC1C,cAAc,4BAAwB;AACtC,cAAc,4BAAwB;AACtC,cAAc,oCAAgC;AAC9C,cAAc,yBAAqB;AACnC,cAAc,yBAAqB;AACnC,cAAc,gCAA4B;AAC1C,cAAc,6BAAyB;AACvC,cAAc,wBAAoB;AAClC,cAAc,2BAAuB;AACrC,cAAc,0BAAsB;AACpC,yEAAyE;AACzE,cAAc,4BAAwB;AACtC,cAAc,iCAA6B;AAC3C,4EAA4E;AAC5E,gDAAgD;AAChD,4EAA4E;AAC5E,wEAAwE;AACxE,6EAA6E;AAC7E,wDAAwD;AACxD,cAAc,0BAAsB;AACpC,cAAc,gCAA4B;AAC1C,uEAAuE;AACvE,cAAc,2BAAuB;AACrC,0EAA0E;AAC1E,+EAA+E;AAC/E,cAAc,8BAA0B;AACxC,cAAc,uBAAmB;AACjC,4EAA4E;AAC5E,6EAA6E;AAC7E,6EAA6E;AAC7E,uDAAuD;AACvD,cAAc,wBAAoB;AAClC,cAAc,uBAAmB;AACjC,cAAc,gCAA4B;AAC1C,2EAA2E;AAC3E,yEAAyE;AACzE,4CAA4C;AAC5C,cAAc,4BAAwB;AACtC,2EAA2E;AAC3E,8EAA8E;AAC9E,cAAc,oCAAgC;AAC9C,6EAA6E;AAC7E,sDAAsD;AACtD,cAAc,gCAA4B;AAC1C,6EAA6E;AAC7E,sEAAsE;AACtE,cAAc,8BAA0B;AACxC,0EAA0E;AAC1E,+DAA+D;AAC/D,wEAAwE;AACxE,cAAc,2BAAuB;AACrC,2EAA2E;AAC3E,yEAAyE;AACzE,2CAA2C;AAC3C,cAAc,yBAAqB;AACnC,cAAc,+BAA2B"}
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
import { type EmailDeliveryEvent, type EmailDeliveryHistorySource, type EmailDeliveryMessage, type EmailDeliveryMessageSource, type EmailDeliverySnapshot } from './email-delivery-events';
|
|
36
36
|
import type { EmailCredentialReport } from './email-health';
|
|
37
37
|
import { type MailInboundEvent, type MailProvider, type MailProviderMessage, type MailSendingDomain } from './mail-provider';
|
|
38
|
-
import type
|
|
38
|
+
import { type ReceivedEmail, type ReceivedEmailSource } from './received-email';
|
|
39
39
|
/** The slug stored on every delivery record this provider produces. */
|
|
40
40
|
export declare const RESEND_PROVIDER_ID = "resend";
|
|
41
41
|
/** The sending key's environment variable. */
|
|
@@ -34,6 +34,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
|
34
34
|
*/ import { bareSenderAddress, normalizeEventTags } from "./email-delivery-events.js";
|
|
35
35
|
import { readBounceText } from "./mail-bounce.js";
|
|
36
36
|
import { providerRetryAtMs } from "./mail-provider.js";
|
|
37
|
+
import { isAutoSubmittedMail } from "./received-email.js";
|
|
37
38
|
/** The slug stored on every delivery record this provider produces. */ export const RESEND_PROVIDER_ID = 'resend';
|
|
38
39
|
/** The sending key's environment variable. */ export const RESEND_SEND_KEY_SETTING = 'RESEND_API_KEY';
|
|
39
40
|
/** The full-access read key's environment variable. */ export const RESEND_READ_KEY_SETTING = 'RESEND_READ_API_KEY';
|
|
@@ -582,7 +583,8 @@ const text = (value)=>typeof value === 'string' ? value : '';
|
|
|
582
583
|
subject: String((_record_subject = record['subject']) != null ? _record_subject : '').trim(),
|
|
583
584
|
text: text(record['text']),
|
|
584
585
|
html: text(record['html']),
|
|
585
|
-
receivedAtMs: Number.isFinite(created) && created > 0 ? created : nowMs
|
|
586
|
+
receivedAtMs: Number.isFinite(created) && created > 0 ? created : nowMs,
|
|
587
|
+
autoSubmitted: isAutoSubmittedMail((name)=>header(headers, name))
|
|
586
588
|
};
|
|
587
589
|
}
|
|
588
590
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/shared/util/email/src/lib/mail-provider-resend.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * RESEND, as one {@link MailProvider} — the only module in the platform that\n * knows Resend's endpoints, payloads, event names and error names.\n *\n * It is the provider a deployment gets when it names none, which is how\n * every deployment that set `RESEND_API_KEY` before the contract existed\n * keeps sending exactly as it did. Two credentials, on purpose:\n *\n * - `RESEND_API_KEY` sends. It is provisioned sending-scoped, so a leaked\n * copy cannot enumerate everyone the platform ever wrote to.\n * - `RESEND_READ_API_KEY` is full-access and only ever reads: history, one\n * message's body, received mail and the account's sending domains. A\n * sending-scoped key answers every such read `401 restricted_api_key`.\n *\n * Pure but for the `fetch` calls, each inside a function that must be called\n * to do anything, so a client component that reaches this module through the\n * library's barrel executes nothing on load.\n */\n\nimport {\n bareSenderAddress,\n normalizeEventTags,\n type EmailDeliveryEvent,\n type EmailDeliveryEventType,\n type EmailDeliveryHistorySource,\n type EmailDeliveryMessage,\n type EmailDeliveryMessageSource,\n type EmailDeliverySnapshot,\n} from './email-delivery-events'\nimport type { EmailCredentialReport } from './email-health'\nimport { readBounceText } from './mail-bounce'\nimport {\n providerRetryAtMs,\n type MailInboundEvent,\n type MailProvider,\n type MailProviderMessage,\n type MailProviderReads,\n type MailProviderSendResult,\n type MailSendingDomain,\n} from './mail-provider'\nimport type { ReceivedEmail, ReceivedEmailSource } from './received-email'\n\n/** The slug stored on every delivery record this provider produces. */\nexport const RESEND_PROVIDER_ID = 'resend'\n\n/** The sending key's environment variable. */\nexport const RESEND_SEND_KEY_SETTING = 'RESEND_API_KEY'\n\n/** The full-access read key's environment variable. */\nexport const RESEND_READ_KEY_SETTING = 'RESEND_READ_API_KEY'\n\ntype Env = Record<string, string | undefined>\n\nfunction setting(env: Env, name: string): string {\n return String(env[name] ?? '').trim()\n}\n\n/*==========================================\n * SENDING.\n *=========================================*/\n\n/** The send endpoint. */\nexport const RESEND_SEND_ENDPOINT = 'https://api.resend.com/emails'\n\n/** A Resend send payload in the provider's own wire shape. */\nexport interface ResendSendPayload {\n to?: unknown\n from?: unknown\n subject?: unknown\n [field: string]: unknown\n}\n\n/**\n * The one place that POSTs to Resend's send endpoint, and the last thing\n * standing between a payload and the network.\n *\n * A payload carrying no recipient cannot become a message. Resend answers it\n * `422 missing_required_field`, which costs an API call and then shows up in\n * the vendor dashboard as a red line indistinguishable from mail that\n * genuinely failed to deliver — carrying no subject, no recipient and nothing\n * naming the code that produced it. Diagnosing that means reading a log\n * outside the deployment and guessing. So the refusal happens here, before\n * the fetch, and names the caller's `context`.\n *\n * It throws rather than returning a result: this is a programming error,\n * not a delivery outcome. `sendEmail` filters recipients well before it\n * reaches this call, so nothing on the ordinary path can trip it. The guard\n * exists because `RESEND_SEND_ENDPOINT` is exported and any module can\n * therefore reach the send endpoint on its own, bypassing every check\n * `sendEmail` owns.\n */\nexport async function postResendEmail(\n apiKey: string,\n payload: ResendSendPayload,\n context?: string,\n): Promise<Response> {\n const raw = payload?.to\n const recipients = (Array.isArray(raw) ? raw : raw == null ? [] : [raw])\n .map((address) => String(address ?? '').trim())\n .filter(Boolean)\n if (!recipients.length) {\n throw new Error(\n `${context ? `${context} ` : ''}send refused before the network — a ` +\n 'Resend payload with no `to` field cannot become a message, and the ' +\n 'attempt would surface only as a 422 in the Resend dashboard',\n )\n }\n\n return fetch(RESEND_SEND_ENDPOINT, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(payload),\n })\n}\n\n/** One message as Resend's send endpoint takes it. */\nexport function resendSendPayload(message: MailProviderMessage): ResendSendPayload {\n return {\n from: message.from,\n to: message.to,\n subject: message.subject,\n ...(message.text ? { text: message.text } : {}),\n ...(message.html ? { html: message.html } : {}),\n ...(message.headers && Object.keys(message.headers).length\n ? { headers: message.headers }\n : {}),\n ...(message.tags?.length ? { tags: message.tags } : {}),\n ...(message.replyTo ? { reply_to: message.replyTo } : {}),\n ...(message.bcc?.length ? { bcc: message.bcc } : {}),\n }\n}\n\n/**\n * Sends one message and reads the answer.\n *\n * A 429 is a request to slow down, not a statement about the recipient, so\n * it comes back `rateLimited` with the wait Resend asked for. Its quota\n * errors — `daily_quota_exceeded`, `monthly_quota_exceeded` — are 429s as\n * well, and clear with time the same way.\n */\nasync function sendThroughResend(\n apiKey: string,\n message: MailProviderMessage,\n context?: string,\n): Promise<MailProviderSendResult> {\n const response = await postResendEmail(apiKey, resendSendPayload(message), context)\n if (!response.ok) {\n const detail = await response.text().catch(() => '')\n if (response.status === 429) {\n return {\n accepted: false,\n rateLimited: true,\n retryAtMs: providerRetryAtMs(response.headers),\n status: response.status,\n detail,\n }\n }\n return { accepted: false, rateLimited: false, status: response.status, detail }\n }\n const body = (await response.json().catch(() => null)) as { id?: string } | null\n return { accepted: true, id: body?.id ?? null }\n}\n\n/*==========================================\n * THE CREDENTIAL PROBE.\n *=========================================*/\n\n/**\n * Where the credential probe and the domain read ask their questions.\n *\n * Deliberately NOT the send endpoint. A probe aimed at `/emails` is a send\n * attempt however empty its body is: it consumes an API call, and Resend\n * records it in the account's logs as a `422` on `POST /emails` with no\n * recipient, no subject and nothing identifying the caller — a line an\n * operator reading that dashboard has to treat as failed mail. A domain read\n * cannot create a message and cannot be mistaken for one.\n */\nexport const RESEND_DOMAINS_ENDPOINT = 'https://api.resend.com/domains'\n\n/**\n * Error names that mean the key itself was not accepted, as opposed to a key\n * that authenticated and merely lacks read scope. Matched by NAME, not\n * status: `401` and `403` each cover both meanings.\n */\nconst REFUSED_KEY_ERRORS = new Set([\n 'missing_api_key',\n 'validation_error',\n 'suspended_api_key',\n])\n\n/**\n * Error names that mean the key authenticated and was then denied this\n * particular read. A sending-scoped key — the shape Aglyn provisions — always\n * lands here, and reaching this answer at all required Resend to recognize\n * the credential, which is exactly what the probe is asking.\n */\nconst AUTHENTICATED_BUT_UNSCOPED_ERRORS = new Set([\n 'restricted_api_key',\n 'invalid_permission',\n])\n\n/** The `name` Resend puts on an error body, or `''` for anything else. */\nfunction resendErrorName(body: string): string {\n try {\n const parsed = JSON.parse(body) as { name?: unknown }\n return typeof parsed?.name === 'string' ? parsed.name : ''\n } catch {\n return ''\n }\n}\n\n/**\n * Whether the sending key is accepted — without sending anything to anybody,\n * and without leaving anything behind that reads as failed mail.\n *\n * How: a `GET` of the domains collection. The question is only ever \"does\n * Resend recognize this credential\", so the probe reads the ERROR NAME rather\n * than the status, because `401` and `403` each carry both meanings:\n *\n * - `2xx` — the key is accepted and has read scope → `ok`\n * - `restricted_api_key` / `invalid_permission` — Resend authenticated the\n * key and then denied it this read. A sending-scoped key always answers\n * this way, and getting the answer proves the credential works → `ok`\n * - `missing_api_key` / `validation_error` / `suspended_api_key` — the key\n * itself was refused → `invalid-key`\n * - anything else → `unknown`\n *\n * An unrecognized rejection is `unknown`, never `invalid-key`: this feeds a\n * staff diagnostics screen whose whole value is that a red line means\n * something, and a shape we have not seen before is not evidence that a\n * working key is broken.\n */\nexport async function checkResendCredentials(\n apiKey: string,\n): Promise<EmailCredentialReport> {\n if (!apiKey) return { status: 'unconfigured' }\n try {\n const response = await fetch(RESEND_DOMAINS_ENDPOINT, {\n method: 'GET',\n headers: { Authorization: `Bearer ${apiKey}` },\n })\n const detail = (await response.text().catch(() => '')).slice(0, 300)\n\n if (response.status >= 200 && response.status < 300) {\n return { status: 'ok', probeStatus: response.status }\n }\n if (response.status === 401 || response.status === 403) {\n const name = resendErrorName(detail)\n if (AUTHENTICATED_BUT_UNSCOPED_ERRORS.has(name)) {\n return { status: 'ok', probeStatus: response.status }\n }\n if (REFUSED_KEY_ERRORS.has(name)) {\n return { status: 'invalid-key', probeStatus: response.status, detail }\n }\n }\n return { status: 'unknown', probeStatus: response.status, detail }\n } catch (error) {\n return {\n status: 'unknown',\n detail: String((error as Error)?.message ?? error).slice(0, 300),\n }\n }\n}\n\n/**\n * Every sending domain on the account, with its verification and tracking\n * state. Throws with the status when Resend does not answer 2xx, so a caller\n * never reads \"I could not look\" as \"there is nothing there\".\n */\nexport async function readResendSendingDomains(\n apiKey: string,\n endpoint: string = RESEND_DOMAINS_ENDPOINT,\n): Promise<MailSendingDomain[]> {\n const response = await fetch(endpoint, {\n method: 'GET',\n headers: { Authorization: `Bearer ${apiKey}` },\n })\n if (response.status < 200 || response.status >= 300) {\n throw new Error(`The provider answered ${response.status} to the domain read.`)\n }\n const payload = (await response.json().catch(() => null)) as\n | {\n data?: Array<{\n name?: unknown\n status?: unknown\n click_tracking?: unknown\n open_tracking?: unknown\n }>\n }\n | null\n /**\n * A tri-state, and the third state matters: a listing that does not carry\n * the field must not be reported as the field being false. Off is a fault\n * somebody should fix; unknown is a question this read could not ask.\n */\n const flag = (value: unknown): boolean | null =>\n typeof value === 'boolean' ? value : null\n const domains: MailSendingDomain[] = []\n for (const row of payload?.data ?? []) {\n const name = String(row?.name ?? '').toLowerCase()\n if (!name) continue\n domains.push({\n name,\n status: String(row?.status ?? 'unknown'),\n clickTracking: flag(row?.click_tracking),\n openTracking: flag(row?.open_tracking),\n })\n }\n return domains\n}\n\n/*==========================================\n * THE DELIVERY FEED.\n *=========================================*/\n\n/** Epoch ms from an ISO string or a number, or `null` when unreadable. */\nfunction eventTimeMs(value: unknown): number | null {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n const parsed = Date.parse(String(value ?? ''))\n return Number.isFinite(parsed) ? parsed : null\n}\n\nfunction normalizeBounceType(\n value: unknown,\n): EmailDeliveryEvent['bounceType'] {\n const lowered = String(value ?? '')\n .trim()\n .toLowerCase()\n if (lowered === 'permanent') return 'permanent'\n if (lowered === 'transient') return 'transient'\n return lowered ? 'undetermined' : null\n}\n\n/** The Resend event names we understand, mapped onto ours. */\nconst RESEND_EVENT_TYPES: Record<string, EmailDeliveryEventType> = {\n 'email.sent': 'sent',\n 'email.delivered': 'delivered',\n 'email.delivery_delayed': 'delayed',\n 'email.opened': 'opened',\n 'email.clicked': 'clicked',\n 'email.bounced': 'bounced',\n 'email.complained': 'complained',\n 'email.failed': 'failed',\n}\n\n/**\n * Turns one Resend webhook payload into zero or more of our events.\n *\n * One event per recipient, not per message: a send addressed to three people\n * produces one webhook, and a staff view keyed on a person has to be able to\n * find it under each of them.\n *\n * Returns an empty array for anything unrecognised — a contact or domain\n * event, an inbound `email.received`, a type added after this was written.\n * Silence rather than a throw, because a webhook handler that 500s on an\n * unfamiliar event teaches the provider to retry it forever.\n *\n * @param payload The parsed webhook body.\n * @param receivedAtMs Fallback timestamp for a payload that carries none.\n */\nexport function normalizeResendDeliveryEvents(\n payload: unknown,\n receivedAtMs: number,\n): EmailDeliveryEvent[] {\n const event = (payload ?? {}) as Record<string, any>\n const type = RESEND_EVENT_TYPES[String(event.type ?? '')]\n if (!type) return []\n\n const data = (event.data ?? {}) as Record<string, any>\n const providerMessageId = String(data.email_id ?? data.id ?? '').trim()\n if (!providerMessageId) return []\n\n const recipients = (Array.isArray(data.to) ? data.to : [data.to])\n .map((address: unknown) => String(address ?? '').trim().toLowerCase())\n .filter((address: string) => address.includes('@'))\n if (!recipients.length) return []\n\n // The per-state timestamp when the provider gives one, because an open\n // three days after the send is the whole point of recording an open.\n const at =\n eventTimeMs(data.click?.timestamp) ??\n eventTimeMs(data.open?.timestamp) ??\n eventTimeMs(event.created_at) ??\n eventTimeMs(data.created_at) ??\n receivedAtMs\n\n const subject = String(data.subject ?? '').trim() || null\n const tags = normalizeEventTags(data.tags)\n const from = bareSenderAddress(data.from)\n // The provider folds a DSN's fields into one sentence; the structured\n // ones are read first where a payload carries them.\n const bounceText =\n type === 'bounced'\n ? readBounceText(\n [data.bounce?.diagnosticCode, data.bounce?.diagnostic_code, data.bounce?.remoteMta, data.bounce?.remote_mta, data.bounce?.message]\n .filter((part: unknown) => typeof part === 'string' && part.trim())\n .join(' '),\n )\n : null\n\n return recipients.map((to: string) => ({\n type,\n at,\n provider: RESEND_PROVIDER_ID,\n providerMessageId,\n to,\n subject,\n context: tags['context'] || null,\n tags,\n link: type === 'clicked' ? String(data.click?.link ?? '') || null : null,\n bounceType: type === 'bounced' ? normalizeBounceType(data.bounce?.type) : null,\n detail:\n String(data.bounce?.message ?? data.failed?.reason ?? '').trim() || null,\n ...(from ? { from } : {}),\n ...(bounceText?.status ? { bounceStatus: bounceText.status } : {}),\n ...(bounceText?.remoteMta ? { remoteMta: bounceText.remoteMta } : {}),\n }))\n}\n\n/*==========================================\n * THE ACCOUNT'S HISTORY.\n *\n * Resend's list endpoint reports a single `last_event` per message and no\n * engagement detail, so a snapshot can say \"this was opened at least once\"\n * and can never say \"three times\" — see `EmailDeliverySnapshot`.\n *=========================================*/\n\n/**\n * A `last_event` string, mapped onto our lifecycle.\n *\n * Deliberately the bare state names rather than the `email.*` event names:\n * the list endpoint reports `\"delivered\"`, the webhook reports\n * `\"email.delivered\"`, and they are two different vocabularies for one\n * concept.\n */\nconst RESEND_LAST_EVENTS: Record<string, EmailDeliveryEventType> = {\n sent: 'sent',\n delivered: 'delivered',\n delivery_delayed: 'delayed',\n opened: 'opened',\n clicked: 'clicked',\n bounced: 'bounced',\n complained: 'complained',\n failed: 'failed',\n canceled: 'failed',\n queued: 'sent',\n scheduled: 'sent',\n}\n\n/**\n * Turns one entry from Resend's `GET /emails` list into zero or more\n * snapshots — one per recipient, for the same reason the event adapter fans\n * out: the staff view is keyed on a person.\n *\n * An unrecognised `last_event` falls back to `sent` rather than being\n * dropped. The message demonstrably exists and was addressed to somebody, and\n * \"we sent this and cannot characterise what happened next\" is a far more\n * useful row than no row — which is the state that sent a staffer to the\n * vendor dashboard in the first place.\n */\nexport function normalizeResendSentEmails(\n raw: unknown,\n): EmailDeliverySnapshot[] {\n const record = (raw ?? {}) as Record<string, any>\n const providerMessageId = String(record.id ?? '').trim()\n if (!providerMessageId) return []\n\n const recipients = (Array.isArray(record.to) ? record.to : [record.to])\n .map((address: unknown) => String(address ?? '').trim().toLowerCase())\n .filter((address: string) => address.includes('@'))\n if (!recipients.length) return []\n\n const parsed = Date.parse(String(record.created_at ?? ''))\n const sentAt = Number.isFinite(parsed) ? parsed : 0\n // A snapshot with no timestamp cannot be ordered, and the log's read drops\n // any document missing its sort key — so it is refused rather than written\n // somewhere nothing will look for it.\n if (!sentAt) return []\n\n const status =\n RESEND_LAST_EVENTS[\n String(record.last_event ?? '')\n .trim()\n .toLowerCase()\n ] ?? 'sent'\n\n return recipients.map((to: string) => ({\n provider: RESEND_PROVIDER_ID,\n providerMessageId,\n to,\n subject: String(record.subject ?? '').trim() || null,\n sentAt,\n status,\n }))\n}\n\n/** The list endpoint. Paginates with `after=<id>`; caps at 100. */\nexport const RESEND_EMAILS_ENDPOINT = 'https://api.resend.com/emails'\n\n/**\n * {@link EmailDeliveryHistorySource} for Resend. Needs the full-access key.\n *\n * The list endpoint takes `limit`, `after` and `before` and no recipient\n * filter, which is why the source is a cursor-paged sweep.\n */\nexport function resendDeliveryHistorySource(\n apiKey: string,\n): EmailDeliveryHistorySource {\n return async ({ cursor, limit } = {}) => {\n const params = new URLSearchParams({\n limit: String(Math.min(Math.max(1, limit ?? 100), 100)),\n })\n if (cursor) params.set('after', cursor)\n const response = await fetch(`${RESEND_EMAILS_ENDPOINT}?${params}`, {\n headers: { Authorization: `Bearer ${apiKey}` },\n })\n if (!response.ok) {\n const detail = await response.text().catch(() => '')\n throw new Error(\n `email history read failed: HTTP ${response.status} ${detail.slice(0, 200)}`,\n )\n }\n const body = (await response.json()) as {\n data?: unknown[]\n has_more?: boolean\n }\n const entries = Array.isArray(body?.data) ? body.data : []\n const snapshots = entries.flatMap((entry) => normalizeResendSentEmails(entry))\n // The cursor is the LAST RAW entry's id, not the last snapshot's: a page\n // whose final entry fanned out to zero snapshots (no recipient, no\n // timestamp) would otherwise rewind the cursor to an earlier message and\n // loop over the same page forever.\n const lastId = String(\n (entries[entries.length - 1] as { id?: unknown })?.id ?? '',\n ).trim()\n return {\n snapshots,\n nextCursor: body?.has_more && lastId ? lastId : null,\n }\n }\n}\n\nfunction addressList(raw: unknown): string[] {\n return (Array.isArray(raw) ? raw : raw == null ? [] : [raw])\n .map((address) => String(address ?? '').trim())\n .filter(Boolean)\n}\n\n/** Resend's `GET /emails/:id` payload, in our vocabulary. */\nexport function normalizeResendMessage(raw: unknown): EmailDeliveryMessage | null {\n const record = (raw ?? {}) as Record<string, any>\n const providerMessageId = String(record.id ?? '').trim()\n if (!providerMessageId) return null\n const parsed = Date.parse(String(record.created_at ?? ''))\n return {\n provider: RESEND_PROVIDER_ID,\n providerMessageId,\n to: addressList(record.to),\n cc: addressList(record.cc),\n bcc: addressList(record.bcc),\n from: String(record.from ?? '').trim() || null,\n replyTo: addressList(record.reply_to).length\n ? addressList(record.reply_to)\n : null,\n subject: String(record.subject ?? '').trim() || null,\n // Empty string is NOT null here, and the difference is the point: a\n // message that went out text-only really does have an empty HTML part,\n // and a reader has to be able to tell that from \"we could not fetch it\".\n html: typeof record.html === 'string' ? record.html : null,\n text: typeof record.text === 'string' ? record.text : null,\n sentAt: Number.isFinite(parsed) ? parsed : null,\n status:\n RESEND_LAST_EVENTS[\n String(record.last_event ?? '')\n .trim()\n .toLowerCase()\n ] ?? null,\n }\n}\n\n/** Resend's single-message endpoint. Needs the full-access key. */\nexport function resendDeliveryMessageSource(\n apiKey: string,\n): EmailDeliveryMessageSource {\n return async (providerMessageId: string) => {\n const response = await fetch(\n `${RESEND_EMAILS_ENDPOINT}/${encodeURIComponent(providerMessageId)}`,\n { headers: { Authorization: `Bearer ${apiKey}` } },\n )\n // A message the provider has aged out is a 404, and that is an ANSWER —\n // \"we know this was sent and the body is gone\" — not a failure to report\n // as an error the staffer must act on.\n if (response.status === 404) return null\n if (!response.ok) {\n const detail = await response.text().catch(() => '')\n throw new Error(\n `message read failed: HTTP ${response.status} ${detail.slice(0, 200)}`,\n )\n }\n return normalizeResendMessage(await response.json())\n }\n}\n\n/*==========================================\n * RECEIVED MAIL.\n *\n * Resend announces a received message as an `email.received` webhook that\n * carries only its metadata — who it was from and to, its subject, its\n * `Message-ID` — and the message itself is read afterwards from the\n * receiving API by the id the event named.\n *=========================================*/\n\n/** The webhook event type a received message arrives as. */\nexport const RESEND_RECEIVED_EVENT = 'email.received'\n\n/** `GET {endpoint}/{id}` — one received message with its text and headers. */\nexport const RESEND_RECEIVING_ENDPOINT = 'https://api.resend.com/emails/receiving'\n\nconst list = (value: unknown): string[] =>\n Array.isArray(value)\n ? value.map((entry) => String(entry ?? '').trim()).filter((entry) => entry !== '')\n : typeof value === 'string' && value.trim()\n ? [value.trim()]\n : []\n\nconst text = (value: unknown): string => (typeof value === 'string' ? value : '')\n\n/** A header by name, case-insensitively, off whatever map the provider sent. */\nfunction header(headers: unknown, name: string): string {\n if (!headers || typeof headers !== 'object') return ''\n const wanted = name.toLowerCase()\n for (const [key, value] of Object.entries(headers as Record<string, unknown>)) {\n if (key.toLowerCase() === wanted) {\n return Array.isArray(value) ? String(value[0] ?? '').trim() : String(value ?? '').trim()\n }\n }\n return ''\n}\n\n/**\n * The provider's id of the message an `email.received` event announces,\n * or `null` for any other event — including a malformed one, which is\n * acknowledged and dropped rather than retried forever.\n */\nexport function resendReceivedEventId(event: unknown): string | null {\n const record = event as { type?: unknown; data?: { email_id?: unknown } } | null\n if (!record || record.type !== RESEND_RECEIVED_EVENT) return null\n const id = String(record.data?.email_id ?? '').trim()\n return id || null\n}\n\n/**\n * Every recipient the event names — To, Cc, Bcc and the addresses the\n * message was received for — so a route can find what it is looking for in\n * the metadata before it spends a read on the body.\n */\nexport function resendReceivedEventRecipients(event: unknown): string[] {\n const data = (event as { data?: Record<string, unknown> } | null)?.data ?? {}\n return [\n ...list(data['to']),\n ...list(data['cc']),\n ...list(data['bcc']),\n ...list(data['received_for']),\n ]\n}\n\n/**\n * One received message as the receiving API answers it, in the neutral\n * shape. `null` for a payload with no id, which is not a message.\n */\nexport function normalizeResendReceivedEmail(\n raw: unknown,\n nowMs: number = Date.now(),\n): ReceivedEmail | null {\n const record = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>\n const id = String(record['id'] ?? '').trim()\n if (!id) return null\n const headers = record['headers']\n const created = Date.parse(String(record['created_at'] ?? ''))\n return {\n id,\n messageId: String(record['message_id'] ?? '').trim() || header(headers, 'message-id'),\n inReplyTo: header(headers, 'in-reply-to'),\n from: String(record['from'] ?? '').trim(),\n to: list(record['to']),\n cc: list(record['cc']),\n bcc: list(record['bcc']),\n receivedFor: list(record['received_for']),\n subject: String(record['subject'] ?? '').trim(),\n text: text(record['text']),\n html: text(record['html']),\n receivedAtMs: Number.isFinite(created) && created > 0 ? created : nowMs,\n }\n}\n\n/**\n * {@link ReceivedEmailSource} for Resend. Needs the full-access key. A `404`\n * is `null` — the message expired or never existed — and anything else\n * throws with the status, so the caller decides whether the provider should\n * retry.\n */\nexport function resendReceivedEmailSource(\n apiKey: string,\n fetchImpl: typeof fetch = fetch,\n): ReceivedEmailSource {\n return async (id) => {\n const response = await fetchImpl(\n `${RESEND_RECEIVING_ENDPOINT}/${encodeURIComponent(id)}`,\n { headers: { Authorization: `Bearer ${apiKey}` } },\n )\n if (response.status === 404) return null\n if (!response.ok) {\n const detail = await response.text().catch(() => '')\n throw new Error(\n `received email read failed: HTTP ${response.status} ${detail.slice(0, 200)}`,\n )\n }\n return normalizeResendReceivedEmail(await response.json())\n }\n}\n\n/** A received-mail notification, or `null` for any other event. */\nexport function resendInboundEvent(event: unknown): MailInboundEvent | null {\n const id = resendReceivedEventId(event)\n return id ? { id, recipients: resendReceivedEventRecipients(event) } : null\n}\n\n/*==========================================\n * THE PROVIDER.\n *=========================================*/\n\n/**\n * Resend as a {@link MailProvider}, reading its two keys from `env` at each\n * call — never at module load, because these run in serverless handlers\n * whose module may be evaluated during a build, long before the runtime\n * environment exists.\n */\nexport function resendMailProvider(\n env: Env = process.env as Env,\n): MailProvider {\n const sendKey = () => setting(env, RESEND_SEND_KEY_SETTING)\n const readKey = () => {\n const key = setting(env, RESEND_READ_KEY_SETTING)\n if (!key) throw new Error(`${RESEND_READ_KEY_SETTING} is not set`)\n return key\n }\n const reads: MailProviderReads = {\n unmet: () =>\n setting(env, RESEND_READ_KEY_SETTING)\n ? null\n : `Set ${RESEND_READ_KEY_SETTING} to a full-access key. The sending ` +\n 'key cannot read mail.',\n // Async, so a missing key REJECTS like every other failed read rather\n // than throwing before the caller has a promise to catch.\n history: async (options) => resendDeliveryHistorySource(readKey())(options),\n message: async (id) => resendDeliveryMessageSource(readKey())(id),\n received: async (id) => resendReceivedEmailSource(readKey())(id),\n sendingDomains: async () => readResendSendingDomains(readKey()),\n }\n return {\n id: RESEND_PROVIDER_ID,\n missingSettings: () => (sendKey() ? [] : [RESEND_SEND_KEY_SETTING]),\n send: (message, context) => sendThroughResend(sendKey(), message, context),\n checkCredentials: () => checkResendCredentials(sendKey()),\n deliveryEvents: normalizeResendDeliveryEvents,\n inboundEvent: resendInboundEvent,\n reads,\n }\n}\n"],"names":["bareSenderAddress","normalizeEventTags","readBounceText","providerRetryAtMs","RESEND_PROVIDER_ID","RESEND_SEND_KEY_SETTING","RESEND_READ_KEY_SETTING","setting","env","name","String","trim","RESEND_SEND_ENDPOINT","postResendEmail","apiKey","payload","context","raw","to","recipients","Array","isArray","map","address","filter","Boolean","length","Error","fetch","method","headers","Authorization","body","JSON","stringify","resendSendPayload","message","from","subject","text","html","Object","keys","tags","replyTo","reply_to","bcc","sendThroughResend","response","ok","detail","catch","status","accepted","rateLimited","retryAtMs","json","id","RESEND_DOMAINS_ENDPOINT","REFUSED_KEY_ERRORS","Set","AUTHENTICATED_BUT_UNSCOPED_ERRORS","resendErrorName","parsed","parse","checkResendCredentials","slice","probeStatus","has","error","readResendSendingDomains","endpoint","flag","value","domains","row","data","toLowerCase","push","clickTracking","click_tracking","openTracking","open_tracking","eventTimeMs","Number","isFinite","Date","normalizeBounceType","lowered","RESEND_EVENT_TYPES","normalizeResendDeliveryEvents","receivedAtMs","event","type","providerMessageId","email_id","includes","at","click","timestamp","open","created_at","bounceText","bounce","diagnosticCode","diagnostic_code","remoteMta","remote_mta","part","join","provider","link","bounceType","failed","reason","bounceStatus","RESEND_LAST_EVENTS","sent","delivered","delivery_delayed","opened","clicked","bounced","complained","canceled","queued","scheduled","normalizeResendSentEmails","record","sentAt","last_event","RESEND_EMAILS_ENDPOINT","resendDeliveryHistorySource","cursor","limit","entries","params","URLSearchParams","Math","min","max","set","snapshots","flatMap","entry","lastId","nextCursor","has_more","addressList","normalizeResendMessage","cc","resendDeliveryMessageSource","encodeURIComponent","RESEND_RECEIVED_EVENT","RESEND_RECEIVING_ENDPOINT","list","header","wanted","key","resendReceivedEventId","resendReceivedEventRecipients","normalizeResendReceivedEmail","nowMs","now","created","messageId","inReplyTo","receivedFor","resendReceivedEmailSource","fetchImpl","resendInboundEvent","resendMailProvider","process","sendKey","readKey","reads","unmet","history","options","received","sendingDomains","missingSettings","send","checkCredentials","deliveryEvents","inboundEvent"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/shared/util/email/src/lib/mail-provider-resend.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * RESEND, as one {@link MailProvider} — the only module in the platform that\n * knows Resend's endpoints, payloads, event names and error names.\n *\n * It is the provider a deployment gets when it names none, which is how\n * every deployment that set `RESEND_API_KEY` before the contract existed\n * keeps sending exactly as it did. Two credentials, on purpose:\n *\n * - `RESEND_API_KEY` sends. It is provisioned sending-scoped, so a leaked\n * copy cannot enumerate everyone the platform ever wrote to.\n * - `RESEND_READ_API_KEY` is full-access and only ever reads: history, one\n * message's body, received mail and the account's sending domains. A\n * sending-scoped key answers every such read `401 restricted_api_key`.\n *\n * Pure but for the `fetch` calls, each inside a function that must be called\n * to do anything, so a client component that reaches this module through the\n * library's barrel executes nothing on load.\n */\n\nimport {\n bareSenderAddress,\n normalizeEventTags,\n type EmailDeliveryEvent,\n type EmailDeliveryEventType,\n type EmailDeliveryHistorySource,\n type EmailDeliveryMessage,\n type EmailDeliveryMessageSource,\n type EmailDeliverySnapshot,\n} from './email-delivery-events'\nimport type { EmailCredentialReport } from './email-health'\nimport { readBounceText } from './mail-bounce'\nimport {\n providerRetryAtMs,\n type MailInboundEvent,\n type MailProvider,\n type MailProviderMessage,\n type MailProviderReads,\n type MailProviderSendResult,\n type MailSendingDomain,\n} from './mail-provider'\nimport { isAutoSubmittedMail, type ReceivedEmail, type ReceivedEmailSource } from './received-email'\n\n/** The slug stored on every delivery record this provider produces. */\nexport const RESEND_PROVIDER_ID = 'resend'\n\n/** The sending key's environment variable. */\nexport const RESEND_SEND_KEY_SETTING = 'RESEND_API_KEY'\n\n/** The full-access read key's environment variable. */\nexport const RESEND_READ_KEY_SETTING = 'RESEND_READ_API_KEY'\n\ntype Env = Record<string, string | undefined>\n\nfunction setting(env: Env, name: string): string {\n return String(env[name] ?? '').trim()\n}\n\n/*==========================================\n * SENDING.\n *=========================================*/\n\n/** The send endpoint. */\nexport const RESEND_SEND_ENDPOINT = 'https://api.resend.com/emails'\n\n/** A Resend send payload in the provider's own wire shape. */\nexport interface ResendSendPayload {\n to?: unknown\n from?: unknown\n subject?: unknown\n [field: string]: unknown\n}\n\n/**\n * The one place that POSTs to Resend's send endpoint, and the last thing\n * standing between a payload and the network.\n *\n * A payload carrying no recipient cannot become a message. Resend answers it\n * `422 missing_required_field`, which costs an API call and then shows up in\n * the vendor dashboard as a red line indistinguishable from mail that\n * genuinely failed to deliver — carrying no subject, no recipient and nothing\n * naming the code that produced it. Diagnosing that means reading a log\n * outside the deployment and guessing. So the refusal happens here, before\n * the fetch, and names the caller's `context`.\n *\n * It throws rather than returning a result: this is a programming error,\n * not a delivery outcome. `sendEmail` filters recipients well before it\n * reaches this call, so nothing on the ordinary path can trip it. The guard\n * exists because `RESEND_SEND_ENDPOINT` is exported and any module can\n * therefore reach the send endpoint on its own, bypassing every check\n * `sendEmail` owns.\n */\nexport async function postResendEmail(\n apiKey: string,\n payload: ResendSendPayload,\n context?: string,\n): Promise<Response> {\n const raw = payload?.to\n const recipients = (Array.isArray(raw) ? raw : raw == null ? [] : [raw])\n .map((address) => String(address ?? '').trim())\n .filter(Boolean)\n if (!recipients.length) {\n throw new Error(\n `${context ? `${context} ` : ''}send refused before the network — a ` +\n 'Resend payload with no `to` field cannot become a message, and the ' +\n 'attempt would surface only as a 422 in the Resend dashboard',\n )\n }\n\n return fetch(RESEND_SEND_ENDPOINT, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(payload),\n })\n}\n\n/** One message as Resend's send endpoint takes it. */\nexport function resendSendPayload(message: MailProviderMessage): ResendSendPayload {\n return {\n from: message.from,\n to: message.to,\n subject: message.subject,\n ...(message.text ? { text: message.text } : {}),\n ...(message.html ? { html: message.html } : {}),\n ...(message.headers && Object.keys(message.headers).length\n ? { headers: message.headers }\n : {}),\n ...(message.tags?.length ? { tags: message.tags } : {}),\n ...(message.replyTo ? { reply_to: message.replyTo } : {}),\n ...(message.bcc?.length ? { bcc: message.bcc } : {}),\n }\n}\n\n/**\n * Sends one message and reads the answer.\n *\n * A 429 is a request to slow down, not a statement about the recipient, so\n * it comes back `rateLimited` with the wait Resend asked for. Its quota\n * errors — `daily_quota_exceeded`, `monthly_quota_exceeded` — are 429s as\n * well, and clear with time the same way.\n */\nasync function sendThroughResend(\n apiKey: string,\n message: MailProviderMessage,\n context?: string,\n): Promise<MailProviderSendResult> {\n const response = await postResendEmail(apiKey, resendSendPayload(message), context)\n if (!response.ok) {\n const detail = await response.text().catch(() => '')\n if (response.status === 429) {\n return {\n accepted: false,\n rateLimited: true,\n retryAtMs: providerRetryAtMs(response.headers),\n status: response.status,\n detail,\n }\n }\n return { accepted: false, rateLimited: false, status: response.status, detail }\n }\n const body = (await response.json().catch(() => null)) as { id?: string } | null\n return { accepted: true, id: body?.id ?? null }\n}\n\n/*==========================================\n * THE CREDENTIAL PROBE.\n *=========================================*/\n\n/**\n * Where the credential probe and the domain read ask their questions.\n *\n * Deliberately NOT the send endpoint. A probe aimed at `/emails` is a send\n * attempt however empty its body is: it consumes an API call, and Resend\n * records it in the account's logs as a `422` on `POST /emails` with no\n * recipient, no subject and nothing identifying the caller — a line an\n * operator reading that dashboard has to treat as failed mail. A domain read\n * cannot create a message and cannot be mistaken for one.\n */\nexport const RESEND_DOMAINS_ENDPOINT = 'https://api.resend.com/domains'\n\n/**\n * Error names that mean the key itself was not accepted, as opposed to a key\n * that authenticated and merely lacks read scope. Matched by NAME, not\n * status: `401` and `403` each cover both meanings.\n */\nconst REFUSED_KEY_ERRORS = new Set([\n 'missing_api_key',\n 'validation_error',\n 'suspended_api_key',\n])\n\n/**\n * Error names that mean the key authenticated and was then denied this\n * particular read. A sending-scoped key — the shape Aglyn provisions — always\n * lands here, and reaching this answer at all required Resend to recognize\n * the credential, which is exactly what the probe is asking.\n */\nconst AUTHENTICATED_BUT_UNSCOPED_ERRORS = new Set([\n 'restricted_api_key',\n 'invalid_permission',\n])\n\n/** The `name` Resend puts on an error body, or `''` for anything else. */\nfunction resendErrorName(body: string): string {\n try {\n const parsed = JSON.parse(body) as { name?: unknown }\n return typeof parsed?.name === 'string' ? parsed.name : ''\n } catch {\n return ''\n }\n}\n\n/**\n * Whether the sending key is accepted — without sending anything to anybody,\n * and without leaving anything behind that reads as failed mail.\n *\n * How: a `GET` of the domains collection. The question is only ever \"does\n * Resend recognize this credential\", so the probe reads the ERROR NAME rather\n * than the status, because `401` and `403` each carry both meanings:\n *\n * - `2xx` — the key is accepted and has read scope → `ok`\n * - `restricted_api_key` / `invalid_permission` — Resend authenticated the\n * key and then denied it this read. A sending-scoped key always answers\n * this way, and getting the answer proves the credential works → `ok`\n * - `missing_api_key` / `validation_error` / `suspended_api_key` — the key\n * itself was refused → `invalid-key`\n * - anything else → `unknown`\n *\n * An unrecognized rejection is `unknown`, never `invalid-key`: this feeds a\n * staff diagnostics screen whose whole value is that a red line means\n * something, and a shape we have not seen before is not evidence that a\n * working key is broken.\n */\nexport async function checkResendCredentials(\n apiKey: string,\n): Promise<EmailCredentialReport> {\n if (!apiKey) return { status: 'unconfigured' }\n try {\n const response = await fetch(RESEND_DOMAINS_ENDPOINT, {\n method: 'GET',\n headers: { Authorization: `Bearer ${apiKey}` },\n })\n const detail = (await response.text().catch(() => '')).slice(0, 300)\n\n if (response.status >= 200 && response.status < 300) {\n return { status: 'ok', probeStatus: response.status }\n }\n if (response.status === 401 || response.status === 403) {\n const name = resendErrorName(detail)\n if (AUTHENTICATED_BUT_UNSCOPED_ERRORS.has(name)) {\n return { status: 'ok', probeStatus: response.status }\n }\n if (REFUSED_KEY_ERRORS.has(name)) {\n return { status: 'invalid-key', probeStatus: response.status, detail }\n }\n }\n return { status: 'unknown', probeStatus: response.status, detail }\n } catch (error) {\n return {\n status: 'unknown',\n detail: String((error as Error)?.message ?? error).slice(0, 300),\n }\n }\n}\n\n/**\n * Every sending domain on the account, with its verification and tracking\n * state. Throws with the status when Resend does not answer 2xx, so a caller\n * never reads \"I could not look\" as \"there is nothing there\".\n */\nexport async function readResendSendingDomains(\n apiKey: string,\n endpoint: string = RESEND_DOMAINS_ENDPOINT,\n): Promise<MailSendingDomain[]> {\n const response = await fetch(endpoint, {\n method: 'GET',\n headers: { Authorization: `Bearer ${apiKey}` },\n })\n if (response.status < 200 || response.status >= 300) {\n throw new Error(`The provider answered ${response.status} to the domain read.`)\n }\n const payload = (await response.json().catch(() => null)) as\n | {\n data?: Array<{\n name?: unknown\n status?: unknown\n click_tracking?: unknown\n open_tracking?: unknown\n }>\n }\n | null\n /**\n * A tri-state, and the third state matters: a listing that does not carry\n * the field must not be reported as the field being false. Off is a fault\n * somebody should fix; unknown is a question this read could not ask.\n */\n const flag = (value: unknown): boolean | null =>\n typeof value === 'boolean' ? value : null\n const domains: MailSendingDomain[] = []\n for (const row of payload?.data ?? []) {\n const name = String(row?.name ?? '').toLowerCase()\n if (!name) continue\n domains.push({\n name,\n status: String(row?.status ?? 'unknown'),\n clickTracking: flag(row?.click_tracking),\n openTracking: flag(row?.open_tracking),\n })\n }\n return domains\n}\n\n/*==========================================\n * THE DELIVERY FEED.\n *=========================================*/\n\n/** Epoch ms from an ISO string or a number, or `null` when unreadable. */\nfunction eventTimeMs(value: unknown): number | null {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n const parsed = Date.parse(String(value ?? ''))\n return Number.isFinite(parsed) ? parsed : null\n}\n\nfunction normalizeBounceType(\n value: unknown,\n): EmailDeliveryEvent['bounceType'] {\n const lowered = String(value ?? '')\n .trim()\n .toLowerCase()\n if (lowered === 'permanent') return 'permanent'\n if (lowered === 'transient') return 'transient'\n return lowered ? 'undetermined' : null\n}\n\n/** The Resend event names we understand, mapped onto ours. */\nconst RESEND_EVENT_TYPES: Record<string, EmailDeliveryEventType> = {\n 'email.sent': 'sent',\n 'email.delivered': 'delivered',\n 'email.delivery_delayed': 'delayed',\n 'email.opened': 'opened',\n 'email.clicked': 'clicked',\n 'email.bounced': 'bounced',\n 'email.complained': 'complained',\n 'email.failed': 'failed',\n}\n\n/**\n * Turns one Resend webhook payload into zero or more of our events.\n *\n * One event per recipient, not per message: a send addressed to three people\n * produces one webhook, and a staff view keyed on a person has to be able to\n * find it under each of them.\n *\n * Returns an empty array for anything unrecognised — a contact or domain\n * event, an inbound `email.received`, a type added after this was written.\n * Silence rather than a throw, because a webhook handler that 500s on an\n * unfamiliar event teaches the provider to retry it forever.\n *\n * @param payload The parsed webhook body.\n * @param receivedAtMs Fallback timestamp for a payload that carries none.\n */\nexport function normalizeResendDeliveryEvents(\n payload: unknown,\n receivedAtMs: number,\n): EmailDeliveryEvent[] {\n const event = (payload ?? {}) as Record<string, any>\n const type = RESEND_EVENT_TYPES[String(event.type ?? '')]\n if (!type) return []\n\n const data = (event.data ?? {}) as Record<string, any>\n const providerMessageId = String(data.email_id ?? data.id ?? '').trim()\n if (!providerMessageId) return []\n\n const recipients = (Array.isArray(data.to) ? data.to : [data.to])\n .map((address: unknown) => String(address ?? '').trim().toLowerCase())\n .filter((address: string) => address.includes('@'))\n if (!recipients.length) return []\n\n // The per-state timestamp when the provider gives one, because an open\n // three days after the send is the whole point of recording an open.\n const at =\n eventTimeMs(data.click?.timestamp) ??\n eventTimeMs(data.open?.timestamp) ??\n eventTimeMs(event.created_at) ??\n eventTimeMs(data.created_at) ??\n receivedAtMs\n\n const subject = String(data.subject ?? '').trim() || null\n const tags = normalizeEventTags(data.tags)\n const from = bareSenderAddress(data.from)\n // The provider folds a DSN's fields into one sentence; the structured\n // ones are read first where a payload carries them.\n const bounceText =\n type === 'bounced'\n ? readBounceText(\n [data.bounce?.diagnosticCode, data.bounce?.diagnostic_code, data.bounce?.remoteMta, data.bounce?.remote_mta, data.bounce?.message]\n .filter((part: unknown) => typeof part === 'string' && part.trim())\n .join(' '),\n )\n : null\n\n return recipients.map((to: string) => ({\n type,\n at,\n provider: RESEND_PROVIDER_ID,\n providerMessageId,\n to,\n subject,\n context: tags['context'] || null,\n tags,\n link: type === 'clicked' ? String(data.click?.link ?? '') || null : null,\n bounceType: type === 'bounced' ? normalizeBounceType(data.bounce?.type) : null,\n detail:\n String(data.bounce?.message ?? data.failed?.reason ?? '').trim() || null,\n ...(from ? { from } : {}),\n ...(bounceText?.status ? { bounceStatus: bounceText.status } : {}),\n ...(bounceText?.remoteMta ? { remoteMta: bounceText.remoteMta } : {}),\n }))\n}\n\n/*==========================================\n * THE ACCOUNT'S HISTORY.\n *\n * Resend's list endpoint reports a single `last_event` per message and no\n * engagement detail, so a snapshot can say \"this was opened at least once\"\n * and can never say \"three times\" — see `EmailDeliverySnapshot`.\n *=========================================*/\n\n/**\n * A `last_event` string, mapped onto our lifecycle.\n *\n * Deliberately the bare state names rather than the `email.*` event names:\n * the list endpoint reports `\"delivered\"`, the webhook reports\n * `\"email.delivered\"`, and they are two different vocabularies for one\n * concept.\n */\nconst RESEND_LAST_EVENTS: Record<string, EmailDeliveryEventType> = {\n sent: 'sent',\n delivered: 'delivered',\n delivery_delayed: 'delayed',\n opened: 'opened',\n clicked: 'clicked',\n bounced: 'bounced',\n complained: 'complained',\n failed: 'failed',\n canceled: 'failed',\n queued: 'sent',\n scheduled: 'sent',\n}\n\n/**\n * Turns one entry from Resend's `GET /emails` list into zero or more\n * snapshots — one per recipient, for the same reason the event adapter fans\n * out: the staff view is keyed on a person.\n *\n * An unrecognised `last_event` falls back to `sent` rather than being\n * dropped. The message demonstrably exists and was addressed to somebody, and\n * \"we sent this and cannot characterise what happened next\" is a far more\n * useful row than no row — which is the state that sent a staffer to the\n * vendor dashboard in the first place.\n */\nexport function normalizeResendSentEmails(\n raw: unknown,\n): EmailDeliverySnapshot[] {\n const record = (raw ?? {}) as Record<string, any>\n const providerMessageId = String(record.id ?? '').trim()\n if (!providerMessageId) return []\n\n const recipients = (Array.isArray(record.to) ? record.to : [record.to])\n .map((address: unknown) => String(address ?? '').trim().toLowerCase())\n .filter((address: string) => address.includes('@'))\n if (!recipients.length) return []\n\n const parsed = Date.parse(String(record.created_at ?? ''))\n const sentAt = Number.isFinite(parsed) ? parsed : 0\n // A snapshot with no timestamp cannot be ordered, and the log's read drops\n // any document missing its sort key — so it is refused rather than written\n // somewhere nothing will look for it.\n if (!sentAt) return []\n\n const status =\n RESEND_LAST_EVENTS[\n String(record.last_event ?? '')\n .trim()\n .toLowerCase()\n ] ?? 'sent'\n\n return recipients.map((to: string) => ({\n provider: RESEND_PROVIDER_ID,\n providerMessageId,\n to,\n subject: String(record.subject ?? '').trim() || null,\n sentAt,\n status,\n }))\n}\n\n/** The list endpoint. Paginates with `after=<id>`; caps at 100. */\nexport const RESEND_EMAILS_ENDPOINT = 'https://api.resend.com/emails'\n\n/**\n * {@link EmailDeliveryHistorySource} for Resend. Needs the full-access key.\n *\n * The list endpoint takes `limit`, `after` and `before` and no recipient\n * filter, which is why the source is a cursor-paged sweep.\n */\nexport function resendDeliveryHistorySource(\n apiKey: string,\n): EmailDeliveryHistorySource {\n return async ({ cursor, limit } = {}) => {\n const params = new URLSearchParams({\n limit: String(Math.min(Math.max(1, limit ?? 100), 100)),\n })\n if (cursor) params.set('after', cursor)\n const response = await fetch(`${RESEND_EMAILS_ENDPOINT}?${params}`, {\n headers: { Authorization: `Bearer ${apiKey}` },\n })\n if (!response.ok) {\n const detail = await response.text().catch(() => '')\n throw new Error(\n `email history read failed: HTTP ${response.status} ${detail.slice(0, 200)}`,\n )\n }\n const body = (await response.json()) as {\n data?: unknown[]\n has_more?: boolean\n }\n const entries = Array.isArray(body?.data) ? body.data : []\n const snapshots = entries.flatMap((entry) => normalizeResendSentEmails(entry))\n // The cursor is the LAST RAW entry's id, not the last snapshot's: a page\n // whose final entry fanned out to zero snapshots (no recipient, no\n // timestamp) would otherwise rewind the cursor to an earlier message and\n // loop over the same page forever.\n const lastId = String(\n (entries[entries.length - 1] as { id?: unknown })?.id ?? '',\n ).trim()\n return {\n snapshots,\n nextCursor: body?.has_more && lastId ? lastId : null,\n }\n }\n}\n\nfunction addressList(raw: unknown): string[] {\n return (Array.isArray(raw) ? raw : raw == null ? [] : [raw])\n .map((address) => String(address ?? '').trim())\n .filter(Boolean)\n}\n\n/** Resend's `GET /emails/:id` payload, in our vocabulary. */\nexport function normalizeResendMessage(raw: unknown): EmailDeliveryMessage | null {\n const record = (raw ?? {}) as Record<string, any>\n const providerMessageId = String(record.id ?? '').trim()\n if (!providerMessageId) return null\n const parsed = Date.parse(String(record.created_at ?? ''))\n return {\n provider: RESEND_PROVIDER_ID,\n providerMessageId,\n to: addressList(record.to),\n cc: addressList(record.cc),\n bcc: addressList(record.bcc),\n from: String(record.from ?? '').trim() || null,\n replyTo: addressList(record.reply_to).length\n ? addressList(record.reply_to)\n : null,\n subject: String(record.subject ?? '').trim() || null,\n // Empty string is NOT null here, and the difference is the point: a\n // message that went out text-only really does have an empty HTML part,\n // and a reader has to be able to tell that from \"we could not fetch it\".\n html: typeof record.html === 'string' ? record.html : null,\n text: typeof record.text === 'string' ? record.text : null,\n sentAt: Number.isFinite(parsed) ? parsed : null,\n status:\n RESEND_LAST_EVENTS[\n String(record.last_event ?? '')\n .trim()\n .toLowerCase()\n ] ?? null,\n }\n}\n\n/** Resend's single-message endpoint. Needs the full-access key. */\nexport function resendDeliveryMessageSource(\n apiKey: string,\n): EmailDeliveryMessageSource {\n return async (providerMessageId: string) => {\n const response = await fetch(\n `${RESEND_EMAILS_ENDPOINT}/${encodeURIComponent(providerMessageId)}`,\n { headers: { Authorization: `Bearer ${apiKey}` } },\n )\n // A message the provider has aged out is a 404, and that is an ANSWER —\n // \"we know this was sent and the body is gone\" — not a failure to report\n // as an error the staffer must act on.\n if (response.status === 404) return null\n if (!response.ok) {\n const detail = await response.text().catch(() => '')\n throw new Error(\n `message read failed: HTTP ${response.status} ${detail.slice(0, 200)}`,\n )\n }\n return normalizeResendMessage(await response.json())\n }\n}\n\n/*==========================================\n * RECEIVED MAIL.\n *\n * Resend announces a received message as an `email.received` webhook that\n * carries only its metadata — who it was from and to, its subject, its\n * `Message-ID` — and the message itself is read afterwards from the\n * receiving API by the id the event named.\n *=========================================*/\n\n/** The webhook event type a received message arrives as. */\nexport const RESEND_RECEIVED_EVENT = 'email.received'\n\n/** `GET {endpoint}/{id}` — one received message with its text and headers. */\nexport const RESEND_RECEIVING_ENDPOINT = 'https://api.resend.com/emails/receiving'\n\nconst list = (value: unknown): string[] =>\n Array.isArray(value)\n ? value.map((entry) => String(entry ?? '').trim()).filter((entry) => entry !== '')\n : typeof value === 'string' && value.trim()\n ? [value.trim()]\n : []\n\nconst text = (value: unknown): string => (typeof value === 'string' ? value : '')\n\n/** A header by name, case-insensitively, off whatever map the provider sent. */\nfunction header(headers: unknown, name: string): string {\n if (!headers || typeof headers !== 'object') return ''\n const wanted = name.toLowerCase()\n for (const [key, value] of Object.entries(headers as Record<string, unknown>)) {\n if (key.toLowerCase() === wanted) {\n return Array.isArray(value) ? String(value[0] ?? '').trim() : String(value ?? '').trim()\n }\n }\n return ''\n}\n\n/**\n * The provider's id of the message an `email.received` event announces,\n * or `null` for any other event — including a malformed one, which is\n * acknowledged and dropped rather than retried forever.\n */\nexport function resendReceivedEventId(event: unknown): string | null {\n const record = event as { type?: unknown; data?: { email_id?: unknown } } | null\n if (!record || record.type !== RESEND_RECEIVED_EVENT) return null\n const id = String(record.data?.email_id ?? '').trim()\n return id || null\n}\n\n/**\n * Every recipient the event names — To, Cc, Bcc and the addresses the\n * message was received for — so a route can find what it is looking for in\n * the metadata before it spends a read on the body.\n */\nexport function resendReceivedEventRecipients(event: unknown): string[] {\n const data = (event as { data?: Record<string, unknown> } | null)?.data ?? {}\n return [\n ...list(data['to']),\n ...list(data['cc']),\n ...list(data['bcc']),\n ...list(data['received_for']),\n ]\n}\n\n/**\n * One received message as the receiving API answers it, in the neutral\n * shape. `null` for a payload with no id, which is not a message.\n */\nexport function normalizeResendReceivedEmail(\n raw: unknown,\n nowMs: number = Date.now(),\n): ReceivedEmail | null {\n const record = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>\n const id = String(record['id'] ?? '').trim()\n if (!id) return null\n const headers = record['headers']\n const created = Date.parse(String(record['created_at'] ?? ''))\n return {\n id,\n messageId: String(record['message_id'] ?? '').trim() || header(headers, 'message-id'),\n inReplyTo: header(headers, 'in-reply-to'),\n from: String(record['from'] ?? '').trim(),\n to: list(record['to']),\n cc: list(record['cc']),\n bcc: list(record['bcc']),\n receivedFor: list(record['received_for']),\n subject: String(record['subject'] ?? '').trim(),\n text: text(record['text']),\n html: text(record['html']),\n receivedAtMs: Number.isFinite(created) && created > 0 ? created : nowMs,\n autoSubmitted: isAutoSubmittedMail((name) => header(headers, name)),\n }\n}\n\n/**\n * {@link ReceivedEmailSource} for Resend. Needs the full-access key. A `404`\n * is `null` — the message expired or never existed — and anything else\n * throws with the status, so the caller decides whether the provider should\n * retry.\n */\nexport function resendReceivedEmailSource(\n apiKey: string,\n fetchImpl: typeof fetch = fetch,\n): ReceivedEmailSource {\n return async (id) => {\n const response = await fetchImpl(\n `${RESEND_RECEIVING_ENDPOINT}/${encodeURIComponent(id)}`,\n { headers: { Authorization: `Bearer ${apiKey}` } },\n )\n if (response.status === 404) return null\n if (!response.ok) {\n const detail = await response.text().catch(() => '')\n throw new Error(\n `received email read failed: HTTP ${response.status} ${detail.slice(0, 200)}`,\n )\n }\n return normalizeResendReceivedEmail(await response.json())\n }\n}\n\n/** A received-mail notification, or `null` for any other event. */\nexport function resendInboundEvent(event: unknown): MailInboundEvent | null {\n const id = resendReceivedEventId(event)\n return id ? { id, recipients: resendReceivedEventRecipients(event) } : null\n}\n\n/*==========================================\n * THE PROVIDER.\n *=========================================*/\n\n/**\n * Resend as a {@link MailProvider}, reading its two keys from `env` at each\n * call — never at module load, because these run in serverless handlers\n * whose module may be evaluated during a build, long before the runtime\n * environment exists.\n */\nexport function resendMailProvider(\n env: Env = process.env as Env,\n): MailProvider {\n const sendKey = () => setting(env, RESEND_SEND_KEY_SETTING)\n const readKey = () => {\n const key = setting(env, RESEND_READ_KEY_SETTING)\n if (!key) throw new Error(`${RESEND_READ_KEY_SETTING} is not set`)\n return key\n }\n const reads: MailProviderReads = {\n unmet: () =>\n setting(env, RESEND_READ_KEY_SETTING)\n ? null\n : `Set ${RESEND_READ_KEY_SETTING} to a full-access key. The sending ` +\n 'key cannot read mail.',\n // Async, so a missing key REJECTS like every other failed read rather\n // than throwing before the caller has a promise to catch.\n history: async (options) => resendDeliveryHistorySource(readKey())(options),\n message: async (id) => resendDeliveryMessageSource(readKey())(id),\n received: async (id) => resendReceivedEmailSource(readKey())(id),\n sendingDomains: async () => readResendSendingDomains(readKey()),\n }\n return {\n id: RESEND_PROVIDER_ID,\n missingSettings: () => (sendKey() ? [] : [RESEND_SEND_KEY_SETTING]),\n send: (message, context) => sendThroughResend(sendKey(), message, context),\n checkCredentials: () => checkResendCredentials(sendKey()),\n deliveryEvents: normalizeResendDeliveryEvents,\n inboundEvent: resendInboundEvent,\n reads,\n }\n}\n"],"names":["bareSenderAddress","normalizeEventTags","readBounceText","providerRetryAtMs","isAutoSubmittedMail","RESEND_PROVIDER_ID","RESEND_SEND_KEY_SETTING","RESEND_READ_KEY_SETTING","setting","env","name","String","trim","RESEND_SEND_ENDPOINT","postResendEmail","apiKey","payload","context","raw","to","recipients","Array","isArray","map","address","filter","Boolean","length","Error","fetch","method","headers","Authorization","body","JSON","stringify","resendSendPayload","message","from","subject","text","html","Object","keys","tags","replyTo","reply_to","bcc","sendThroughResend","response","ok","detail","catch","status","accepted","rateLimited","retryAtMs","json","id","RESEND_DOMAINS_ENDPOINT","REFUSED_KEY_ERRORS","Set","AUTHENTICATED_BUT_UNSCOPED_ERRORS","resendErrorName","parsed","parse","checkResendCredentials","slice","probeStatus","has","error","readResendSendingDomains","endpoint","flag","value","domains","row","data","toLowerCase","push","clickTracking","click_tracking","openTracking","open_tracking","eventTimeMs","Number","isFinite","Date","normalizeBounceType","lowered","RESEND_EVENT_TYPES","normalizeResendDeliveryEvents","receivedAtMs","event","type","providerMessageId","email_id","includes","at","click","timestamp","open","created_at","bounceText","bounce","diagnosticCode","diagnostic_code","remoteMta","remote_mta","part","join","provider","link","bounceType","failed","reason","bounceStatus","RESEND_LAST_EVENTS","sent","delivered","delivery_delayed","opened","clicked","bounced","complained","canceled","queued","scheduled","normalizeResendSentEmails","record","sentAt","last_event","RESEND_EMAILS_ENDPOINT","resendDeliveryHistorySource","cursor","limit","entries","params","URLSearchParams","Math","min","max","set","snapshots","flatMap","entry","lastId","nextCursor","has_more","addressList","normalizeResendMessage","cc","resendDeliveryMessageSource","encodeURIComponent","RESEND_RECEIVED_EVENT","RESEND_RECEIVING_ENDPOINT","list","header","wanted","key","resendReceivedEventId","resendReceivedEventRecipients","normalizeResendReceivedEmail","nowMs","now","created","messageId","inReplyTo","receivedFor","autoSubmitted","resendReceivedEmailSource","fetchImpl","resendInboundEvent","resendMailProvider","process","sendKey","readKey","reads","unmet","history","options","received","sendingDomains","missingSettings","send","checkCredentials","deliveryEvents","inboundEvent"],"mappings":"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"}
|
|
@@ -44,6 +44,24 @@ export interface ReceivedEmail {
|
|
|
44
44
|
html: string;
|
|
45
45
|
/** When the provider received it, epoch ms; the read's clock when it said nothing usable. */
|
|
46
46
|
receivedAtMs: number;
|
|
47
|
+
/**
|
|
48
|
+
* Whether its headers say a machine sent it — an out-of-office, a vacation
|
|
49
|
+
* responder ({@link isAutoSubmittedMail}). Absent from a reader that sees
|
|
50
|
+
* no headers, which reads as a person.
|
|
51
|
+
*/
|
|
52
|
+
autoSubmitted?: boolean;
|
|
47
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Whether a message's headers say a machine sent it rather than a person
|
|
56
|
+
* (RFC 3834, AGL-3997): an `Auto-Submitted` other than `no`, the
|
|
57
|
+
* `X-Autoreply` or `X-Autorespond` older responders stamp instead, or
|
|
58
|
+
* `Precedence: auto_reply`. A route that files received mail skips these,
|
|
59
|
+
* so an out-of-office answering an email never becomes a request.
|
|
60
|
+
*
|
|
61
|
+
* `Precedence: list` and `bulk` are deliberately not read: a mailbox that
|
|
62
|
+
* delivers a group's mail onward stamps them on every message a person
|
|
63
|
+
* wrote. `header` answers a header's value by name, `''` when it is absent.
|
|
64
|
+
*/
|
|
65
|
+
export declare function isAutoSubmittedMail(header: (name: string) => string): boolean;
|
|
48
66
|
/** Reads one received message by the provider's id; `null` when it has none. */
|
|
49
67
|
export type ReceivedEmailSource = (id: string) => Promise<ReceivedEmail | null>;
|
|
@@ -23,6 +23,27 @@
|
|
|
23
23
|
* both answer in the neutral {@link ReceivedEmail} everything downstream
|
|
24
24
|
* works with, so a route that files received mail never reads a vendor's
|
|
25
25
|
* payload.
|
|
26
|
-
*/ /** One received message, provider-neutral. Addresses are as the headers spelled them. */ /**
|
|
26
|
+
*/ /** One received message, provider-neutral. Addresses are as the headers spelled them. */ /**
|
|
27
|
+
* Whether a message's headers say a machine sent it rather than a person
|
|
28
|
+
* (RFC 3834, AGL-3997): an `Auto-Submitted` other than `no`, the
|
|
29
|
+
* `X-Autoreply` or `X-Autorespond` older responders stamp instead, or
|
|
30
|
+
* `Precedence: auto_reply`. A route that files received mail skips these,
|
|
31
|
+
* so an out-of-office answering an email never becomes a request.
|
|
32
|
+
*
|
|
33
|
+
* `Precedence: list` and `bulk` are deliberately not read: a mailbox that
|
|
34
|
+
* delivers a group's mail onward stamps them on every message a person
|
|
35
|
+
* wrote. `header` answers a header's value by name, `''` when it is absent.
|
|
36
|
+
*/ export function isAutoSubmittedMail(header) {
|
|
37
|
+
const submitted = header('auto-submitted').split(';')[0].trim().toLowerCase();
|
|
38
|
+
if (submitted && submitted !== 'no') return true;
|
|
39
|
+
for (const name of [
|
|
40
|
+
'x-autoreply',
|
|
41
|
+
'x-autorespond'
|
|
42
|
+
]){
|
|
43
|
+
const value = header(name).trim().toLowerCase();
|
|
44
|
+
if (value && value !== 'no') return true;
|
|
45
|
+
}
|
|
46
|
+
return header('precedence').trim().toLowerCase() === 'auto_reply';
|
|
47
|
+
}
|
|
27
48
|
|
|
28
49
|
//# sourceMappingURL=received-email.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/shared/util/email/src/lib/received-email.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * RECEIVED MAIL, in the platform's own vocabulary (AGL-2657).\n *\n * A provider that receives mail announces each message with a notification\n * that carries its metadata, and the message itself is read afterwards by\n * the id the notification named. Both halves are the provider's to read —\n * its `inboundEvent` and `reads.received` (see `mail-provider.ts`) — and\n * both answer in the neutral {@link ReceivedEmail} everything downstream\n * works with, so a route that files received mail never reads a vendor's\n * payload.\n */\n\n/** One received message, provider-neutral. Addresses are as the headers spelled them. */\nexport interface ReceivedEmail {\n /** The provider's id for the message — what a redelivered event repeats. */\n id: string\n /** The `Message-ID` header, angle brackets included, or `''`. */\n messageId: string\n /** The `In-Reply-To` header, or `''`. */\n inReplyTo: string\n from: string\n to: string[]\n cc: string[]\n bcc: string[]\n /** The addresses the provider received the message FOR — a forwarded alias's real target. */\n receivedFor: string[]\n subject: string\n text: string\n html: string\n /** When the provider received it, epoch ms; the read's clock when it said nothing usable. */\n receivedAtMs: number\n}\n\n/** Reads one received message by the provider's id; `null` when it has none. */\nexport type ReceivedEmailSource = (id: string) => Promise<ReceivedEmail | null>\n"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;CAUC,GAED,uFAAuF,
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/shared/util/email/src/lib/received-email.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * RECEIVED MAIL, in the platform's own vocabulary (AGL-2657).\n *\n * A provider that receives mail announces each message with a notification\n * that carries its metadata, and the message itself is read afterwards by\n * the id the notification named. Both halves are the provider's to read —\n * its `inboundEvent` and `reads.received` (see `mail-provider.ts`) — and\n * both answer in the neutral {@link ReceivedEmail} everything downstream\n * works with, so a route that files received mail never reads a vendor's\n * payload.\n */\n\n/** One received message, provider-neutral. Addresses are as the headers spelled them. */\nexport interface ReceivedEmail {\n /** The provider's id for the message — what a redelivered event repeats. */\n id: string\n /** The `Message-ID` header, angle brackets included, or `''`. */\n messageId: string\n /** The `In-Reply-To` header, or `''`. */\n inReplyTo: string\n from: string\n to: string[]\n cc: string[]\n bcc: string[]\n /** The addresses the provider received the message FOR — a forwarded alias's real target. */\n receivedFor: string[]\n subject: string\n text: string\n html: string\n /** When the provider received it, epoch ms; the read's clock when it said nothing usable. */\n receivedAtMs: number\n /**\n * Whether its headers say a machine sent it — an out-of-office, a vacation\n * responder ({@link isAutoSubmittedMail}). Absent from a reader that sees\n * no headers, which reads as a person.\n */\n autoSubmitted?: boolean\n}\n\n/**\n * Whether a message's headers say a machine sent it rather than a person\n * (RFC 3834, AGL-3997): an `Auto-Submitted` other than `no`, the\n * `X-Autoreply` or `X-Autorespond` older responders stamp instead, or\n * `Precedence: auto_reply`. A route that files received mail skips these,\n * so an out-of-office answering an email never becomes a request.\n *\n * `Precedence: list` and `bulk` are deliberately not read: a mailbox that\n * delivers a group's mail onward stamps them on every message a person\n * wrote. `header` answers a header's value by name, `''` when it is absent.\n */\nexport function isAutoSubmittedMail(header: (name: string) => string): boolean {\n const submitted = header('auto-submitted').split(';')[0].trim().toLowerCase()\n if (submitted && submitted !== 'no') return true\n for (const name of ['x-autoreply', 'x-autorespond']) {\n const value = header(name).trim().toLowerCase()\n if (value && value !== 'no') return true\n }\n return header('precedence').trim().toLowerCase() === 'auto_reply'\n}\n\n/** Reads one received message by the provider's id; `null` when it has none. */\nexport type ReceivedEmailSource = (id: string) => Promise<ReceivedEmail | null>\n"],"names":["isAutoSubmittedMail","header","submitted","split","trim","toLowerCase","name","value"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;CAUC,GAED,uFAAuF,GA2BvF;;;;;;;;;;CAUC,GACD,OAAO,SAASA,oBAAoBC,MAAgC;IAClE,MAAMC,YAAYD,OAAO,kBAAkBE,KAAK,CAAC,IAAI,CAAC,EAAE,CAACC,IAAI,GAAGC,WAAW;IAC3E,IAAIH,aAAaA,cAAc,MAAM,OAAO;IAC5C,KAAK,MAAMI,QAAQ;QAAC;QAAe;KAAgB,CAAE;QACnD,MAAMC,QAAQN,OAAOK,MAAMF,IAAI,GAAGC,WAAW;QAC7C,IAAIE,SAASA,UAAU,MAAM,OAAO;IACtC;IACA,OAAON,OAAO,cAAcG,IAAI,GAAGC,WAAW,OAAO;AACvD"}
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
import type { SystemEmailTemplateDefinition } from './system-email-catalog';
|
|
18
|
+
export declare const SITE_CARE_REQUEST_RECEIVED_EMAIL_KEY = "site-care-request-received";
|
|
19
|
+
export declare const SITE_CARE_REQUEST_DONE_EMAIL_KEY = "site-care-request-done";
|
|
20
|
+
export declare const SITE_CARE_REQUEST_CORRECTED_EMAIL_KEY = "site-care-request-corrected";
|
|
21
|
+
export declare const SITE_CARE_APPROVAL_EMAIL_KEY = "site-care-approval-confirmation";
|
|
22
|
+
export declare const SITE_CARE_CHARGE_REVERSED_EMAIL_KEY = "site-care-charge-reversed";
|
|
23
|
+
export declare const SITE_CARE_STATEMENT_EMAIL_KEY = "site-care-statement";
|
|
24
|
+
export declare const SITE_CARE_QUOTE_EMAIL_KEY = "site-care-quote";
|
|
25
|
+
export declare const SITE_CARE_SYSTEM_EMAIL_TEMPLATES: readonly SystemEmailTemplateDefinition[];
|