@framers/agentos-ext-channel-email 0.6.3 → 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.
- package/README.md +99 -4
- package/dist/EmailChannelAdapter.d.ts +4 -1
- package/dist/EmailChannelAdapter.d.ts.map +1 -1
- package/dist/EmailChannelAdapter.js +6 -1
- package/dist/EmailChannelAdapter.js.map +1 -1
- package/dist/EmailService.d.ts +47 -1
- package/dist/EmailService.d.ts.map +1 -1
- package/dist/EmailService.js +88 -16
- package/dist/EmailService.js.map +1 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -2
- package/dist/index.js.map +1 -1
- package/dist/tools/send.d.ts +3 -1
- package/dist/tools/send.d.ts.map +1 -1
- package/dist/tools/send.js +5 -1
- package/dist/tools/send.js.map +1 -1
- package/dist/unsubscribe.d.ts +208 -0
- package/dist/unsubscribe.d.ts.map +1 -0
- package/dist/unsubscribe.js +584 -0
- package/dist/unsubscribe.js.map +1 -0
- package/dist/unsubscribeHandler.d.ts +65 -0
- package/dist/unsubscribeHandler.d.ts.map +1 -0
- package/dist/unsubscribeHandler.js +218 -0
- package/dist/unsubscribeHandler.js.map +1 -0
- package/manifest.json +3 -2
- package/package.json +5 -4
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Unsubscribe = exports.EmailSuppressedError = exports.ADDRESS_HASH = exports.UNSUBSCRIBE_URL_MARKER = exports.OPTIONAL_MAIL_SCOPE = exports.DO_NOT_CONTACT_SCOPE = void 0;
|
|
4
|
+
exports.categoryScope = categoryScope;
|
|
5
|
+
exports.categoryOfScope = categoryOfScope;
|
|
6
|
+
exports.requireLine = requireLine;
|
|
7
|
+
exports.escapeHtml = escapeHtml;
|
|
8
|
+
exports.oneRecipient = oneRecipient;
|
|
9
|
+
exports.refuseCopies = refuseCopies;
|
|
10
|
+
exports.refuseOwnListFields = refuseOwnListFields;
|
|
11
|
+
/**
|
|
12
|
+
* @fileoverview An unsubscribe link on every mail the service sends once its configuration names `unsubscribe`: the
|
|
13
|
+
* suppression record read before the send, an opaque token drawn for the recipient's address and the kind of mail, RFC
|
|
14
|
+
* 8058's `List-Unsubscribe` and `List-Unsubscribe-Post` fields, and a visible link at the foot of each part with the
|
|
15
|
+
* sender's postal address. The token names the address's hash and the list its link writes to, nothing else, so a link
|
|
16
|
+
* can only ever stop mail. `createUnsubscribeHandler` (`./unsubscribeHandler`) answers the link.
|
|
17
|
+
*/
|
|
18
|
+
const node_crypto_1 = require("node:crypto");
|
|
19
|
+
const node_util_1 = require("node:util");
|
|
20
|
+
const RecipientCapability_js_1 = require("./preferences/RecipientCapability.js");
|
|
21
|
+
const SuppressionStore_js_1 = require("./preferences/SuppressionStore.js");
|
|
22
|
+
/**
|
|
23
|
+
* The list every send reads, required mail and replies included: an address on it gets no mail at all. An operator
|
|
24
|
+
* writes it, such as for a person who asked never to be written to again.
|
|
25
|
+
*/
|
|
26
|
+
exports.DO_NOT_CONTACT_SCOPE = 'do-not-contact';
|
|
27
|
+
/** The list of the addresses that turned off every optional mail: a required mail's link, and the page's second button, write it. */
|
|
28
|
+
exports.OPTIONAL_MAIL_SCOPE = 'optional';
|
|
29
|
+
/** What a part may hold where its link goes: the service writes the link there and adds no footer to that part. */
|
|
30
|
+
exports.UNSUBSCRIBE_URL_MARKER = '{{unsubscribe_url}}';
|
|
31
|
+
/** What every category's list begins with. */
|
|
32
|
+
const CATEGORY_PREFIX = 'optional:';
|
|
33
|
+
/** A category: a lower-case letter, then at most 63 lower-case letters, digits, hyphens or underscores. */
|
|
34
|
+
const CATEGORY = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
35
|
+
/** The most characters a page address takes, so a link with its token stays inside one line of `List-Unsubscribe`. */
|
|
36
|
+
const MOST_PAGE_CHARACTERS = 900;
|
|
37
|
+
/** An address a `mailto:` link can carry as written: no character a URI would have to escape. */
|
|
38
|
+
const MAILBOX = /^[A-Za-z0-9._+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)+$/;
|
|
39
|
+
/** One address in a recipient: no white space and none of the characters that separate or quote addresses. */
|
|
40
|
+
const ADDRESS = /^[^\s@<>()[\]\\,;:"]+@[^\s@<>()[\]\\,;:"]+$/;
|
|
41
|
+
/** A character no line of a footer may hold: a control character, or a Unicode line or paragraph separator. */
|
|
42
|
+
const NOT_ON_A_LINE = /[\x00-\x1f\x7f-\x9f\u2028\u2029]/;
|
|
43
|
+
/** How the record names an address: 64 lower-case hexadecimal characters, which the route reads a token's recipient by. */
|
|
44
|
+
exports.ADDRESS_HASH = /^[0-9a-f]{64}$/;
|
|
45
|
+
/** The white space of HTML, the only white space that separates a tag's parts: a no-break space is part of a name. */
|
|
46
|
+
const HTML_SPACE = String.raw `\t\n\f\r `;
|
|
47
|
+
/** What ends a tag's name: white space, a slash or a closing bracket, read and not taken. */
|
|
48
|
+
const NAME_END = String.raw `(?=[${HTML_SPACE}/>])`;
|
|
49
|
+
/**
|
|
50
|
+
* An attribute's value as the HTML standard's tokenizer reads it: quoted, a quote left open running to the part's end,
|
|
51
|
+
* or unquoted, running to white space or a closing bracket.
|
|
52
|
+
*/
|
|
53
|
+
const VALUE = String.raw `(?:"[^"]*(?:"|$)|'[^']*(?:'|$)|[^${HTML_SPACE}>"'][^${HTML_SPACE}>]*)`;
|
|
54
|
+
/**
|
|
55
|
+
* A tag's attributes after its name, as the HTML standard's tokenizer reads them: a name, which an equals sign may
|
|
56
|
+
* start, then an equals sign and a value when it has one, so a quote opens a value only after an equals sign. Every
|
|
57
|
+
* character but `>` starts one alternative or the other, so this stops only at a `>` or the part's end, and whatever
|
|
58
|
+
* follows it in `HTML_SPANS` matches at either: a match never backtracks into it, however long the tag.
|
|
59
|
+
*/
|
|
60
|
+
const ATTRIBUTES = String.raw `(?:[${HTML_SPACE}/]+|(?:=[^${HTML_SPACE}/>=]*|[^${HTML_SPACE}/>=]+)(?:[${HTML_SPACE}]*=[${HTML_SPACE}]*${VALUE}?)?)*`;
|
|
61
|
+
/**
|
|
62
|
+
* The elements a browser reads as text to their end tag, whatever tags they seem to hold. A script has rules of its own,
|
|
63
|
+
* and a plaintext has no end tag.
|
|
64
|
+
*/
|
|
65
|
+
const TEXT_ELEMENTS = 'style|title|textarea|xmp|iframe|noembed|noframes';
|
|
66
|
+
/**
|
|
67
|
+
* Every span of a part's HTML that decides where a footer can go, one match each, read as a browser reads them with
|
|
68
|
+
* scripting off, as mail is:
|
|
69
|
+
*
|
|
70
|
+
* - a comment (`openComment` is set when it is left open to the part's end);
|
|
71
|
+
* - an element whose content is text, to its end tag (`openText` is set when it is left open, `openEnd` when its end tag
|
|
72
|
+
* is);
|
|
73
|
+
* - a doctype, a processing instruction or a bad end tag (`openDeclaration` is set when it is left open);
|
|
74
|
+
* - any other tag, named by `slash` and `name` (`openTag` is set when it is left open).
|
|
75
|
+
*
|
|
76
|
+
* A `</body>` inside any of these is no tag, and a part's end inside any of them hides what follows. Inside an `svg` or
|
|
77
|
+
* a `math` a browser reads a style's or a title's content as tags and a CDATA section as text; this reading takes them
|
|
78
|
+
* as it takes them elsewhere.
|
|
79
|
+
*/
|
|
80
|
+
const HTML_SPANS = new RegExp([
|
|
81
|
+
String.raw `<!--(?:-?>|[\s\S]*?(?:--!?>|(?<openComment>$)))`,
|
|
82
|
+
String.raw `<(?<text>${TEXT_ELEMENTS})${NAME_END}${ATTRIBUTES}(?:>|$)[\s\S]*?(?:<\/\k<text>${NAME_END}${ATTRIBUTES}(?:>|(?<openEnd>$))|(?<openText>$))`,
|
|
83
|
+
String.raw `<(?:[!?]|\/[^a-z>])[^>]*(?:>|(?<openDeclaration>$))`,
|
|
84
|
+
String.raw `<(?<slash>\/?)(?<name>[a-z][^${HTML_SPACE}/>]*)${ATTRIBUTES}(?:>|(?<openTag>$))`,
|
|
85
|
+
].join('|'), 'gi');
|
|
86
|
+
/**
|
|
87
|
+
* What changes how a browser reads a script's content (the script data states of the HTML standard): the start of a
|
|
88
|
+
* comment, which `>` right after it closes at once; the end of a comment; a script's start tag; a script's end tag.
|
|
89
|
+
*/
|
|
90
|
+
const SCRIPT_MARKS = new RegExp(String.raw `<!--(?:-*>)?|-->|<script${NAME_END}|<\/script${NAME_END}`, 'gi');
|
|
91
|
+
/** The rest of an end tag after its name: `open` is set when the part ends inside it. */
|
|
92
|
+
const END_TAG_REST = new RegExp(String.raw `${ATTRIBUTES}(?:>|(?<open>$))`, 'y');
|
|
93
|
+
/** The shortest key the tokens take, HMAC-SHA256's output length. */
|
|
94
|
+
const KEY_BYTES = 32;
|
|
95
|
+
/** The footer's paragraph: 13 pixels, the smallest text a reader is asked to read at a phone's width. */
|
|
96
|
+
const FOOT = 'margin:24px 0 0;font-size:13px;line-height:20px;color:#5f6368';
|
|
97
|
+
/** The footer's link, underlined so it reads as one without colour. */
|
|
98
|
+
const FOOT_LINK = 'color:#5f6368;text-decoration:underline';
|
|
99
|
+
/**
|
|
100
|
+
* Returns the category when it is a lower-case letter followed by at most 63 lower-case letters, digits, hyphens or
|
|
101
|
+
* underscores, and otherwise throws a `TypeError` that never holds the value.
|
|
102
|
+
*/
|
|
103
|
+
function requireCategory(value) {
|
|
104
|
+
if (typeof value === 'string' && CATEGORY.test(value))
|
|
105
|
+
return value;
|
|
106
|
+
throw new TypeError('The category was refused: give a lower-case letter followed by at most 63 lower-case letters, digits, hyphens or underscores');
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The list of one category of optional mail, `optional:` and the category: its link writes it, and every optional send
|
|
110
|
+
* of the category reads it.
|
|
111
|
+
*
|
|
112
|
+
* @param category A lower-case letter followed by at most 63 lower-case letters, digits, hyphens or underscores.
|
|
113
|
+
* @throws TypeError when the category does not fit.
|
|
114
|
+
*/
|
|
115
|
+
function categoryScope(category) {
|
|
116
|
+
return `${CATEGORY_PREFIX}${requireCategory(category)}`;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The category a list names, or `null` when it is no category's list. `OPTIONAL_MAIL_SCOPE` is no category's list.
|
|
120
|
+
*
|
|
121
|
+
* @param scope A suppression list's name.
|
|
122
|
+
*/
|
|
123
|
+
function categoryOfScope(scope) {
|
|
124
|
+
if (!scope.startsWith(CATEGORY_PREFIX))
|
|
125
|
+
return null;
|
|
126
|
+
const category = scope.slice(CATEGORY_PREFIX.length);
|
|
127
|
+
return CATEGORY.test(category) ? category : null;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* A send the suppression record stopped: nothing was sent and no token was drawn. `scope` names the list that held the
|
|
131
|
+
* address; the message names it too, and never the address.
|
|
132
|
+
*/
|
|
133
|
+
class EmailSuppressedError extends Error {
|
|
134
|
+
constructor(scope) {
|
|
135
|
+
super(`The mail was not sent: the recipient is on the ${scope} suppression list`);
|
|
136
|
+
/** Always `suppressed`. */
|
|
137
|
+
this.code = 'suppressed';
|
|
138
|
+
this.name = 'EmailSuppressedError';
|
|
139
|
+
this.scope = scope;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
exports.EmailSuppressedError = EmailSuppressedError;
|
|
143
|
+
/** The page address as given, checked: https, no query, fragment or credentials, 900 characters at most. */
|
|
144
|
+
function requirePageUrl(value) {
|
|
145
|
+
if (typeof value === 'string' && !value.includes('?') && !value.includes('#')) {
|
|
146
|
+
let url;
|
|
147
|
+
try {
|
|
148
|
+
url = new URL(value);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
url = null;
|
|
152
|
+
}
|
|
153
|
+
if (url !== null &&
|
|
154
|
+
url.protocol === 'https:' &&
|
|
155
|
+
url.username === '' &&
|
|
156
|
+
url.password === '' &&
|
|
157
|
+
url.href.length <= MOST_PAGE_CHARACTERS &&
|
|
158
|
+
/^[\x21-\x7e]+$/.test(url.href)) {
|
|
159
|
+
return url.href;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
throw new TypeError(`The unsubscribe page was refused: give an https address with no query, fragment or credentials, ${MOST_PAGE_CHARACTERS} characters at most`);
|
|
163
|
+
}
|
|
164
|
+
/** A `mailto` address, checked: one address of letters, digits, dots, plus signs, hyphens and underscores. */
|
|
165
|
+
function requireMailto(value) {
|
|
166
|
+
if (typeof value === 'string' && value.length <= 254 && MAILBOX.test(value))
|
|
167
|
+
return value;
|
|
168
|
+
throw new TypeError('The unsubscribe mailto was refused: give one address, such as unsubscribe@example.com');
|
|
169
|
+
}
|
|
170
|
+
/** A line of a footer or a page, checked: text on one line, not blank, `most` characters at most. */
|
|
171
|
+
function requireLine(field, value, most) {
|
|
172
|
+
if (typeof value === 'string' && value.trim() !== '' && value.length <= most && !NOT_ON_A_LINE.test(value))
|
|
173
|
+
return value;
|
|
174
|
+
throw new TypeError(`The ${field} was refused: give it as text on one line, ${most} characters at most`);
|
|
175
|
+
}
|
|
176
|
+
/** A store, checked: an object with each of `methods` as a function. */
|
|
177
|
+
function requireStore(field, value, methods) {
|
|
178
|
+
if (typeof value === 'object' && value !== null && methods.every((name) => typeof value[name] === 'function')) {
|
|
179
|
+
return value;
|
|
180
|
+
}
|
|
181
|
+
throw new TypeError(`The unsubscribe ${field} were refused: give a store with ${methods.join(' and ')}`);
|
|
182
|
+
}
|
|
183
|
+
/** A flag of a send, checked: `undefined` and `null` are `false`. */
|
|
184
|
+
function requireFlag(field, value) {
|
|
185
|
+
if (value === undefined || value === null)
|
|
186
|
+
return false;
|
|
187
|
+
if (typeof value === 'boolean')
|
|
188
|
+
return value;
|
|
189
|
+
throw new TypeError(`The ${field} was refused: give it as true or false, or leave it out`);
|
|
190
|
+
}
|
|
191
|
+
/** Retired keys, checked: a list of keys of 32 bytes or more. The message holds none of them. */
|
|
192
|
+
function requireRetiredKeys(value) {
|
|
193
|
+
if (Array.isArray(value) && value.every((key) => node_util_1.types.isUint8Array(key) && key.byteLength >= KEY_BYTES))
|
|
194
|
+
return value;
|
|
195
|
+
throw new TypeError(`The unsubscribe retiredKeys were refused: give a list of keys of ${KEY_BYTES} bytes or more`);
|
|
196
|
+
}
|
|
197
|
+
/** The default name of an address on the record: its SHA-256 in lower-case hexadecimal. */
|
|
198
|
+
function sha256Of(address) {
|
|
199
|
+
return (0, node_crypto_1.createHash)('sha256').update(address, 'utf8').digest('hex');
|
|
200
|
+
}
|
|
201
|
+
/** Text written into HTML, its five special characters as references. */
|
|
202
|
+
function escapeHtml(text) {
|
|
203
|
+
return text.replace(/[&<>"']/g, (character) => `&#${character.charCodeAt(0)};`);
|
|
204
|
+
}
|
|
205
|
+
/** What goes between a part's text and a footer: a blank line, or nothing when the part is empty. */
|
|
206
|
+
function breakAfter(text) {
|
|
207
|
+
if (text === '')
|
|
208
|
+
return '';
|
|
209
|
+
return text.endsWith('\n') ? '\n' : '\n\n';
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Where a script's content and its end tag stop, given where its content starts, or `-1` when the part ends first. A
|
|
213
|
+
* comment in a script that opens another script holds the next end tag as text, until that comment ends.
|
|
214
|
+
*/
|
|
215
|
+
function endOfScript(html, from) {
|
|
216
|
+
const marks = new RegExp(SCRIPT_MARKS);
|
|
217
|
+
marks.lastIndex = from;
|
|
218
|
+
let state = 'script';
|
|
219
|
+
for (let mark = marks.exec(html); mark !== null; mark = marks.exec(html)) {
|
|
220
|
+
const found = mark[0].toLowerCase();
|
|
221
|
+
if (found.startsWith('<!--')) {
|
|
222
|
+
if (found.endsWith('>'))
|
|
223
|
+
state = 'script';
|
|
224
|
+
else if (state === 'script')
|
|
225
|
+
state = 'comment';
|
|
226
|
+
}
|
|
227
|
+
else if (found === '-->') {
|
|
228
|
+
state = 'script';
|
|
229
|
+
}
|
|
230
|
+
else if (found === '<script') {
|
|
231
|
+
if (state === 'comment')
|
|
232
|
+
state = 'nested';
|
|
233
|
+
}
|
|
234
|
+
else if (state === 'nested') {
|
|
235
|
+
state = 'comment';
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
const rest = new RegExp(END_TAG_REST);
|
|
239
|
+
rest.lastIndex = marks.lastIndex;
|
|
240
|
+
const tail = rest.exec(html);
|
|
241
|
+
return tail === null || tail.groups?.open !== undefined ? -1 : rest.lastIndex;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return -1;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* `html` with `footer` before its last closing body tag that is a tag, not text in a comment, an attribute's value or
|
|
248
|
+
* an element whose content is text, nor inside a template, which a browser never shows. With none it goes at the part's
|
|
249
|
+
* end, or, since they would hide it, before a template or anything else the part leaves open there.
|
|
250
|
+
*/
|
|
251
|
+
function atFoot(html, footer) {
|
|
252
|
+
const spans = new RegExp(HTML_SPANS);
|
|
253
|
+
let closingBody = -1;
|
|
254
|
+
let leftOpen = -1;
|
|
255
|
+
let templates = 0;
|
|
256
|
+
let outerTemplate = -1;
|
|
257
|
+
for (let match = spans.exec(html); match !== null; match = spans.exec(html)) {
|
|
258
|
+
const { slash, name, openComment, openDeclaration, openEnd, openTag, openText } = match.groups ?? {};
|
|
259
|
+
if ((openComment ?? openDeclaration ?? openEnd ?? openTag ?? openText) !== undefined) {
|
|
260
|
+
leftOpen = match.index;
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
const tag = name?.toLowerCase();
|
|
264
|
+
if (tag === 'script' && slash === '') {
|
|
265
|
+
const end = endOfScript(html, spans.lastIndex);
|
|
266
|
+
if (end < 0) {
|
|
267
|
+
leftOpen = match.index;
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
spans.lastIndex = end;
|
|
271
|
+
}
|
|
272
|
+
else if (tag === 'plaintext' && slash === '') {
|
|
273
|
+
// A plaintext holds the rest of the part as text, whatever end tag it seems to hold.
|
|
274
|
+
leftOpen = match.index;
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
else if (tag === 'template') {
|
|
278
|
+
if (slash === '') {
|
|
279
|
+
if (templates === 0)
|
|
280
|
+
outerTemplate = match.index;
|
|
281
|
+
templates += 1;
|
|
282
|
+
}
|
|
283
|
+
else if (templates > 0) {
|
|
284
|
+
templates -= 1;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
else if (tag === 'body' && slash === '/' && templates === 0) {
|
|
288
|
+
closingBody = match.index;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
let place = html.length;
|
|
292
|
+
if (closingBody >= 0)
|
|
293
|
+
place = closingBody;
|
|
294
|
+
else if (templates > 0)
|
|
295
|
+
place = outerTemplate;
|
|
296
|
+
else if (leftOpen >= 0)
|
|
297
|
+
place = leftOpen;
|
|
298
|
+
return `${html.slice(0, place)}${footer}${html.slice(place)}`;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Whether `html` holds `UNSUBSCRIBE_URL_MARKER` where a reader can reach it: in the part's text or in a tag's attribute,
|
|
302
|
+
* such as a link's `href`. A marker in a comment, a doctype, an element whose content is text (a script, a style, a
|
|
303
|
+
* title, a textarea and the like), a template, a plaintext or a tag the part leaves open counts for nothing, since a
|
|
304
|
+
* browser shows none of it as a link.
|
|
305
|
+
*/
|
|
306
|
+
function markerPlaced(html) {
|
|
307
|
+
const hidden = [];
|
|
308
|
+
const spans = new RegExp(HTML_SPANS);
|
|
309
|
+
let templates = 0;
|
|
310
|
+
let outerTemplate = -1;
|
|
311
|
+
for (let match = spans.exec(html); match !== null; match = spans.exec(html)) {
|
|
312
|
+
const { slash, name, openTag } = match.groups ?? {};
|
|
313
|
+
const tag = name?.toLowerCase();
|
|
314
|
+
if (tag === undefined) {
|
|
315
|
+
// A comment, a doctype or bad end tag, or an element whose content is text, to its end.
|
|
316
|
+
hidden.push([match.index, spans.lastIndex]);
|
|
317
|
+
}
|
|
318
|
+
else if (openTag !== undefined) {
|
|
319
|
+
hidden.push([match.index, html.length]);
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
else if (tag === 'script' && slash === '') {
|
|
323
|
+
const end = endOfScript(html, spans.lastIndex);
|
|
324
|
+
hidden.push([match.index, end < 0 ? html.length : end]);
|
|
325
|
+
if (end < 0)
|
|
326
|
+
break;
|
|
327
|
+
spans.lastIndex = end;
|
|
328
|
+
}
|
|
329
|
+
else if (tag === 'plaintext' && slash === '') {
|
|
330
|
+
hidden.push([match.index, html.length]);
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
else if (tag === 'template') {
|
|
334
|
+
if (slash === '') {
|
|
335
|
+
if (templates === 0)
|
|
336
|
+
outerTemplate = match.index;
|
|
337
|
+
templates += 1;
|
|
338
|
+
}
|
|
339
|
+
else if (templates > 0) {
|
|
340
|
+
templates -= 1;
|
|
341
|
+
if (templates === 0)
|
|
342
|
+
hidden.push([outerTemplate, spans.lastIndex]);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (templates > 0)
|
|
347
|
+
hidden.push([outerTemplate, html.length]);
|
|
348
|
+
// The marker holds no `<`, so no span starts inside one: each marker is wholly inside a span or wholly outside.
|
|
349
|
+
for (let at = html.indexOf(exports.UNSUBSCRIBE_URL_MARKER); at >= 0; at = html.indexOf(exports.UNSUBSCRIBE_URL_MARKER, at + 1)) {
|
|
350
|
+
if (!hidden.some(([start, end]) => at >= start && at < end))
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
/** Whether a cc or a bcc names anyone. */
|
|
356
|
+
function names(value) {
|
|
357
|
+
if (value === undefined || value === null || value === '')
|
|
358
|
+
return false;
|
|
359
|
+
return !(Array.isArray(value) && value.length === 0);
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* The one address a recipient names, trimmed and lower-cased: `reader@example.com` or `Reader <reader@example.com>`.
|
|
363
|
+
* A list of addresses, a group, or a display name holding an address is refused with a `TypeError` that holds none of
|
|
364
|
+
* it, since a link names one recipient.
|
|
365
|
+
*
|
|
366
|
+
* @param to The recipient as the send gives it.
|
|
367
|
+
*/
|
|
368
|
+
function oneRecipient(to) {
|
|
369
|
+
if (typeof to === 'string') {
|
|
370
|
+
const text = to.trim();
|
|
371
|
+
const angle = /^([^<>]*)<([^<>]*)>$/.exec(text);
|
|
372
|
+
// A quoted display name may hold a comma or a colon; outside quotes either would start another address or a group.
|
|
373
|
+
const shown = angle === null ? '' : (angle[1] ?? '').replace(/"(?:[^"\\]|\\.)*"/g, '');
|
|
374
|
+
const address = (angle === null ? text : (angle[2] ?? '')).trim();
|
|
375
|
+
if (!/[@,;:"]/.test(shown) && ADDRESS.test(address))
|
|
376
|
+
return address.toLowerCase();
|
|
377
|
+
}
|
|
378
|
+
throw new TypeError('The recipient was refused: with unsubscribe links on, give one address, such as reader@example.com or Reader <reader@example.com>');
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Refuses a cc or a bcc while unsubscribe links are on: a link names one recipient, so each person gets a message of
|
|
382
|
+
* their own.
|
|
383
|
+
*/
|
|
384
|
+
function refuseCopies(cc, bcc) {
|
|
385
|
+
if (names(cc) || names(bcc)) {
|
|
386
|
+
throw new TypeError('The send was refused: with unsubscribe links on, each recipient gets a message of their own, so cc and bcc are not taken');
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/** Refuses the two fields the service writes itself while unsubscribe links are on, in any case. */
|
|
390
|
+
function refuseOwnListFields(headers) {
|
|
391
|
+
for (const name of Object.keys(headers ?? {})) {
|
|
392
|
+
const lower = name.toLowerCase();
|
|
393
|
+
if (lower === 'list-unsubscribe' || lower === 'list-unsubscribe-post') {
|
|
394
|
+
throw new Error(`The header field ${name} was refused: with unsubscribe links on, the service writes it`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* The checked `unsubscribe` configuration of one service: the plan of each send, the gate and the token, and the
|
|
400
|
+
* footer of each part.
|
|
401
|
+
*/
|
|
402
|
+
class Unsubscribe {
|
|
403
|
+
/**
|
|
404
|
+
* Checks every option, and throws a `TypeError` that names the first one at fault and holds none of its value.
|
|
405
|
+
*
|
|
406
|
+
* @param options The configuration's `unsubscribe`.
|
|
407
|
+
*/
|
|
408
|
+
constructor(options) {
|
|
409
|
+
if (typeof options !== 'object' || options === null) {
|
|
410
|
+
throw new TypeError('The unsubscribe configuration was refused: give it as an object');
|
|
411
|
+
}
|
|
412
|
+
this.pageUrl = requirePageUrl(options.pageUrl);
|
|
413
|
+
this.mailto = options.mailto === undefined || options.mailto === null ? undefined : requireMailto(options.mailto);
|
|
414
|
+
this.tokens = requireStore('tokens', options.tokens, ['issue', 'find']);
|
|
415
|
+
this.suppressions = requireStore('suppressions', options.suppressions, ['isSuppressed', 'suppress']);
|
|
416
|
+
const key = options.key;
|
|
417
|
+
if (typeof key !== 'function' && !(node_util_1.types.isUint8Array(key) && key.byteLength >= KEY_BYTES)) {
|
|
418
|
+
throw new TypeError(`The unsubscribe key was refused: give ${KEY_BYTES} bytes or more, such as a Buffer, or a function that answers them`);
|
|
419
|
+
}
|
|
420
|
+
this.key = options.key;
|
|
421
|
+
const retired = options.retiredKeys;
|
|
422
|
+
if (typeof retired === 'function') {
|
|
423
|
+
this.retiredKeys = () => requireRetiredKeys(retired());
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
const listed = requireRetiredKeys(retired ?? []);
|
|
427
|
+
this.retiredKeys = () => listed;
|
|
428
|
+
}
|
|
429
|
+
if (options.addressHash !== undefined && typeof options.addressHash !== 'function') {
|
|
430
|
+
throw new TypeError('The unsubscribe addressHash was refused: give a function or leave it out');
|
|
431
|
+
}
|
|
432
|
+
this.hash = options.addressHash ?? sha256Of;
|
|
433
|
+
this.postalAddress =
|
|
434
|
+
options.postalAddress === undefined || options.postalAddress === null
|
|
435
|
+
? undefined
|
|
436
|
+
: requireLine('postal address', options.postalAddress, 300);
|
|
437
|
+
this.agentCategory = options.agentCategory === undefined ? 'conversation' : requireCategory(options.agentCategory);
|
|
438
|
+
this.linkText = options.linkText === undefined ? 'Unsubscribe' : requireLine('link text', options.linkText, 64);
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* The plan of one send, from its kind: required mail reads the do-not-contact list alone and its link turns off every
|
|
442
|
+
* optional mail; optional mail names its category, reads the do-not-contact list, the provider's record
|
|
443
|
+
* (`DEFAULT_SUPPRESSION_SCOPE`), the list of all optional mail and its category's list, and its link stops the
|
|
444
|
+
* category. Throws before anything is read for a kind that does not fit: optional mail that names no category, a
|
|
445
|
+
* category that is not a lower-case word, required mail marked commercial, or commercial mail with no postal address
|
|
446
|
+
* configured.
|
|
447
|
+
*
|
|
448
|
+
* @param kind What the send says of its mail.
|
|
449
|
+
*/
|
|
450
|
+
plan(kind) {
|
|
451
|
+
const required = requireFlag('required', kind.required);
|
|
452
|
+
const commercial = requireFlag('commercial', kind.commercial);
|
|
453
|
+
if (required && commercial)
|
|
454
|
+
throw new TypeError('The send was refused: commercial mail is never required mail');
|
|
455
|
+
if (commercial && this.postalAddress === undefined) {
|
|
456
|
+
throw new Error("The send was refused: commercial mail carries the sender's postal address; give the unsubscribe configuration a postalAddress");
|
|
457
|
+
}
|
|
458
|
+
if (required) {
|
|
459
|
+
if (kind.category !== undefined && kind.category !== null)
|
|
460
|
+
requireCategory(kind.category);
|
|
461
|
+
return this.replyPlan();
|
|
462
|
+
}
|
|
463
|
+
if (kind.category === undefined || kind.category === null) {
|
|
464
|
+
throw new TypeError('The send was refused: optional mail names its category, which its unsubscribe link stops; mark mail a person cannot turn off as required');
|
|
465
|
+
}
|
|
466
|
+
const scope = categoryScope(kind.category);
|
|
467
|
+
return { reads: [exports.DO_NOT_CONTACT_SCOPE, SuppressionStore_js_1.DEFAULT_SUPPRESSION_SCOPE, exports.OPTIONAL_MAIL_SCOPE, scope], tokenScope: scope, commercial };
|
|
468
|
+
}
|
|
469
|
+
/** The plan of a reply, and of required mail: the do-not-contact list alone, and a link that turns off every optional mail. */
|
|
470
|
+
replyPlan() {
|
|
471
|
+
return { reads: [exports.DO_NOT_CONTACT_SCOPE], tokenScope: exports.OPTIONAL_MAIL_SCOPE, commercial: false };
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Names the address by `addressHash`, and throws a `TypeError` that holds neither the address nor the name when the
|
|
475
|
+
* name is not 64 lower-case hexadecimal characters, since the route would find no link drawn for it. Then reads each of
|
|
476
|
+
* the plan's lists for the address and throws `EmailSuppressedError` for the first that holds it, then
|
|
477
|
+
* draws the token and answers the link with its two fields. A nonce of the send's own, such as its idempotency key,
|
|
478
|
+
* gives a retry the same token, the one a retired key drew when its first attempt came before a rotation; without one
|
|
479
|
+
* each send draws a new one.
|
|
480
|
+
*
|
|
481
|
+
* @param address The recipient, trimmed and lower-cased, as `oneRecipient` answers it.
|
|
482
|
+
* @param plan The send's plan.
|
|
483
|
+
* @param nonce The send's nonce, or `undefined` for a new one.
|
|
484
|
+
*/
|
|
485
|
+
async prepare(address, plan, nonce) {
|
|
486
|
+
const addressHash = this.nameOf(address);
|
|
487
|
+
for (const scope of plan.reads) {
|
|
488
|
+
if (await this.suppressions.isSuppressed(scope, addressHash))
|
|
489
|
+
throw new EmailSuppressedError(scope);
|
|
490
|
+
}
|
|
491
|
+
const token = (nonce === undefined ? null : await this.drawnBefore(nonce, addressHash, plan.tokenScope)) ?? (await this.draw(nonce, addressHash, plan.tokenScope));
|
|
492
|
+
const url = `${this.pageUrl}?t=${token}`;
|
|
493
|
+
return {
|
|
494
|
+
url,
|
|
495
|
+
headers: {
|
|
496
|
+
'List-Unsubscribe': this.mailto === undefined ? `<${url}>` : `<${url}>, <mailto:${this.mailto}?subject=unsubscribe>`,
|
|
497
|
+
'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click',
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Reads the do-not-contact list for every address a message names as its sender, trimmed and lower-cased, and throws
|
|
503
|
+
* `EmailSuppressedError` when it holds any of them, before anything is drawn or sent: a person on the list gets no
|
|
504
|
+
* reply, whichever of their addresses the reply would go to. An empty list reads nothing.
|
|
505
|
+
*
|
|
506
|
+
* @param addresses The addresses of the message's From, Sender and Reply-To.
|
|
507
|
+
*/
|
|
508
|
+
async refuseDoNotContact(addresses) {
|
|
509
|
+
for (const address of addresses) {
|
|
510
|
+
const text = address.trim().toLowerCase();
|
|
511
|
+
if (text === '')
|
|
512
|
+
continue;
|
|
513
|
+
if (await this.suppressions.isSuppressed(exports.DO_NOT_CONTACT_SCOPE, this.nameOf(text)))
|
|
514
|
+
throw new EmailSuppressedError(exports.DO_NOT_CONTACT_SCOPE);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
/** The record's name for an address by `addressHash`, refused when the route could not read it. */
|
|
518
|
+
nameOf(address) {
|
|
519
|
+
const addressHash = this.hash(address);
|
|
520
|
+
// A name the route cannot read would send a link that stops nothing, so it is refused before any list is read.
|
|
521
|
+
if (typeof addressHash !== 'string' || !exports.ADDRESS_HASH.test(addressHash)) {
|
|
522
|
+
throw new TypeError("The unsubscribe addressHash answered a name that was refused: answer the address's hash as 64 lower-case hexadecimal characters");
|
|
523
|
+
}
|
|
524
|
+
return addressHash;
|
|
525
|
+
}
|
|
526
|
+
/** The token for the nonce under the current key. */
|
|
527
|
+
draw(nonce, addressHash, scope) {
|
|
528
|
+
const key = typeof this.key === 'function' ? this.key() : this.key;
|
|
529
|
+
return this.tokens.issue(addressHash, scope, nonce ?? (0, node_crypto_1.randomUUID)(), key);
|
|
530
|
+
}
|
|
531
|
+
/** The token a retired key drew for this nonce, recipient and scope, or `null` when none did or it no longer works. */
|
|
532
|
+
async drawnBefore(nonce, addressHash, scope) {
|
|
533
|
+
for (const key of this.retiredKeys()) {
|
|
534
|
+
const token = (0, RecipientCapability_js_1.capabilityToken)(nonce, key);
|
|
535
|
+
const grant = await this.tokens.find(token);
|
|
536
|
+
if (grant !== null && grant.recipient === addressHash && grant.scope === scope)
|
|
537
|
+
return token;
|
|
538
|
+
}
|
|
539
|
+
return null;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* The text part with its link: where it holds `UNSUBSCRIBE_URL_MARKER`, the link written in place of each, and the
|
|
543
|
+
* postal address after a blank line when commercial mail does not hold it already; otherwise a blank line, the link's
|
|
544
|
+
* words and the link, and the postal address on the next line when one is configured.
|
|
545
|
+
*
|
|
546
|
+
* @param body The text part as given.
|
|
547
|
+
* @param url The send's link.
|
|
548
|
+
* @param commercial Whether the mail is commercial.
|
|
549
|
+
*/
|
|
550
|
+
text(body, url, commercial) {
|
|
551
|
+
if (body.includes(exports.UNSUBSCRIBE_URL_MARKER)) {
|
|
552
|
+
const written = body.split(exports.UNSUBSCRIBE_URL_MARKER).join(url);
|
|
553
|
+
const postal = this.postalAddress;
|
|
554
|
+
return commercial && postal !== undefined && !written.includes(postal) ? `${written}${breakAfter(written)}${postal}\n` : written;
|
|
555
|
+
}
|
|
556
|
+
const lines = [`${this.linkText}: ${url}`];
|
|
557
|
+
if (this.postalAddress !== undefined)
|
|
558
|
+
lines.push(this.postalAddress);
|
|
559
|
+
return `${body}${breakAfter(body)}${lines.join('\n')}\n`;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* The HTML part with its link: where it holds `UNSUBSCRIBE_URL_MARKER` in its text or a tag's attribute, the link
|
|
563
|
+
* written in place of each, and a paragraph with the postal address before `</body>` when commercial mail does not hold
|
|
564
|
+
* it already; otherwise a paragraph in 13-pixel text before `</body>`, or at the end when the part has none, with the
|
|
565
|
+
* link and the postal address under it when one is configured. A marker only in a comment, a script, a style, a title,
|
|
566
|
+
* a textarea or a template is written as the link too, and the part still gets the paragraph, since no reader sees it.
|
|
567
|
+
*
|
|
568
|
+
* @param html The HTML part as given.
|
|
569
|
+
* @param url The send's link.
|
|
570
|
+
* @param commercial Whether the mail is commercial.
|
|
571
|
+
*/
|
|
572
|
+
html(html, url, commercial) {
|
|
573
|
+
const postal = this.postalAddress === undefined ? undefined : escapeHtml(this.postalAddress);
|
|
574
|
+
const placed = markerPlaced(html);
|
|
575
|
+
const written = html.split(exports.UNSUBSCRIBE_URL_MARKER).join(escapeHtml(url));
|
|
576
|
+
if (placed) {
|
|
577
|
+
return commercial && postal !== undefined && !written.includes(postal) ? atFoot(written, `<p style="${FOOT}">${postal}</p>`) : written;
|
|
578
|
+
}
|
|
579
|
+
const link = `<a href="${escapeHtml(url)}" style="${FOOT_LINK}">${escapeHtml(this.linkText)}</a>`;
|
|
580
|
+
return atFoot(written, `<p style="${FOOT}">${link}${postal === undefined ? '' : `<br>${postal}`}</p>`);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
exports.Unsubscribe = Unsubscribe;
|
|
584
|
+
//# sourceMappingURL=unsubscribe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unsubscribe.js","sourceRoot":"","sources":["../src/unsubscribe.ts"],"names":[],"mappings":"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"}
|