@framers/agentos-ext-channel-email 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +43 -10
  2. package/dist/EmailChannelAdapter.d.ts +3 -1
  3. package/dist/EmailChannelAdapter.d.ts.map +1 -1
  4. package/dist/EmailChannelAdapter.js +3 -1
  5. package/dist/EmailChannelAdapter.js.map +1 -1
  6. package/dist/EmailService.d.ts +85 -16
  7. package/dist/EmailService.d.ts.map +1 -1
  8. package/dist/EmailService.js +176 -100
  9. package/dist/EmailService.js.map +1 -1
  10. package/dist/ResendTransport.d.ts +5 -2
  11. package/dist/ResendTransport.d.ts.map +1 -1
  12. package/dist/ResendTransport.js.map +1 -1
  13. package/dist/messageHeaders.d.ts +50 -10
  14. package/dist/messageHeaders.d.ts.map +1 -1
  15. package/dist/messageHeaders.js +175 -15
  16. package/dist/messageHeaders.js.map +1 -1
  17. package/dist/messageText.d.ts +9 -1
  18. package/dist/messageText.d.ts.map +1 -1
  19. package/dist/messageText.js +17 -1
  20. package/dist/messageText.js.map +1 -1
  21. package/dist/preferences/RecipientCapability.d.ts +16 -11
  22. package/dist/preferences/RecipientCapability.d.ts.map +1 -1
  23. package/dist/preferences/RecipientCapability.js +10 -8
  24. package/dist/preferences/RecipientCapability.js.map +1 -1
  25. package/dist/preferences/SuppressionStore.d.ts +23 -15
  26. package/dist/preferences/SuppressionStore.d.ts.map +1 -1
  27. package/dist/preferences/SuppressionStore.js +20 -12
  28. package/dist/preferences/SuppressionStore.js.map +1 -1
  29. package/dist/preferences/database.d.ts +20 -4
  30. package/dist/preferences/database.d.ts.map +1 -1
  31. package/dist/preferences/database.js +34 -7
  32. package/dist/preferences/database.js.map +1 -1
  33. package/dist/preferences/index.d.ts +2 -1
  34. package/dist/preferences/index.d.ts.map +1 -1
  35. package/dist/preferences/index.js +2 -1
  36. package/dist/preferences/index.js.map +1 -1
  37. package/dist/reads.d.ts +69 -0
  38. package/dist/reads.d.ts.map +1 -0
  39. package/dist/reads.js +167 -0
  40. package/dist/reads.js.map +1 -0
  41. package/dist/tools/extractCodes.d.ts +5 -0
  42. package/dist/tools/extractCodes.d.ts.map +1 -1
  43. package/dist/tools/extractCodes.js +10 -2
  44. package/dist/tools/extractCodes.js.map +1 -1
  45. package/dist/tools/read.d.ts +5 -0
  46. package/dist/tools/read.d.ts.map +1 -1
  47. package/dist/tools/read.js +5 -1
  48. package/dist/tools/read.js.map +1 -1
  49. package/dist/tools/reply.d.ts +2 -1
  50. package/dist/tools/reply.d.ts.map +1 -1
  51. package/dist/tools/reply.js +3 -2
  52. package/dist/tools/reply.js.map +1 -1
  53. package/dist/tools/search.d.ts +4 -0
  54. package/dist/tools/search.d.ts.map +1 -1
  55. package/dist/tools/search.js +8 -1
  56. package/dist/tools/search.js.map +1 -1
  57. package/manifest.json +2 -2
  58. package/package.json +1 -1
package/README.md CHANGED
@@ -79,13 +79,23 @@ await email.sendEmail({
79
79
  });
