@framers/agentos-ext-channel-email 0.6.2 → 0.6.4

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.
@@ -0,0 +1,208 @@
1
+ import type { RecipientCapability } from './preferences/RecipientCapability.js';
2
+ import type { SuppressionStore } from './preferences/SuppressionStore.js';
3
+ /**
4
+ * The list every send reads, required mail and replies included: an address on it gets no mail at all. An operator
5
+ * writes it, such as for a person who asked never to be written to again.
6
+ */
7
+ export declare const DO_NOT_CONTACT_SCOPE = "do-not-contact";
8
+ /** The list of the addresses that turned off every optional mail: a required mail's link, and the page's second button, write it. */
9
+ export declare const OPTIONAL_MAIL_SCOPE = "optional";
10
+ /** What a part may hold where its link goes: the service writes the link there and adds no footer to that part. */
11
+ export declare const UNSUBSCRIBE_URL_MARKER = "{{unsubscribe_url}}";
12
+ /** How the record names an address: 64 lower-case hexadecimal characters, which the route reads a token's recipient by. */
13
+ export declare const ADDRESS_HASH: RegExp;
14
+ /**
15
+ * The list of one category of optional mail, `optional:` and the category: its link writes it, and every optional send
16
+ * of the category reads it.
17
+ *
18
+ * @param category A lower-case letter followed by at most 63 lower-case letters, digits, hyphens or underscores.
19
+ * @throws TypeError when the category does not fit.
20
+ */
21
+ export declare function categoryScope(category: string): string;
22
+ /**
23
+ * The category a list names, or `null` when it is no category's list. `OPTIONAL_MAIL_SCOPE` is no category's list.
24
+ *
25
+ * @param scope A suppression list's name.
26
+ */
27
+ export declare function categoryOfScope(scope: string): string | null;
28
+ /** The configuration's `unsubscribe`: what every mail the service sends then carries, and what it reads first. */
29
+ export interface UnsubscribeOptions {
30
+ /**
31
+ * The page each link opens and the address a mailbox provider's one-click POST goes to: an https address with no
32
+ * query, fragment or credentials, 900 characters at most. A link is it with `?t=` and the token.
33
+ */
34
+ readonly pageUrl: string;
35
+ /**
36
+ * An address at the sender that takes unsubscribe requests by mail, listed after the link in `List-Unsubscribe` as
37
+ * `mailto:` with the subject `unsubscribe`. Leave it out when nothing reads that mailbox.
38
+ */
39
+ readonly mailto?: string;
40
+ /** Where the tokens are drawn, such as `SqlRecipientCapability`; the route finds them in the same store. */
41
+ readonly tokens: RecipientCapability;
42
+ /** The suppression record read before each send, such as `SqlSuppressionStore`; the route writes it. */
43
+ readonly suppressions: SuppressionStore;
44
+ /**
45
+ * The key the tokens are drawn under, 32 random bytes or more kept secret, or a function that answers it at each send
46
+ * so a rotated key applies at once.
47
+ */
48
+ readonly key: Uint8Array | (() => Uint8Array);
49
+ /**
50
+ * The keys tokens were drawn under before `key`, or a function that answers them at each send: list a rotated key
51
+ * while a send under an idempotency key may still be retried, 24 hours through Resend. A retry then finds the token
52
+ * its first attempt drew and carries the same link, since Resend answers a changed payload under the same
53
+ * Idempotency-Key with 409 `invalid_idempotent_request`. Each is 32 bytes or more.
54
+ */
55
+ readonly retiredKeys?: readonly Uint8Array[] | (() => readonly Uint8Array[]);
56
+ /**
57
+ * How the record names an address, given the address trimmed and lower-cased: 64 lower-case hexadecimal characters,
58
+ * the same way wherever the host reads or writes the record. The SHA-256 of the address in hex when not given. A send
59
+ * whose address it names any other way is refused with a `TypeError` before any list is read.
60
+ */
61
+ readonly addressHash?: (address: string) => string;
62
+ /**
63
+ * The sender's postal address on one line, printed at the foot of every mail. Commercial mail is refused without it,
64
+ * since CAN-SPAM asks a commercial mail for the sender's valid physical postal address (15 U.S.C. 7704(a)(5)).
65
+ */
66
+ readonly postalAddress?: string;
67
+ /** The category of the mail the `emailSend` tool and the channel adapter send: `conversation` when not given. */
68
+ readonly agentCategory?: string;
69
+ /** The words of the footer's link: `Unsubscribe` when not given. */
70
+ readonly linkText?: string;
71
+ }
72
+ /** What one send says of its mail. */
73
+ export interface UnsubscribeKind {
74
+ /** The category of optional mail, which its link stops; required mail may name one too, which changes nothing. */
75
+ readonly category?: unknown;
76
+ /** Mail a person cannot turn off: sign-in codes, receipts, security and legal notices. */
77
+ readonly required?: unknown;
78
+ /** Advertising mail: optional, and carrying the postal address. */
79
+ readonly commercial?: unknown;
80
+ }
81
+ /** The lists one send reads, in order, the list its link writes, and whether it is commercial. */
82
+ export interface UnsubscribePlan {
83
+ readonly reads: readonly string[];
84
+ readonly tokenScope: string;
85
+ readonly commercial: boolean;
86
+ }
87
+ /** What a send carries: its link, and the two fields that name it. */
88
+ export interface UnsubscribeLink {
89
+ readonly url: string;
90
+ readonly headers: Readonly<Record<'List-Unsubscribe' | 'List-Unsubscribe-Post', string>>;
91
+ }
92
+ /**
93
+ * A send the suppression record stopped: nothing was sent and no token was drawn. `scope` names the list that held the
94
+ * address; the message names it too, and never the address.
95
+ */
96
+ export declare class EmailSuppressedError extends Error {
97
+ /** Always `suppressed`. */
98
+ readonly code: "suppressed";
99
+ /** The list that held the address: `DO_NOT_CONTACT_SCOPE`, `DEFAULT_SUPPRESSION_SCOPE`, `OPTIONAL_MAIL_SCOPE` or a category's list. */
100
+ readonly scope: string;
101
+ constructor(scope: string);
102
+ }
103
+ /** A line of a footer or a page, checked: text on one line, not blank, `most` characters at most. */
104
+ export declare function requireLine(field: string, value: unknown, most: number): string;
105
+ /** Text written into HTML, its five special characters as references. */
106
+ export declare function escapeHtml(text: string): string;
107
+ /**
108
+ * The one address a recipient names, trimmed and lower-cased: `reader@example.com` or `Reader <reader@example.com>`.
109
+ * A list of addresses, a group, or a display name holding an address is refused with a `TypeError` that holds none of
110
+ * it, since a link names one recipient.
111
+ *
112
+ * @param to The recipient as the send gives it.
113
+ */
114
+ export declare function oneRecipient(to: unknown): string;
115
+ /**
116
+ * Refuses a cc or a bcc while unsubscribe links are on: a link names one recipient, so each person gets a message of
117
+ * their own.
118
+ */
119
+ export declare function refuseCopies(cc: unknown, bcc: unknown): void;
120
+ /** Refuses the two fields the service writes itself while unsubscribe links are on, in any case. */
121
+ export declare function refuseOwnListFields(headers: Record<string, string> | undefined): void;
122
+ /**
123
+ * The checked `unsubscribe` configuration of one service: the plan of each send, the gate and the token, and the
124
+ * footer of each part.
125
+ */
126
+ export declare class Unsubscribe {
127
+ private readonly pageUrl;
128
+ private readonly mailto;
129
+ private readonly tokens;
130
+ private readonly suppressions;
131
+ private readonly key;
132
+ private readonly retiredKeys;
133
+ private readonly hash;
134
+ private readonly linkText;
135
+ /** The sender's postal address, or `undefined`. */
136
+ readonly postalAddress: string | undefined;
137
+ /** The category of the mail the `emailSend` tool and the channel adapter send. */
138
+ readonly agentCategory: string;
139
+ /**
140
+ * Checks every option, and throws a `TypeError` that names the first one at fault and holds none of its value.
141
+ *
142
+ * @param options The configuration's `unsubscribe`.
143
+ */
144
+ constructor(options: UnsubscribeOptions);
145
+ /**
146
+ * The plan of one send, from its kind: required mail reads the do-not-contact list alone and its link turns off every
147
+ * optional mail; optional mail names its category, reads the do-not-contact list, the provider's record
148
+ * (`DEFAULT_SUPPRESSION_SCOPE`), the list of all optional mail and its category's list, and its link stops the
149
+ * category. Throws before anything is read for a kind that does not fit: optional mail that names no category, a
150
+ * category that is not a lower-case word, required mail marked commercial, or commercial mail with no postal address
151
+ * configured.
152
+ *
153
+ * @param kind What the send says of its mail.
154
+ */
155
+ plan(kind: UnsubscribeKind): UnsubscribePlan;
156
+ /** The plan of a reply, and of required mail: the do-not-contact list alone, and a link that turns off every optional mail. */
157
+ replyPlan(): UnsubscribePlan;
158
+ /**
159
+ * Names the address by `addressHash`, and throws a `TypeError` that holds neither the address nor the name when the
160
+ * name is not 64 lower-case hexadecimal characters, since the route would find no link drawn for it. Then reads each of
161
+ * the plan's lists for the address and throws `EmailSuppressedError` for the first that holds it, then
162
+ * draws the token and answers the link with its two fields. A nonce of the send's own, such as its idempotency key,
163
+ * gives a retry the same token, the one a retired key drew when its first attempt came before a rotation; without one
164
+ * each send draws a new one.
165
+ *
166
+ * @param address The recipient, trimmed and lower-cased, as `oneRecipient` answers it.
167
+ * @param plan The send's plan.
168
+ * @param nonce The send's nonce, or `undefined` for a new one.
169
+ */
170
+ prepare(address: string, plan: UnsubscribePlan, nonce: string | undefined): Promise<UnsubscribeLink>;
171
+ /**
172
+ * Reads the do-not-contact list for every address a message names as its sender, trimmed and lower-cased, and throws
173
+ * `EmailSuppressedError` when it holds any of them, before anything is drawn or sent: a person on the list gets no
174
+ * reply, whichever of their addresses the reply would go to. An empty list reads nothing.
175
+ *
176
+ * @param addresses The addresses of the message's From, Sender and Reply-To.
177
+ */
178
+ refuseDoNotContact(addresses: readonly string[]): Promise<void>;
179
+ /** The record's name for an address by `addressHash`, refused when the route could not read it. */
180
+ private nameOf;
181
+ /** The token for the nonce under the current key. */
182
+ private draw;
183
+ /** The token a retired key drew for this nonce, recipient and scope, or `null` when none did or it no longer works. */
184
+ private drawnBefore;
185
+ /**
186
+ * The text part with its link: where it holds `UNSUBSCRIBE_URL_MARKER`, the link written in place of each, and the
187
+ * postal address after a blank line when commercial mail does not hold it already; otherwise a blank line, the link's
188
+ * words and the link, and the postal address on the next line when one is configured.
189
+ *
190
+ * @param body The text part as given.
191
+ * @param url The send's link.
192
+ * @param commercial Whether the mail is commercial.
193
+ */
194
+ text(body: string, url: string, commercial: boolean): string;
195
+ /**
196
+ * The HTML part with its link: where it holds `UNSUBSCRIBE_URL_MARKER` in its text or a tag's attribute, the link
197
+ * written in place of each, and a paragraph with the postal address before `</body>` when commercial mail does not hold
198
+ * it already; otherwise a paragraph in 13-pixel text before `</body>`, or at the end when the part has none, with the
199
+ * link and the postal address under it when one is configured. A marker only in a comment, a script, a style, a title,
200
+ * a textarea or a template is written as the link too, and the part still gets the paragraph, since no reader sees it.
201
+ *
202
+ * @param html The HTML part as given.
203
+ * @param url The send's link.
204
+ * @param commercial Whether the mail is commercial.
205
+ */
206
+ html(html: string, url: string, commercial: boolean): string;
207
+ }
208
+ //# sourceMappingURL=unsubscribe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unsubscribe.d.ts","sourceRoot":"","sources":["../src/unsubscribe.ts"],"names":[],"mappings":"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"}