80
80
  ```
81
81
 
82
- Through Resend the fields go in the request's `headers` ([custom headers](https://resend.com/docs/dashboard/emails/custom-headers)); through SMTP nodemailer writes each into the message. `headers` is a plain object of names and values. A name is printable US-ASCII with no space or colon, the field name of RFC 5322, and a value is a string with no line break or other control character than the tab, so no field can end and start another. Each value is read once, and the fields are sent as read.
82
+ Through Resend the fields go in the request's `headers` ([custom headers](https://resend.com/docs/dashboard/emails/custom-headers)); through SMTP nodemailer writes each into the message as given, folded at its white space. Both transports get the same text, so `headers` takes only what both carry alike:
83
+
84
+ - `headers` is a plain object of names and values, made in this realm or in another, such as a `vm` context. A list, a `Map` or text is refused.
85
+ - A name is printable US-ASCII with no space or colon, the field name of RFC 5322, given once in any case.
86
+ - A value is a string of printable US-ASCII words, with one space or tab between each two and none at either end. An empty or blank value is refused: nodemailer would leave the field out, and Resend would send it empty. So is a character outside US-ASCII, which nodemailer would write as an RFC 2047 encoded word that a structured field such as `List-Unsubscribe` cannot hold; percent-encode a link instead. A line break or any other control character is refused, so no field can end and start another, and so is white space at either end or two white-space characters in a row, which nodemailer can fold into a line of white space alone: obsolete syntax ([RFC 5322 section 4.2](https://www.rfc-editor.org/rfc/rfc5322#section-4.2)) that a message must not be written with.
87
+ - No word may be so long that the name, a colon, a space and the word pass 998 characters, the longest line [RFC 5322 section 2.1.1](https://www.rfc-editor.org/rfc/rfc5322#section-2.1.1) allows: a field folds only at its white space, so a word stays on one line. A `List-Unsubscribe` link holds at most 980 characters.
88
+ - `In-Reply-To` and `References` list message ids, each in angle brackets, such as `<id@example.com>`.
89
+
90
+ Each value is read once, and the fields are sent as read. A value with a double quote, such as a `List-Id` of `"Weekly summary" <weekly.example.com>`, arrives as written through both transports. Two differences stay, and neither changes what a field means: nodemailer writes a name in its own case, such as `List-ID` for `List-Id`, and RFC 5322 makes field names case-insensitive; and it writes `Sender` as the address it parses from the value, the same mailbox. Resend's [custom headers](https://resend.com/docs/dashboard/emails/custom-headers) page and its [send call](https://resend.com/docs/api-reference/emails/send-email) do not say how Resend writes a long field into the message.
83
91
 
84
92
  `headers` adds fields and never replaces the service's own: `From`, `To`, `Cc`, `Bcc`, `Reply-To`, `Subject`, `Date`, `Message-ID`, `MIME-Version` and every `Content-` field are refused, in any case. The recipients, the reply-to and the subject have their own options, and the From comes from the configuration's `from`. A name given twice in different cases is refused too. A refusal throws an `Error` before either transport is used, naming the field, or its place in the object when its name is at fault, and none of its value. The `emailSend` tool and the channel adapter add no header fields.
85
93
 
94
+ The subject goes out on one line through both transports: each line break in it, and each other control character but the tab, is sent as a space, as nodemailer writes a line break in a subject.
95
+
86
96
  ## Suppressions and unsubscribe tokens
87
97
 
88
- Two stores a sender reads, each on the host's SQL database: `SqlSuppressionStore` keeps the addresses not to mail, each named by its SHA-256, and `SqlRecipientCapability` keeps opaque tokens, such as an unsubscribe link's, each naming a recipient and a scope. Both take [`@framers/sql-storage-adapter`](https://www.npmjs.com/package/@framers/sql-storage-adapter)'s adapter on SQLite or PostgreSQL, or any handle with its `run` and `get` (`?` placeholders, positional parameters), so the pack gains no dependency.
98
+ Two stores a sender reads, each on the host's SQL database: `SqlSuppressionStore` keeps the addresses not to mail, each named by a hash of it, such as its SHA-256, and `SqlRecipientCapability` keeps opaque tokens, such as an unsubscribe link's, each naming a recipient and a scope. Both take [`@framers/sql-storage-adapter`](https://www.npmjs.com/package/@framers/sql-storage-adapter)'s adapter on SQLite or PostgreSQL, or any handle with its `run` and `get` (`?` placeholders, positional parameters), so the pack gains no dependency. A handle's `run` answers `changes`, the rows the statement wrote. A handle whose answer holds no such count, because it leaves `changes` out or gives something other than a whole number of 0 or more (the adapter's Capacitor handle gave an object before 0.6.14), still records, issues and revokes, but `suppress` then answers `false` and `revoke` 0, whatever they wrote.
89
99
 
90
100
  ```ts
91
101
  import { createHash } from 'node:crypto';
@@ -97,7 +107,8 @@ import {
97
107
  emailPreferencesDdl,
98
108
  } from '@framers/agentos-ext-channel-email';
99
109
 
100
- const db = await createDatabase({ url: process.env.DATABASE_URL });
110
+ // strict: with DATABASE_URL unset, throw rather than open a local database in its place
111
+ const db = await createDatabase({ url: process.env.DATABASE_URL, strict: true });
101
112
  await db.exec(emailPreferencesDdl('mail')); // mail_suppressions and mail_capabilities
102
113
 
103
114
  // A bounce reported by the provider: the store gets the address's hash, never the address.
@@ -114,17 +125,17 @@ const link = `https://example.com/unsubscribe?t=${token}`;
114
125
  const grant = await tokens.find(token); // { recipient: 'account-1', scope: 'weekly-summary' }, or null
115
126
  ```
116
127
 
117
- `suppress(scope, addressHash, reason, day)` records an address in a scope with its reason and its day (`YYYY-MM-DD`), and no end. The reason is `bounce`, `complaint`, `suppressed` (the provider refused a message because its own list held the address) or `manual`. A second record of the address in the scope writes nothing and keeps the first, so a provider's repeated event is harmless; `suppress` answers `true` only for the call that recorded it, and two hosts recording one address at once leave one row. `isSuppressed(scope, addressHash)` answers for one scope. A scope is one list, and a host that keeps one passes `DEFAULT_SUPPRESSION_SCOPE` (`mail`) everywhere. `addressHash` is the SHA-256 of the address as 64 lower-case hexadecimal characters, which the host computes the same way wherever it reads or writes the record. Any other value, an address among them, throws a `TypeError` that holds none of it, so a mistake stores nothing and is never answered as not suppressed.
128
+ `suppress(scope, addressHash, reason, day)` records an address in a scope with its reason and its day (`YYYY-MM-DD`), and no end: the store has no call that lifts an address, so an operator lifts one by deleting its row through the same handle, as in `await db.run('DELETE FROM mail_suppressions WHERE scope = ? AND address_hash = ?', [DEFAULT_SUPPRESSION_SCOPE, addressHash])` for the example's prefix `mail`. The `?` placeholders are the adapter's, which binds the values given and on PostgreSQL writes `$1` and `$2` in their place. In a console, write the scope and the hash as quoted literals instead: PostgreSQL reads a `?` as an operator and refuses the statement, and the sqlite3 shell binds a `?` to NULL, so the statement deletes nothing. The reason is `bounce`, `complaint`, `suppressed` (the provider refused a message because its own list held the address) or `manual`. A second record of the address in the scope writes nothing and keeps the first, so a provider's repeated event is harmless; `suppress` answers `true` only for the call that recorded it, and two hosts recording one address at once leave one row. `isSuppressed(scope, addressHash)` answers for one scope. A scope is one list, and a host that keeps one passes `DEFAULT_SUPPRESSION_SCOPE` (`mail`) everywhere. `addressHash` is the address's hash as 64 lower-case hexadecimal characters, such as its SHA-256, which the host computes the same way wherever it reads or writes the record. Any other value, an address among them, throws a `TypeError` that holds none of it, so a mistake stores nothing and is never answered as not suppressed. The store takes any 64 lower-case hexadecimal characters, so the host chooses the hash. A plain SHA-256, as in the example, lets anyone who holds the table check an address they guess against it; a host that wants the table to tell nothing of an address it does not already hold computes HMAC-SHA256 under a secret key of its own instead, the same way at every reader and writer. `SUPPRESSION_REASONS` lists the four reasons, and it is frozen, since `suppress` checks a reason against it.
118
129
 
119
- `issue(recipient, scope, nonce, key)` answers base64url of HMAC-SHA256 over the nonce under the key, 43 characters, and stores the token's SHA-256 beside the recipient and the scope: the token carries neither, and the table holds nothing it can be recovered from. The same nonce under the same key gives the same token, so each message takes a nonce of its own, such as its id. Issuing it again for the same recipient and scope writes nothing; for another recipient or scope it throws, and the first token keeps its recipient and scope. The key is 32 bytes or more, given as bytes; a different key gives different tokens for the same nonces. `find(token)` answers the recipient and the scope, or `null` for a token that is malformed, unknown, revoked or past its expiry: it looks the token up by its SHA-256 and compares in constant time. `revoke(recipient)` ends every token of the recipient, in every scope, and answers how many it ended. Each revoked row stays, marked with the time of the revoke, so `issue` refuses a revoked token's nonce for every recipient, scope and expiry: a revoked link never works again and never comes to name someone else. A message that needs a working token after a revoke takes a new nonce, and the token it gets works. A token has no expiry unless `issue`'s options set `expiresAt`, in epoch milliseconds.
130
+ `issue(recipient, scope, nonce, key)` answers base64url of HMAC-SHA256 over the nonce under the key, 43 characters, and stores the token's SHA-256 beside the recipient and the scope: the token carries neither, and the table holds nothing it can be recovered from. The same nonce under the same key gives the same token, so each message takes a nonce of its own, such as its id. Issuing it again for the same recipient, scope and expiry writes nothing; for another recipient, scope or expiry it throws, and the first token keeps all three. The key is 32 random bytes or more, given as bytes and kept secret, such as `crypto.randomBytes(32)` stored with the host's other secrets; the store cannot judge how random a key is. A different key gives different tokens for the same nonces, and the same key the same tokens, whatever the prefix: the prefix keeps two hosts' tables apart, not their tokens. Two hosts on one database that share a key and use one nonce draw one token, and each one's `find` answers its own row for it, so a link one host sent could act on the other host's recipient. Each host takes a key of its own, or nonces no other host uses, such as its own name before each message id. `find(token)` answers the recipient and the scope, or `null` for a token that is malformed, unknown, revoked or past its expiry: it looks the token up by its SHA-256 and compares in constant time. `revoke(recipient)` ends every token of the recipient, in every scope, and answers how many it ended. Each revoked row stays, marked with the time of the revoke, so `issue` refuses a revoked token's nonce for every recipient, scope and expiry: a revoked link never works again and never comes to name someone else. A message that needs a working token after a revoke takes a new nonce, and the token it gets works. A token has no expiry unless `issue`'s options set `expiresAt`, in epoch milliseconds.
120
131
 
121
- `suppressionStoreDdl(prefix)` makes the table `<prefix>_suppressions`, `recipientCapabilityDdl(prefix)` makes `<prefix>_capabilities` (`token_hash`, `recipient`, `scope`, `expires_at`, `revoked_at`) with its index on the recipient, and `emailPreferencesDdl(prefix)` makes both. The prefix is a lower-case letter and at most 39 lower-case letters, digits or underscores, and it keeps two hosts on one database apart. The statements are `CREATE ... IF NOT EXISTS` and run on SQLite and PostgreSQL. A token's row refuses its nonce for as long as it stays, revoked or not. A host that deletes a recipient's rows, by its own foreign key with `ON DELETE CASCADE` or by hand, never issues their nonces again: each message's nonce is one no other message ever had, such as a random id or a row id that is never reused. A host that cannot promise that calls `revoke` with each deletion and keeps the rows. Each store checks every argument before its first statement and lets a database error through as it was thrown. A store made on the handle that the adapter's `transaction` gives its callback writes inside that transaction.
132
+ `suppressionStoreDdl(prefix)` makes the table `<prefix>_suppressions` (`scope`, `address_hash`, `reason`, `suppressed_on`), `recipientCapabilityDdl(prefix)` makes `<prefix>_capabilities` (`token_hash`, `recipient`, `scope`, `expires_at`, `revoked_at`) with its index on the recipient, and `emailPreferencesDdl(prefix)` makes both. The prefix is a lower-case letter and at most 39 lower-case letters, digits or underscores, other than `sqlite` and a prefix beginning `sqlite_`, whose tables would begin `sqlite_`: SQLite keeps those names for its own tables and refuses to create one ([CREATE TABLE](https://www.sqlite.org/lang_createtable.html)). A refused prefix throws a `RangeError` from the DDL functions and both constructors. The prefix keeps two hosts' tables apart on one database. The statements are `CREATE ... IF NOT EXISTS`, so a migration can run them again, and they run on SQLite and PostgreSQL, from one process at a time: PostgreSQL does not promise that two `CREATE TABLE IF NOT EXISTS` of one new table at once both succeed, and one of them can fail on a duplicate key in `pg_type` (pgsql-hackers, 9 October 2018: [the report](https://www.postgresql.org/message-id/93336f06-86c2-71b6-abe3-0d42cc614b2b%40postgrespro.ru) and [the reply](https://www.postgresql.org/message-id/22700.1539093909%40sss.pgh.pa.us)). A host whose processes start together runs them in a migration, such as the adapter's `runSchemaMigrations` (0.7.2 or later), which takes an advisory lock on PostgreSQL. A token's row refuses its nonce for as long as it stays, revoked or not. A host that deletes a recipient's rows, by its own foreign key with `ON DELETE CASCADE` or by hand, never issues their nonces again: each message's nonce is one no other message ever had, such as a random id or a row id that is never reused. A host that cannot promise that calls `revoke` with each deletion and keeps the rows. Each store checks every argument before its first statement. A scope, a recipient and a nonce are strings of 1 to 512 UTF-16 code units (JavaScript's `length`, in which a character past U+FFFF counts two) with no U+0000, which PostgreSQL's text refuses, and no lone surrogate, which UTF-8 cannot encode, so a driver would store something else in its place: any other value throws a `TypeError` that holds none of it, and a name is stored as given on every backend. A database error passes through as it was thrown. A store made on the handle that the adapter's `transaction` gives its callback writes inside that transaction. On a server, open the database as the example does, with the adapter's `strict` option (0.6.15 or later): without it, a `url` that is unset opens a local SQLite database in its place, and the record a sender reads before each send would live there.
122
133
 
123
134
  ## Errors and retries
124
135
 
125
136
  A refusal from Resend throws `EmailApiError`, with `status` (the HTTP status), `type` (Resend's error name, such as `validation_error`, or `unknown` when the answer's body is not JSON, names no error, or names one that is not 1 to 64 letters, digits, `_`, `.`, `:` or `-`) and `retryAfterSeconds` (the answer's `retry-after`, or `null`). Its message names the status and the type and never carries Resend's message, which can quote an address.
126
137
 
127
- `fetch`'s own errors pass through as it throws them: a `TypeError` when the connection fails, and a `TimeoutError` when `timeoutMs` passes before an answer comes. A deadline that passes while an answer's body is being read throws no `TimeoutError`: a refusal then throws `EmailApiError` with `type` `unknown`, and an accepted send resolves with an empty `messageId`. The key is trimmed at each send. A key, an `idempotencyKey` or a `userAgent` that holds a character an HTTP header cannot carry (a line break or another control character other than a tab, or a character above U+00FF) throws an `Error` before any request, and its message names which one and none of its value. `ResendTransport.send` checks a message's `headers` by the rules for names and values in [header fields](#header-fields), though not against the fields the service writes, and throws the same way: a reply through Resend whose message id holds a line break is refused rather than sent in `In-Reply-To` and `References`.
138
+ `fetch`'s own errors pass through as it throws them: a `TypeError` when the connection fails, and a `TimeoutError` when `timeoutMs` passes before an answer comes. A deadline that passes while an answer's body is being read throws no `TimeoutError`: a refusal then throws `EmailApiError` with `type` `unknown`, and an accepted send resolves with an empty `messageId`. The key is trimmed at each send. A key, an `idempotencyKey` or a `userAgent` that holds a character an HTTP header cannot carry (a line break or another control character other than a tab, or a character above U+00FF) throws an `Error` before any request, and its message names which one and none of its value. `ResendTransport.send` checks a message's `headers` by the rules for names and values in [header fields](#header-fields), though not against the fields the service writes, and throws the same way.
128
139
 
129
140
  A send is tried again only on the terms of Resend's [errors](https://resend.com/docs/api-reference/errors) and [rate limit](https://resend.com/docs/api-reference/rate-limit) pages, as `retryWaitSeconds` decides:
130
141
 
@@ -136,7 +147,27 @@ The wait is the answer's `retry-after` when it has one, else as many seconds as
136
147
 
137
148
  ## Reading mail
138
149
 
139
- `readInbox`, `searchEmails`, `extractCodes` and `replyToEmail` read through IMAP: the `imap` account (`host`, `user` and `password`, with `port` and `secure`, by default TLS on port 993), or the SMTP account when `imap` is not given. With Resend, reading needs `imap`. A reply goes out through the transport that sends, threaded with `In-Reply-To` and `References`. The pack's factory reads the account from the secrets `email.imap.host`, `email.imap.user` and `email.imap.password`, or from `IMAP_HOST`, `IMAP_USER` and `IMAP_PASSWORD`.
150
+ `readInbox`, `searchEmails`, `extractCodes` and `replyToEmail` read through IMAP: the `imap` account (`host`, `user` and `password`, with `port` and `secure`, by default TLS on port 993), or the SMTP account when `imap` is not given. With Resend, reading needs `imap`. The pack's factory reads the account from the secrets `email.imap.host`, `email.imap.user` and `email.imap.password`, or from `IMAP_HOST`, `IMAP_USER` and `IMAP_PASSWORD`.
151
+
152
+ `readInbox(folder?, limit?)` gives the last `limit` messages of `folder`, newest first: INBOX and 20 when they are not given. `searchEmails({ query, folder?, since?, limit? })` finds the messages whose subject, body or From holds `query`, ignoring case, received on or after the day `since` names when it is given, and gives the last `limit` it finds (20 by default), newest first. Each search answers UIDs, and the messages are fetched by those UIDs, which need not follow the messages' sequence numbers once a message has been removed ([RFC 3501 section 2.3.1.1](https://www.rfc-editor.org/rfc/rfc3501#section-2.3.1.1)).
153
+
154
+ `limit` is a whole number of at least 1. `since` is an ISO 8601 date, such as `2026-10-15`, alone or followed by a time and an offset, such as `2026-10-15T09:30:00-05:00`. The search finds the messages the server received on the day the date names, as written, or later, the time of day disregarded ([RFC 3501 section 6.4.4](https://www.rfc-editor.org/rfc/rfc3501#section-6.4.4)), on every server: the time and the offset are checked, then disregarded, and imapflow is given the day as text, which it sends as `SINCE` and that day. Given a `Date`, imapflow would search a server that announces `WITHIN` from that moment, to the second ([RFC 5032 section 2](https://www.rfc-editor.org/rfc/rfc5032#section-2)), and any other server from its day in UTC.
155
+
156
+ `limit` and `since` count as not given when they are `undefined` or `null`, and `since` also when it is empty. Any other `limit` or `since` throws an `Error` before any mail is read: the fetch would otherwise name a message past the last one, which a server answers `BAD`, and imapflow leaves a date it cannot read out of the search, which then answers mail of every date. The refused `since` values include a number, text in another form such as `October 15, 2026`, which `Date` reads by guesswork and in the local time zone, and a day its month lacks such as `2026-02-30`, which `Date` reads as 2 March.
157
+
158
+ Each message read carries its `messageId`, the first address of its `from` and of its `to`, its `subject`, `date`, `body`, `flags` and `uid`. `date` is the message's Date field in ISO 8601, or the empty string when it has none or the field does not read as a date, so one such message never fails the read.
159
+
160
+ `extractCodes(messageId, pattern?)` and `replyToEmail(messageId, body, html?)` name an INBOX message by its Message-ID, with or without its angle brackets: `<id@example.com>` and `id@example.com` name the same message. An IMAP search matches every Message-ID that holds the text ([RFC 3501 section 6.4.4](https://www.rfc-editor.org/rfc/rfc3501#section-6.4.4)), so each searches for the id in angle brackets and takes only the message whose own id is that one. `extractCodes` answers no codes for an id that is not one message id or that no message has.
161
+
162
+ `extractCodes` searches the message's plain part and its HTML part, each tag of the HTML (a `<`, at least one character, and the first `>` after it) written as a space, and answers each code once: the matches of `pattern` when it is given, else runs of four to eight digits, then the codes that phrases such as "code is", "OTP:", "is your" or "enter ... to" point to. `pattern` is a JavaScript regular expression, compiled with the `g` flag. A code is each match's first group, or the whole match when that group takes no part in it; an empty one is no code, and a match of empty text moves the search on by one character. A `pattern` that is `undefined`, `null` or empty counts as not given. One that is not a string, or that does not compile, throws an `Error` before any mail is read. The pattern runs over the message for at most 250 ms, timed by Node's `vm` module, which V8 honours at each step back a regular expression takes: one that runs longer, as a pattern that backtracks without end such as `(\d+)+$` can, throws an `Error` naming the limit. The limit holds however long the message, so a slow pattern over a very large message reaches it too. The `vm` module serves as a timer there, not as a sandbox: a regular expression runs no code of its own.
163
+
164
+ A reply goes out through the transport that sends:
165
+
166
+ - to the original's Reply-To, or its From when it names none ([RFC 5322 section 3.6.2](https://www.rfc-editor.org/rfc/rfc5322#section-3.6.2));
167
+ - under the original's subject on one line, with `Re: ` before it unless it starts with `Re:` in any case;
168
+ - threaded as [RFC 5322 section 3.6.4](https://www.rfc-editor.org/rfc/rfc5322#section-3.6.4) has it: `In-Reply-To` names the original, and `References` lists the ids of the original's own References, or the one id of its In-Reply-To when its References lists none, then the original's id. Only fields of those very names are read, each whole however it is folded. An id in them that holds a character outside printable US-ASCII, or that is too long for one line, is left out, so whatever the original holds, both transports send the same reply.
169
+
170
+ `replyToEmail` throws an `Error` that holds none of the id when the id is not one message id of printable US-ASCII with no space, or is too long for one line of `In-Reply-To`, before the original is read; and when no message in INBOX has the id, or the original names no address to reply to, before anything is sent.
140
171
 
141
172
  ## In an agent
142
173
 
@@ -146,7 +177,9 @@ A model names the tools' arguments and the channel's blocks, so `emailSend` and
146
177
 
147
178
  They send each attachment as they checked it: a copy of its own `filename`, `content`, `path` and `contentType`, each read once, with a Buffer's bytes copied. They refuse the same way an attachment that is not an object, one that gives any of those four through a getter or a setter, and one whose `filename` or `contentType` is not a string. Any other property of an attachment is left out.
148
179
 
149
- The subject, the bodies and the message id a reply names must be text. Through SMTP, nodemailer reads the file or fetches the address an object body names into the message, and writes an object subject or message id marked `prepared` into the headers as it is. So `sendEmail` and `replyToEmail`, whoever calls them, throw a `TypeError` that names the field, never its value, before anything is sent on either transport: `subject`, `body` and the reply's `messageId` must be strings, and `html` a string when given (`null` leaves the HTML part out). `emailSend` and `emailReply` answer it as `{ success: false, error }`, and the adapter throws it.
180
+ The subject, the bodies and the message id a reply names must be text. Through SMTP, nodemailer reads the file or fetches the address an object body names into the message, and writes an object subject or message id marked `prepared` into the headers as it is. So `sendEmail` and `replyToEmail`, whoever calls them, throw a `TypeError` that names the field, never its value, before anything is sent on either transport: `subject`, `body` and the reply's `messageId` must be strings, and `html` a string when given (`null` leaves the HTML part out). `emailSend` and `emailReply` answer it as `{ success: false, error }`, and the adapter throws it. A reply whose message id is not one message id, or names no message in INBOX, is answered and thrown the same way, with the `Error` [reading mail](#reading-mail) describes.
181
+
182
+ `emailRead`, `emailSearch` and `emailExtractCodes` answer a `limit`, a `since` or a `pattern` that [reading mail](#reading-mail) refuses as `{ success: false, error }`, before any mail is read, and a pattern that runs past its 250 ms the same way. `emailSearch` gives the last 20 messages it finds.
150
183
 
151
184
  ## API
152
185
 
@@ -161,7 +194,7 @@ The subject, the bodies and the message id a reply names must be text. Through S
161
194
  | `SqlSuppressionStore`, `suppressionStoreDdl(prefix)` | The suppression record on a SQL database, and its table's DDL |
162
195
  | `SqlRecipientCapability`, `recipientCapabilityDdl(prefix)` | Recipient capabilities on a SQL database, and their table's DDL |
163
196
  | `emailPreferencesDdl(prefix)` | Both tables' DDL |
164
- | `DEFAULT_SUPPRESSION_SCOPE`, `SUPPRESSION_REASONS` | The scope of a host that keeps one list (`mail`), and the reasons an address is suppressed |
197
+ | `DEFAULT_SUPPRESSION_SCOPE`, `SUPPRESSION_REASONS` | The scope of a host that keeps one list (`mail`), and the reasons an address is suppressed, frozen |
165
198
  | `EmailConfig`, `SendEmailOptions`, `EmailMessage`, `SearchEmailOptions`, `ResendOptions`, `OutgoingEmail`, `OutgoingAttachment`, `EmailChannelOptions`, `ExtensionContext`, `ExtensionPack` | The types of the configuration, the messages, the options and the pack |
166
199
  | `SuppressionStore`, `SuppressionReason`, `RecipientCapability`, `RecipientGrant`, `RecipientCapabilityIssueOptions`, `SqlRecipientCapabilityOptions`, `EmailPreferencesDb` | The stores' interfaces, their answers and options, and what they need of a database |
167
200
 
@@ -49,7 +49,9 @@ export declare class EmailChannelAdapter {
49
49
  * filename before anything is sent, a reply included; what is sent is the copy `requireAgentAttachments` made of
50
50
  * each attachment, so a Buffer is sent as the bytes it held then. A text block's `text`, an HTML block's `html` (or
51
51
  * its `text`), the `subject` in `platformOptions` and `replyToMessageId` must be strings when given: otherwise the
52
- * service throws a `TypeError` naming the field before anything is sent.
52
+ * service throws a `TypeError` naming the field before anything is sent. A reply goes to the original's Reply-To, or
53
+ * its From, as `EmailService.replyToEmail` sends it, and a `replyToMessageId` that is not one message id, or that no
54
+ * message in the inbox has, throws an `Error` before anything is sent.
53
55
  */
54
56
  sendMessage(conversationId: string, content: MessageContent): Promise<ChannelSendResult>;
55
57
  sendTypingIndicator(_conversationId: string, _isTyping: boolean): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"EmailChannelAdapter.d.ts","sourceRoot":"","sources":["../src/EmailChannelAdapter.ts"],"names":[],"mappings":"AACA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC;AACrC,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACvC,MAAM,MAAM,uBAAuB,GAAG,cAAc,GAAG,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,OAAO,CAAC;AAC7G,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEtC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,eAAe,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,uBAAuB,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC,CAAC;IACpD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEvE,qBAAa,mBAAmB;IAC9B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAW;IAC7C,QAAQ,CAAC,WAAW,WAAW;IAC/B,QAAQ,CAAC,YAAY,EAAE,SAAS,iBAAiB,EAAE,CAEjD;IAEF,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,QAAQ,CAAuC;IACvD,OAAO,CAAC,YAAY,CAAuB;gBAE/B,OAAO,EAAE,YAAY;IAI3B,UAAU,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAWnD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAK/B,iBAAiB,IAAI,qBAAqB;IAc1C;;;;;;;;OAQG;IACG,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAyCxF,mBAAmB,CAAC,eAAe,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrF,EAAE,CAAC,OAAO,EAAE,mBAAmB,EAAE,WAAW,CAAC,EAAE,gBAAgB,EAAE,GAAG,MAAM,IAAI;CAI/E"}
1
+ {"version":3,"file":"EmailChannelAdapter.d.ts","sourceRoot":"","sources":["../src/EmailChannelAdapter.ts"],"names":[],"mappings":"AACA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC;AACrC,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACvC,MAAM,MAAM,uBAAuB,GAAG,cAAc,GAAG,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,OAAO,CAAC;AAC7G,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEtC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,eAAe,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,uBAAuB,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC,CAAC;IACpD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEvE,qBAAa,mBAAmB;IAC9B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAW;IAC7C,QAAQ,CAAC,WAAW,WAAW;IAC/B,QAAQ,CAAC,YAAY,EAAE,SAAS,iBAAiB,EAAE,CAEjD;IAEF,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,QAAQ,CAAuC;IACvD,OAAO,CAAC,YAAY,CAAuB;gBAE/B,OAAO,EAAE,YAAY;IAI3B,UAAU,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAWnD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAK/B,iBAAiB,IAAI,qBAAqB;IAc1C;;;;;;;;;;OAUG;IACG,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAyCxF,mBAAmB,CAAC,eAAe,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrF,EAAE,CAAC,OAAO,EAAE,mBAAmB,EAAE,WAAW,CAAC,EAAE,gBAAgB,EAAE,GAAG,MAAM,IAAI;CAI/E"}
@@ -53,7 +53,9 @@ class EmailChannelAdapter {
53
53
  * filename before anything is sent, a reply included; what is sent is the copy `requireAgentAttachments` made of
54
54
  * each attachment, so a Buffer is sent as the bytes it held then. A text block's `text`, an HTML block's `html` (or
55
55
  * its `text`), the `subject` in `platformOptions` and `replyToMessageId` must be strings when given: otherwise the
56
- * service throws a `TypeError` naming the field before anything is sent.
56
+ * service throws a `TypeError` naming the field before anything is sent. A reply goes to the original's Reply-To, or
57
+ * its From, as `EmailService.replyToEmail` sends it, and a `replyToMessageId` that is not one message id, or that no
58
+ * message in the inbox has, throws an `Error` before anything is sent.
57
59
  */
58
60
  async sendMessage(conversationId, content) {
59
61
  const textBlock = content.blocks.find((b) => b.type === 'text');
@@ -1 +1 @@
1
- {"version":3,"file":"EmailChannelAdapter.js","sourceRoot":"","sources":["../src/EmailChannelAdapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"EmailChannelAdapter.js","sourceRoot":"","sources":["../src/EmailChannelAdapter.ts"],"names":[],"mappings":"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"}
@@ -45,28 +45,48 @@ export interface SendEmailOptions {
45
45
  idempotencyKey?: string;
46
46
  /**
47
47
  * Header fields added to the message, by name, such as `List-Unsubscribe` and `List-Unsubscribe-Post`: through Resend
48
- * the request's `headers`, through SMTP fields nodemailer writes into the message. A name is printable US-ASCII with no
49
- * space or colon, given once in any case, and a value is a string with no line break or other control character than
50
- * the tab. A field the service writes itself is refused, in any case: `From`, `To`, `Cc`, `Bcc`, `Reply-To`, `Subject`,
51
- * `Date`, `Message-ID`, `MIME-Version` and any `Content-` field.
48
+ * the request's `headers`, through SMTP fields nodemailer writes into the message as given. A name is printable
49
+ * US-ASCII with no space or colon, given once in any case. A value is a string of printable US-ASCII words, one space
50
+ * or tab between each two and none at either end, with no word so long that the name, a colon, a space and the word
51
+ * pass 998 characters, RFC 5322's longest line; `In-Reply-To` and `References` list message ids, each in angle
52
+ * brackets. A field the service writes itself is refused, in any case: `From`, `To`, `Cc`, `Bcc`, `Reply-To`,
53
+ * `Subject`, `Date`, `Message-ID`, `MIME-Version` and any `Content-` field.
52
54
  */
53
55
  headers?: Record<string, string>;
54
56
  }
57
+ /** A message as `readInbox` and `searchEmails` give it. */
55
58
  export interface EmailMessage {
59
+ /** Its Message-ID field as the server's envelope gives it, or the empty string when it has none. */
56
60
  messageId: string;
61
+ /** The first address of its From, or the empty string. */
57
62
  from: string;
63
+ /** The first address of its To, or the empty string. */
58
64
  to: string;
59
65
  subject: string;
66
+ /** Its Date field in ISO 8601, or the empty string when it has none or the field does not read as a date. */
60
67
  date: string;
61
68
  body: string;
62
69
  html?: string;
63
70
  flags: string[];
64
71
  uid: number;
65
72
  }
73
+ /** What `searchEmails` searches for. */
66
74
  export interface SearchEmailOptions {
75
+ /** Text the subject, the body or the From holds, ignoring case; every message when empty. */
67
76
  query: string;
77
+ /** The folder searched; INBOX when not given. */
68
78
  folder?: string;
79
+ /**
80
+ * The day to search from, as an ISO 8601 date such as `2026-10-15`, alone or followed by a time and an offset such as
81
+ * `2026-10-15T09:30:00-05:00`: messages the server received on the day the date names, as written, or later, the time
82
+ * and the offset disregarded, on every server. No bound when not given, `null` or empty. Any other value, a day its
83
+ * month lacks such as `2026-02-30` among them, throws an `Error` before the search.
84
+ */
69
85
  since?: string;
86
+ /**
87
+ * The most messages given, a whole number of at least 1; 20 when not given or `null`. Any other value throws an `Error`
88
+ * before the search.
89
+ */
70
90
  limit?: number;
71
91
  }
72
92
  export declare class EmailService {
@@ -98,31 +118,80 @@ export declare class EmailService {
98
118
  * `subject` and `body` must be strings, and `html` a string when it is given (`null` sends no HTML part, as
99
119
  * `undefined` does): any other value throws a `TypeError` naming the field, never its value, before either transport
100
120
  * is used. An object body would have nodemailer read the file its `path` names or fetch the address its `href` names
101
- * into the message, and an object subject marked `prepared` would be written into the headers as it is.
121
+ * into the message, and an object subject marked `prepared` would be written into the headers as it is. The subject
122
+ * goes out on one line through either transport: each line break in it, and each other control character but the
123
+ * tab, is sent as a space, as nodemailer writes a line break in a subject.
102
124
  *
103
125
  * `headers` adds fields to the message and never replaces one the service writes. It must be a plain object of names
104
- * and values: a name printable US-ASCII with no space or colon, given once in any case, and none of `From`, `To`,
105
- * `Cc`, `Bcc`, `Reply-To`, `Subject`, `Date`, `Message-ID`, `MIME-Version` or a `Content-` field, in any case; a value
106
- * a string with no line break or other control character than the tab. Anything else throws an `Error` that names the
107
- * field, or its place when its name is at fault, and none of its value, before either transport is used. Each value is
108
- * read once, and the fields are sent as read.
126
+ * and values, made in this realm or another: a name printable US-ASCII with no space or colon, given once in any case,
127
+ * and none of `From`, `To`, `Cc`, `Bcc`, `Reply-To`, `Subject`, `Date`, `Message-ID`, `MIME-Version` or a `Content-`
128
+ * field, in any case; a value a string of printable US-ASCII words, one space or tab between each two and none at
129
+ * either end, with no word so long that the name, a colon, a space and the word pass 998 characters, and for
130
+ * `In-Reply-To` and `References` message ids, each in angle brackets. Anything else throws an `Error` that names the
131
+ * field, or its place when its name is at fault, and none of its value, before either transport is used. Each value
132
+ * is read once, and both transports send the fields as read: nodemailer writes each value as given, folded at its
133
+ * white space, except `Sender`, which it writes as the address it parses from the value.
109
134
  */
110
135
  sendEmail(options: SendEmailOptions): Promise<{
111
136
  messageId: string;
112
137
  }>;
138
+ /**
139
+ * The last `limit` messages of `folder`, newest first: INBOX and 20 when they are not given, and 20 when `limit` is
140
+ * `null`. `limit` must be a whole number of at least 1: any other value throws an `Error` before any mail is read,
141
+ * where the fetch would name a message past the last one, which a server answers BAD. A message whose Date field does
142
+ * not read as a date is given with an empty `date`.
143
+ */
113
144
  readInbox(folder?: string, limit?: number): Promise<EmailMessage[]>;
145
+ /**
146
+ * The messages of `folder` (INBOX unless given) whose subject, body or From holds `query`, ignoring case, received on
147
+ * or after `since` when it is given: the last `limit` (20 unless given) the search finds, newest first. The search
148
+ * answers UIDs, and the messages are fetched by those UIDs, which need not follow the messages' sequence numbers.
149
+ *
150
+ * `limit` must be a whole number of at least 1, and `since` an ISO 8601 date, such as `2026-10-15`, alone or followed
151
+ * by a time and an offset, which are checked and then disregarded: the search starts from the day the date names, as
152
+ * written, on every server. Either counts as not given when `null`, and `since` when empty. Any other value of either,
153
+ * a day its month lacks such as `2026-02-30` among them, throws an `Error` before the search: imapflow leaves a date it
154
+ * cannot read out of the search, which would answer mail of every date, and `Date` rolls a day past its month's end
155
+ * over into the next month. A message whose Date field does not read as a date is given with an empty `date`.
156
+ */
114
157
  searchEmails(options: SearchEmailOptions): Promise<EmailMessage[]>;
158
+ /**
159
+ * The codes in the INBOX message whose Message-ID is `messageId`, given with or without its angle brackets: the
160
+ * matches of `pattern` when it is given, else runs of four to eight digits, then the codes that phrases such as "code
161
+ * is", "OTP:", "is your" or "enter ... to" point to, each once. The message is searched for by the id in angle brackets
162
+ * and fetched by the UID the search answers; a search matches every Message-ID that holds the text, so only the
163
+ * message whose own id is the one given counts. An id that is not one message id, or that no message has, gives no
164
+ * codes. The text searched is the plain part and the HTML part, each tag of the HTML written as a space.
165
+ *
166
+ * `pattern` is a JavaScript regular expression, compiled with the `g` flag: each match's first group is a code, or the
167
+ * whole match when that group takes no part in it, and an empty one is none; a match of empty text moves the search
168
+ * on by one character. A pattern that is `null` or empty counts as not given. One that is not a string, or that does
169
+ * not compile, throws an `Error` before any mail is read. The pattern runs over the message for at most 250 ms, and
170
+ * one that runs longer, as one that backtracks without end such as `(\d+)+$` can, throws an `Error` naming that limit.
171
+ */
115
172
  extractCodes(messageId: string, pattern?: string): Promise<{
116
173
  codes: string[];
117
174
  messageId: string;
118
175
  }>;
119
176
  /**
120
- * Replies to the message whose Message-ID is `messageId`, reading its sender, subject and References through IMAP,
121
- * and sends `body` with `html`, when given, through the transport that sends, threaded with `In-Reply-To` and
122
- * `References`. `messageId` and `body` must be strings, and `html` a string when it is given (`null` sends no HTML
123
- * part): any other value throws a `TypeError` naming the field, never its value, before the original is read or
124
- * anything is sent. Besides the bodies' files and addresses (see `sendEmail`), an object message id marked `prepared`
125
- * would be written into the `In-Reply-To` header as it is.
177
+ * Replies to the INBOX message whose Message-ID is `messageId`, given with or without its angle brackets, and sends
178
+ * `body` with `html`, when given, through the transport that sends. The original is searched for by the id in angle
179
+ * brackets and fetched by the UID the search answers; a search matches every Message-ID that holds the text, so the
180
+ * original is the message whose own id is the one given. The reply goes to the original's Reply-To, or its From when
181
+ * it names none (RFC 5322 section 3.6.2), under its subject with one `Re: ` before it unless it starts with one in any
182
+ * case, on one line. It is threaded as RFC 5322 section 3.6.4 has it: `In-Reply-To` names the original, and
183
+ * `References` lists the ids of the original's own References, or the one id of its In-Reply-To when its References
184
+ * lists none, then the original's id. Only fields of those very names are read, each whole however it is folded, and
185
+ * an id in them that holds a character outside printable US-ASCII, or that is too long for one line, is left out, so
186
+ * both transports send the same fields whatever the original holds.
187
+ *
188
+ * `messageId` and `body` must be strings, and `html` a string when it is given (`null` sends no HTML part): any other
189
+ * value throws a `TypeError` naming the field, never its value, before the original is read or anything is sent.
190
+ * Besides the bodies' files and addresses (see `sendEmail`), an object message id marked `prepared` would be written
191
+ * into the `In-Reply-To` header as it is. A message id that is not one id of printable US-ASCII with no space, or that
192
+ * is too long for one line of `In-Reply-To`, throws an `Error` before the original is read; no message with that id,
193
+ * or an original that names no address to reply to, throws an `Error` before anything is sent. None of these errors
194
+ * holds the id.
126
195
  */
127
196
  replyToEmail(messageId: string, body: string, html?: string): Promise<{
128
197
  messageId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"EmailService.d.ts","sourceRoot":"","sources":["../src/EmailService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"EmailService.d.ts","sourceRoot":"","sources":["../src/EmailService.ts"],"names":[],"mappings":"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"}