@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
@@ -1,11 +1,14 @@
1
1
  /**
2
- * @fileoverview The suppression record: the addresses a host must not mail, by scope, each named by the SHA-256 of the
3
- * address that the host computes, so the record never holds an address. A host's senders read it before a send, and
2
+ * @fileoverview The suppression record: the addresses a host must not mail, by scope, each named by a hash of the
3
+ * address that the host computes, such as its SHA-256, so the record never holds an address. A host's senders read it before a send, and
4
4
  * the handler of its provider's events writes it when a message bounces, draws a complaint or meets the provider's own
5
5
  * suppression list. A second record of an address writes nothing, so a provider's repeated event is harmless.
6
6
  */
7
7
  import type { EmailPreferencesDb } from './database.js';
8
- /** The reasons an address is suppressed, in the order the README lists them. */
8
+ /**
9
+ * The reasons an address is suppressed, in the order the README lists them. The store checks a reason against this
10
+ * list, so it is frozen: no caller can widen it or empty it.
11
+ */
9
12
  export declare const SUPPRESSION_REASONS: readonly ["bounce", "complaint", "suppressed", "manual"];
10
13
  /**
11
14
  * Why an address is suppressed: `bounce` (a message to it bounced), `complaint` (its reader reported a message as
@@ -17,37 +20,42 @@ export type SuppressionReason = (typeof SUPPRESSION_REASONS)[number];
17
20
  export declare const DEFAULT_SUPPRESSION_SCOPE = "mail";
18
21
  /**
19
22
  * The addresses a host must not mail, by scope. A scope is one list, such as all of a host's mail or one sending
20
- * domain's, and most hosts keep one, `DEFAULT_SUPPRESSION_SCOPE`. An address is named by its hash, the SHA-256 of the
21
- * address as 64 lower-case hexadecimal characters, which the host computes the same way wherever it reads or writes the
22
- * record, such as over the address lower-cased. Anything else is refused with a `TypeError`, so an address passed by
23
- * mistake is never stored and never answered as not suppressed.
23
+ * domain's, and most hosts keep one, `DEFAULT_SUPPRESSION_SCOPE`. An address is named by its hash, 64 lower-case
24
+ * hexadecimal characters, which the host computes the same way wherever it reads or writes the record, such as the
25
+ * SHA-256 of the address lower-cased. Anything else is refused with a `TypeError`, so an address passed by mistake is
26
+ * never stored and never answered as not suppressed. A plain SHA-256 lets anyone who holds the table check an address
27
+ * they guess; a host that wants the table to tell nothing of an address it does not already hold computes HMAC-SHA256
28
+ * under a key of its own instead.
24
29
  */
25
30
  export interface SuppressionStore {
26
31
  /**
27
32
  * Whether the address is suppressed in the scope.
28
33
  *
29
- * @param scope The list, 1 to 512 characters.
30
- * @param addressHash The SHA-256 of the address as 64 lower-case hexadecimal characters.
34
+ * @param scope The list: 1 to 512 UTF-16 code units, with no U+0000 and no lone surrogate.
35
+ * @param addressHash The address's hash as 64 lower-case hexadecimal characters.
31
36
  */
32
37
  isSuppressed(scope: string, addressHash: string): Promise<boolean>;
33
38
  /**
34
39
  * Records the address as suppressed in the scope, with the reason and the day, and no end. A second record of the
35
40
  * address in the scope writes nothing and leaves the first as it was, whatever its reason or day.
36
41
  *
37
- * @param scope The list, 1 to 512 characters.
38
- * @param addressHash The SHA-256 of the address as 64 lower-case hexadecimal characters.
42
+ * @param scope The list: 1 to 512 UTF-16 code units, with no U+0000 and no lone surrogate.
43
+ * @param addressHash The address's hash as 64 lower-case hexadecimal characters.
39
44
  * @param reason Why the address is suppressed.
40
45
  * @param day The calendar day of the event, `YYYY-MM-DD`.
41
- * @returns `true` when this call recorded the address, `false` when the scope held it already.
46
+ * @returns `true` when this call recorded the address, `false` when the scope held it already; `false` whatever the
47
+ * call wrote when the database's `run` answers no count of `changes`.
42
48
  */
43
49
  suppress(scope: string, addressHash: string, reason: SuppressionReason, day: string): Promise<boolean>;
44
50
  }
45
51
  /**
46
52
  * The suppression record's table, `<prefix>_suppressions`, for a host's migration: one row an address and scope, with
47
- * the reason and the day, `CREATE TABLE IF NOT EXISTS` so it can run at each start. It runs on SQLite and on
48
- * PostgreSQL.
53
+ * the reason and the day, `CREATE TABLE IF NOT EXISTS` so a migration can run it again. It runs on SQLite and on
54
+ * PostgreSQL, from one process at a time: PostgreSQL does not promise that two processes making one new table at once
55
+ * both succeed.
49
56
  *
50
- * @param prefix A lower-case letter followed by at most 39 lower-case letters, digits or underscores.
57
+ * @param prefix A lower-case letter followed by at most 39 lower-case letters, digits or underscores, other than
58
+ * `sqlite` and a prefix beginning `sqlite_`.
51
59
  * @throws RangeError when the prefix does not fit.
52
60
  */
53
61
  export declare function suppressionStoreDdl(prefix: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"SuppressionStore.d.ts","sourceRoot":"","sources":["../../src/preferences/SuppressionStore.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGxD,gFAAgF;AAChF,eAAO,MAAM,mBAAmB,0DAA2D,CAAC;AAE5F;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAErE,gGAAgG;AAChG,eAAO,MAAM,yBAAyB,SAAS,CAAC;AAEhD;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEnE;;;;;;;;;OASG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACxG;AAwCD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAW1D;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,YAAW,gBAAgB;IASxD,OAAO,CAAC,QAAQ,CAAC,EAAE;IARrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAE/B;;;;OAIG;gBAEgB,EAAE,EAAE,kBAAkB,EACvC,MAAM,EAAE,MAAM;IAKV,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASlE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAS7G"}
1
+ {"version":3,"file":"SuppressionStore.d.ts","sourceRoot":"","sources":["../../src/preferences/SuppressionStore.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGxD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,0DAA0E,CAAC;AAE3G;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAErE,gGAAgG;AAChG,eAAO,MAAM,yBAAyB,SAAS,CAAC;AAEhD;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEnE;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACxG;AAwCD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAW1D;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,YAAW,gBAAgB;IASxD,OAAO,CAAC,QAAQ,CAAC,EAAE;IARrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAE/B;;;;OAIG;gBAEgB,EAAE,EAAE,kBAAkB,EACvC,MAAM,EAAE,MAAM;IAKV,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASlE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAS7G"}
@@ -3,11 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SqlSuppressionStore = exports.DEFAULT_SUPPRESSION_SCOPE = exports.SUPPRESSION_REASONS = void 0;
4
4
  exports.suppressionStoreDdl = suppressionStoreDdl;
5
5
  const database_js_1 = require("./database.js");
6
- /** The reasons an address is suppressed, in the order the README lists them. */
7
- exports.SUPPRESSION_REASONS = ['bounce', 'complaint', 'suppressed', 'manual'];
6
+ /**
7
+ * The reasons an address is suppressed, in the order the README lists them. The store checks a reason against this
8
+ * list, so it is frozen: no caller can widen it or empty it.
9
+ */
10
+ exports.SUPPRESSION_REASONS = Object.freeze(['bounce', 'complaint', 'suppressed', 'manual']);
8
11
  /** The scope of a host that keeps one list: every reader and writer of the record passes it. */
9
12
  exports.DEFAULT_SUPPRESSION_SCOPE = 'mail';
10
- /** The SHA-256 of an address in lower-case hexadecimal. */
13
+ /** An address's hash, such as its SHA-256, in lower-case hexadecimal. */
11
14
  const ADDRESS_HASH = /^[0-9a-f]{64}$/;
12
15
  /** A calendar day, `YYYY-MM-DD`. */
13
16
  const DAY = /^(\d{4})-(\d{2})-(\d{2})$/;
@@ -18,7 +21,7 @@ const DAY = /^(\d{4})-(\d{2})-(\d{2})$/;
18
21
  function requireAddressHash(value) {
19
22
  if (typeof value === 'string' && ADDRESS_HASH.test(value))
20
23
  return value;
21
- throw new TypeError('The address hash was refused: give the SHA-256 of the address as 64 lower-case hexadecimal characters');
24
+ throw new TypeError("The address hash was refused: give the address's hash, such as its SHA-256, as 64 lower-case hexadecimal characters");
22
25
  }
23
26
  /** Returns the reason when it is one of `SUPPRESSION_REASONS`, and otherwise throws a `TypeError`. */
24
27
  function requireReason(value) {
@@ -31,20 +34,25 @@ function requireReason(value) {
31
34
  function requireDay(value) {
32
35
  if (typeof value === 'string') {
33
36
  const parts = DAY.exec(value);
34
- // A date that does not exist, such as the 30th of February, comes back from Date.UTC as another day.
35
- if (parts !== null &&
36
- new Date(Date.UTC(Number(parts[1]), Number(parts[2]) - 1, Number(parts[3]))).toISOString().slice(0, 10) === value) {
37
- return value;
37
+ if (parts !== null) {
38
+ // setUTCFullYear takes the year as written, where Date.UTC reads 0 to 99 as 1900 to 1999. A date that does not
39
+ // exist, such as the 30th of February, comes back as another day.
40
+ const date = new Date(0);
41
+ date.setUTCFullYear(Number(parts[1]), Number(parts[2]) - 1, Number(parts[3]));
42
+ if (date.toISOString().slice(0, 10) === value)
43
+ return value;
38
44
  }
39
45
  }
40
46
  throw new TypeError('The day was refused: give a calendar date as YYYY-MM-DD');
41
47
  }
42
48
  /**
43
49
  * The suppression record's table, `<prefix>_suppressions`, for a host's migration: one row an address and scope, with
44
- * the reason and the day, `CREATE TABLE IF NOT EXISTS` so it can run at each start. It runs on SQLite and on
45
- * PostgreSQL.
50
+ * the reason and the day, `CREATE TABLE IF NOT EXISTS` so a migration can run it again. It runs on SQLite and on
51
+ * PostgreSQL, from one process at a time: PostgreSQL does not promise that two processes making one new table at once
52
+ * both succeed.
46
53
  *
47
- * @param prefix A lower-case letter followed by at most 39 lower-case letters, digits or underscores.
54
+ * @param prefix A lower-case letter followed by at most 39 lower-case letters, digits or underscores, other than
55
+ * `sqlite` and a prefix beginning `sqlite_`.
48
56
  * @throws RangeError when the prefix does not fit.
49
57
  */
50
58
  function suppressionStoreDdl(prefix) {
@@ -84,7 +92,7 @@ class SqlSuppressionStore {
84
92
  const params = [(0, database_js_1.requireName)('scope', scope), requireAddressHash(addressHash), requireReason(reason), requireDay(day)];
85
93
  const result = await this.db.run(`INSERT INTO ${this.table} (scope, address_hash, reason, suppressed_on) VALUES (?, ?, ?, ?) ` +
86
94
  'ON CONFLICT (scope, address_hash) DO NOTHING', params);
87
- return result.changes === 1;
95
+ return (0, database_js_1.changesOf)(result) === 1;
88
96
  }
89
97
  }
90
98
  exports.SqlSuppressionStore = SqlSuppressionStore;
@@ -1 +1 @@
1
- {"version":3,"file":"SuppressionStore.js","sourceRoot":"","sources":["../../src/preferences/SuppressionStore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SuppressionStore.js","sourceRoot":"","sources":["../../src/preferences/SuppressionStore.ts"],"names":[],"mappings":"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"}
@@ -9,23 +9,39 @@
9
9
  * its `transaction` gives a callback, so a store made on that handle writes inside the transaction.
10
10
  */
11
11
  export interface EmailPreferencesDb {
12
- /** Runs one statement; `changes` is the number of rows it inserted, updated or deleted. */
12
+ /**
13
+ * Runs one statement; `changes` is the number of rows it inserted, updated or deleted. A handle whose answer holds no
14
+ * such count, because it leaves `changes` out or gives something other than a whole number of 0 or more, still
15
+ * records, issues and revokes, but then `suppress` answers `false` and `revoke` answers 0 whatever they wrote.
16
+ */
13
17
  run(sql: string, params?: unknown[]): Promise<{
14
18
  changes?: number;
15
19
  }>;
16
20
  /** Runs one query and answers its first row, or `null` (or `undefined`) when it has none. */
17
21
  get<T>(sql: string, params?: unknown[]): Promise<T | null | undefined>;
18
22
  }
23
+ /**
24
+ * The rows a statement wrote, as its handle's `run` answered them: `changes` when it is a whole number of 0 or more, and
25
+ * otherwise 0. An answer that is no count never passes for one, such as text, NaN, -1 or the object the storage
26
+ * adapter's Capacitor handle answered before 0.6.14 (`{ changes: { changes, lastId } }`).
27
+ *
28
+ * @param result What the handle's `run` answered.
29
+ */
30
+ export declare function changesOf(result: {
31
+ readonly changes?: unknown;
32
+ } | null | undefined): number;
19
33
  /**
20
34
  * Returns the prefix when it is a lower-case letter followed by at most 39 lower-case letters, digits or underscores,
21
- * and otherwise throws a `RangeError`. The prefix is written into the SQL as it is, so nothing else passes.
35
+ * other than `sqlite` and a prefix beginning `sqlite_`, and otherwise throws a `RangeError`. The prefix is written into
36
+ * the SQL as it is, so nothing else passes, and every prefix that passes makes tables on SQLite and on PostgreSQL.
22
37
  *
23
38
  * @param prefix The table prefix the caller gave.
24
39
  */
25
40
  export declare function requirePrefix(prefix: unknown): string;
26
41
  /**
27
- * Returns `value` when it is a string of 1 to 512 characters, and otherwise throws a `TypeError` that names `field` and
28
- * never the value.
42
+ * Returns `value` when it is a string of 1 to 512 UTF-16 code units (its `length`, in which a character past U+FFFF
43
+ * counts two) that a text column holds as it is, with no U+0000 and no lone surrogate, and otherwise throws a
44
+ * `TypeError` that names `field` and never the value.
29
45
  *
30
46
  * @param field The argument's name, such as `scope`.
31
47
  * @param value The value the caller gave.
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/preferences/database.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,2FAA2F;IAC3F,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpE,6FAA6F;IAC7F,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CACxE;AAKD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAKrD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAGjE"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/preferences/database.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpE,6FAA6F;IAC7F,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CACxE;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAG3F;AAWD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAMrD;AAUD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAKjE"}
@@ -4,31 +4,58 @@
4
4
  * take. Nothing here imports a database driver: the host passes its own handle.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.changesOf = changesOf;
7
8
  exports.requirePrefix = requirePrefix;
8
9
  exports.requireName = requireName;
10
+ /**
11
+ * The rows a statement wrote, as its handle's `run` answered them: `changes` when it is a whole number of 0 or more, and
12
+ * otherwise 0. An answer that is no count never passes for one, such as text, NaN, -1 or the object the storage
13
+ * adapter's Capacitor handle answered before 0.6.14 (`{ changes: { changes, lastId } }`).
14
+ *
15
+ * @param result What the handle's `run` answered.
16
+ */
17
+ function changesOf(result) {
18
+ const changes = result?.changes;
19
+ return typeof changes === 'number' && Number.isSafeInteger(changes) && changes >= 0 ? changes : 0;
20
+ }
9
21
  /** A table prefix: a lower-case letter, then at most 39 lower-case letters, digits or underscores. */
10
22
  const PREFIX = /^[a-z][a-z0-9_]{0,39}$/;
23
+ /**
24
+ * A prefix whose tables would begin `sqlite_`: SQLite keeps those names for its own tables and refuses to create one
25
+ * (https://www.sqlite.org/lang_createtable.html).
26
+ */
27
+ const SQLITE_OWN = /^sqlite(?:_|$)/;
11
28
  /**
12
29
  * Returns the prefix when it is a lower-case letter followed by at most 39 lower-case letters, digits or underscores,
13
- * and otherwise throws a `RangeError`. The prefix is written into the SQL as it is, so nothing else passes.
30
+ * other than `sqlite` and a prefix beginning `sqlite_`, and otherwise throws a `RangeError`. The prefix is written into
31
+ * the SQL as it is, so nothing else passes, and every prefix that passes makes tables on SQLite and on PostgreSQL.
14
32
  *
15
33
  * @param prefix The table prefix the caller gave.
16
34
  */
17
35
  function requirePrefix(prefix) {
18
- if (typeof prefix === 'string' && PREFIX.test(prefix))
36
+ if (typeof prefix === 'string' && PREFIX.test(prefix) && !SQLITE_OWN.test(prefix))
19
37
  return prefix;
20
- throw new RangeError('The table prefix was refused: give a lower-case letter followed by at most 39 lower-case letters, digits or underscores');
38
+ throw new RangeError('The table prefix was refused: give a lower-case letter followed by at most 39 lower-case letters, digits or ' +
39
+ 'underscores, other than sqlite and a prefix beginning sqlite_, whose tables SQLite keeps for itself');
21
40
  }
22
41
  /**
23
- * Returns `value` when it is a string of 1 to 512 characters, and otherwise throws a `TypeError` that names `field` and
24
- * never the value.
42
+ * What a text column cannot hold as given: U+0000, which PostgreSQL's text refuses, and a lone surrogate, which UTF-8
43
+ * cannot encode, so a driver writes something else in its place (node-postgres writes U+FFFD): the row would name
44
+ * another value, and two values would meet in one. With the `u` flag a surrogate pair is one code point and does not
45
+ * match.
46
+ */
47
+ const UNSTORABLE = /\u0000|[\uD800-\uDFFF]/u;
48
+ /**
49
+ * Returns `value` when it is a string of 1 to 512 UTF-16 code units (its `length`, in which a character past U+FFFF
50
+ * counts two) that a text column holds as it is, with no U+0000 and no lone surrogate, and otherwise throws a
51
+ * `TypeError` that names `field` and never the value.
25
52
  *
26
53
  * @param field The argument's name, such as `scope`.
27
54
  * @param value The value the caller gave.
28
55
  */
29
56
  function requireName(field, value) {
30
- if (typeof value === 'string' && value.length > 0 && value.length <= 512)
57
+ if (typeof value === 'string' && value.length > 0 && value.length <= 512 && !UNSTORABLE.test(value))
31
58
  return value;
32
- throw new TypeError(`The ${field} was refused: give it as a string of 1 to 512 characters`);
59
+ throw new TypeError(`The ${field} was refused: give it as a string of 1 to 512 UTF-16 code units with no U+0000 and no lone surrogate`);
33
60
  }
34
61
  //# sourceMappingURL=database.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/preferences/database.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAwBH,sCAKC;AASD,kCAGC;AA1BD,sGAAsG;AACtG,MAAM,MAAM,GAAG,wBAAwB,CAAC;AAExC;;;;;GAKG;AACH,SAAgB,aAAa,CAAC,MAAe;IAC3C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IACrE,MAAM,IAAI,UAAU,CAClB,yHAAyH,CAC1H,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,WAAW,CAAC,KAAa,EAAE,KAAc;IACvD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,KAAK,CAAC;IACvF,MAAM,IAAI,SAAS,CAAC,OAAO,KAAK,0DAA0D,CAAC,CAAC;AAC9F,CAAC"}
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/preferences/database.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AA0BH,8BAGC;AAkBD,sCAMC;AAkBD,kCAKC;AAzDD;;;;;;GAMG;AACH,SAAgB,SAAS,CAAC,MAAyD;IACjF,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,CAAC;IAChC,OAAO,OAAO,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AACpG,CAAC;AAED,sGAAsG;AACtG,MAAM,MAAM,GAAG,wBAAwB,CAAC;AAExC;;;GAGG;AACH,MAAM,UAAU,GAAG,gBAAgB,CAAC;AAEpC;;;;;;GAMG;AACH,SAAgB,aAAa,CAAC,MAAe;IAC3C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IACjG,MAAM,IAAI,UAAU,CAClB,8GAA8G;QAC5G,qGAAqG,CACxG,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,GAAG,yBAAyB,CAAC;AAE7C;;;;;;;GAOG;AACH,SAAgB,WAAW,CAAC,KAAa,EAAE,KAAc;IACvD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAClH,MAAM,IAAI,SAAS,CACjB,OAAO,KAAK,sGAAsG,CACnH,CAAC;AACJ,CAAC"}
@@ -8,7 +8,8 @@ export type { RecipientCapability, RecipientCapabilityIssueOptions, RecipientGra
8
8
  * migration makes both. A host whose tables come from different migrations runs `suppressionStoreDdl` and
9
9
  * `recipientCapabilityDdl` where each belongs.
10
10
  *
11
- * @param prefix A lower-case letter followed by at most 39 lower-case letters, digits or underscores.
11
+ * @param prefix A lower-case letter followed by at most 39 lower-case letters, digits or underscores, other than
12
+ * `sqlite` and a prefix beginning `sqlite_`.
12
13
  * @throws RangeError when the prefix does not fit.
13
14
  */
14
15
  export declare function emailPreferencesDdl(prefix: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/preferences/index.ts"],"names":[],"mappings":"AAOA,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjI,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAC1F,YAAY,EACV,mBAAmB,EACnB,+BAA+B,EAC/B,cAAc,EACd,6BAA6B,GAC9B,MAAM,0BAA0B,CAAC;AAElC;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/preferences/index.ts"],"names":[],"mappings":"AAOA,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACjI,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAC1F,YAAY,EACV,mBAAmB,EACnB,+BAA+B,EAC/B,cAAc,EACd,6BAA6B,GAC9B,MAAM,0BAA0B,CAAC;AAElC;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D"}
@@ -21,7 +21,8 @@ Object.defineProperty(exports, "recipientCapabilityDdl", { enumerable: true, get
21
21
  * migration makes both. A host whose tables come from different migrations runs `suppressionStoreDdl` and
22
22
  * `recipientCapabilityDdl` where each belongs.
23
23
  *
24
- * @param prefix A lower-case letter followed by at most 39 lower-case letters, digits or underscores.
24
+ * @param prefix A lower-case letter followed by at most 39 lower-case letters, digits or underscores, other than
25
+ * `sqlite` and a prefix beginning `sqlite_`.
25
26
  * @throws RangeError when the prefix does not fit.
26
27
  */
27
28
  function emailPreferencesDdl(prefix) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/preferences/index.ts"],"names":[],"mappings":";;;AA0BA,kDAEC;AA5BD;;;GAGG;AACH,qEAAkE;AAClE,+DAA4D;AAG5D,6DAAiI;AAAxH,gIAAA,yBAAyB,OAAA;AAAE,0HAAA,mBAAmB,OAAA;AAAE,0HAAA,mBAAmB,OAAA;AAAE,0HAAA,mBAAmB,OAAA;AAEjG,mEAA0F;AAAjF,gIAAA,sBAAsB,OAAA;AAAE,gIAAA,sBAAsB,OAAA;AAQvD;;;;;;;GAOG;AACH,SAAgB,mBAAmB,CAAC,MAAc;IAChD,OAAO,GAAG,IAAA,yCAAmB,EAAC,MAAM,CAAC,KAAK,IAAA,+CAAsB,EAAC,MAAM,CAAC,EAAE,CAAC;AAC7E,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/preferences/index.ts"],"names":[],"mappings":";;;AA2BA,kDAEC;AA7BD;;;GAGG;AACH,qEAAkE;AAClE,+DAA4D;AAG5D,6DAAiI;AAAxH,gIAAA,yBAAyB,OAAA;AAAE,0HAAA,mBAAmB,OAAA;AAAE,0HAAA,mBAAmB,OAAA;AAAE,0HAAA,mBAAmB,OAAA;AAEjG,mEAA0F;AAAjF,gIAAA,sBAAsB,OAAA;AAAE,gIAAA,sBAAsB,OAAA;AAQvD;;;;;;;;GAQG;AACH,SAAgB,mBAAmB,CAAC,MAAc;IAChD,OAAO,GAAG,IAAA,yCAAmB,EAAC,MAAM,CAAC,KAAK,IAAA,+CAAsB,EAAC,MAAM,CAAC,EAAE,CAAC;AAC7E,CAAC"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @fileoverview What `EmailService`'s reads check before any mail is read, and the work they do over a message, bounded.
3
+ * `readInbox` and `searchEmails` take a `limit` that is a whole number of at least 1: for any other, `readInbox` would
4
+ * fetch a sequence number past the last message, which a server answers BAD, and `searchEmails` would give every match
5
+ * or the wrong ones. `searchEmails` takes a `since` that is an ISO 8601 date naming a day its month has, and searches
6
+ * from that day: imapflow leaves an invalid date out of the search, which then answers mail of every date, and `Date`
7
+ * rolls a day past its month's end over into the next month. `extractCodes` compiles a caller's pattern before it
8
+ * reads the message, runs it over the message for at most `PATTERN_TIME_LIMIT_MS`, and moves on past a match of empty
9
+ * text, so no pattern holds the process. A message's HTML part loses its tags in one pass, in time growing with its
10
+ * length.
11
+ */
12
+ /** The longest a caller's pattern may run over one message, in milliseconds. */
13
+ export declare const PATTERN_TIME_LIMIT_MS = 250;
14
+ /**
15
+ * `limit` when it is a whole number of at least 1, and `fallback` when it is `undefined` or `null`. Anything else, `0`,
16
+ * a negative or fractional number, `NaN`, `Infinity` and text among them, throws an `Error` before any mail is read.
17
+ *
18
+ * @param limit The most messages a read gives, as the caller gave it.
19
+ * @param fallback The limit when none is given.
20
+ */
21
+ export declare function requireLimit(limit: unknown, fallback: number): number;
22
+ /**
23
+ * The day a search starts from, as `YYYY-MM-DD`: the date `since` names, as written, or `undefined` when `since` is
24
+ * `undefined`, `null` or the empty string, for no bound. `since` is an ISO 8601 date, such as `2026-10-15`, alone or
25
+ * followed by a time and an offset, such as `2026-10-15T09:30:00-05:00`, which are checked and then disregarded.
26
+ * Anything else throws an `Error` before any mail is read: a value that is not text, a number among them; text in
27
+ * another form, which `Date` reads by guesswork and in the local time zone; and a day its month lacks, such as
28
+ * `2026-02-30`, which `Date` rolls over into the next month.
29
+ *
30
+ * @param since The day to search from, as the caller gave it.
31
+ */
32
+ export declare function requireSince(since: unknown): string | undefined;
33
+ /**
34
+ * `pattern` compiled as a JavaScript regular expression with the `g` flag, or `undefined` when it is `undefined`, `null`
35
+ * or the empty string, for the default. A pattern that is not a string throws a `TypeError`, and one that does not
36
+ * compile an `Error` holding the compiler's message, both before any mail is read.
37
+ *
38
+ * @param pattern The pattern as the caller gave it.
39
+ */
40
+ export declare function requirePattern(pattern: unknown): RegExp | undefined;
41
+ /**
42
+ * The codes `pattern` finds in `text`, in the order found: each match's first group, or the whole match when that group
43
+ * takes no part in it, leaving out each that is empty. A match of empty text moves the search on by one character, as
44
+ * `String.prototype.matchAll` does. The search runs for at most `PATTERN_TIME_LIMIT_MS`, timed by a `node:vm` script's
45
+ * timeout, which V8 honours at each step back a regular expression takes; one that runs longer, such as `(\d+)+$` over
46
+ * a long run of digits, throws an `Error` that names the limit. The `vm` module serves as a timer here, not a sandbox:
47
+ * the pattern runs in this realm, and a regular expression runs no code of its own.
48
+ *
49
+ * @param pattern A pattern `requirePattern` compiled, with the `g` flag.
50
+ * @param text The message's text.
51
+ */
52
+ export declare function codesMatching(pattern: RegExp, text: string): string[];
53
+ /**
54
+ * `html` with each tag written as a space, a tag running from a `<` to the first `>` after it with at least one
55
+ * character between them, as `/<[^>]+>/g` reads one: `<>` is no tag, and neither is a `<` with no `>` after it, and
56
+ * both stay in the text. The tags are found in one pass, in time growing with the length of `html`, where that
57
+ * expression takes time growing with the square of a run of `<` with no `>` after it.
58
+ *
59
+ * @param html The HTML part's text.
60
+ */
61
+ export declare function withoutTags(html: string): string;
62
+ /**
63
+ * A message's date as ISO 8601 text: the `Date` imapflow read from its Date field, or the empty string when the field
64
+ * is missing or does not read as a date, in which case imapflow gives the field's text instead.
65
+ *
66
+ * @param date The envelope's `date` as imapflow gives it.
67
+ */
68
+ export declare function isoDateOf(date: unknown): string;
69
+ //# sourceMappingURL=reads.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reads.d.ts","sourceRoot":"","sources":["../src/reads.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIrE;AAeD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAc/D;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAQnE;AAKD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAmBrE;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAkBhD;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAE/C"}
package/dist/reads.js ADDED
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview What `EmailService`'s reads check before any mail is read, and the work they do over a message, bounded.
4
+ * `readInbox` and `searchEmails` take a `limit` that is a whole number of at least 1: for any other, `readInbox` would
5
+ * fetch a sequence number past the last message, which a server answers BAD, and `searchEmails` would give every match
6
+ * or the wrong ones. `searchEmails` takes a `since` that is an ISO 8601 date naming a day its month has, and searches
7
+ * from that day: imapflow leaves an invalid date out of the search, which then answers mail of every date, and `Date`
8
+ * rolls a day past its month's end over into the next month. `extractCodes` compiles a caller's pattern before it
9
+ * reads the message, runs it over the message for at most `PATTERN_TIME_LIMIT_MS`, and moves on past a match of empty
10
+ * text, so no pattern holds the process. A message's HTML part loses its tags in one pass, in time growing with its
11
+ * length.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.PATTERN_TIME_LIMIT_MS = void 0;
15
+ exports.requireLimit = requireLimit;
16
+ exports.requireSince = requireSince;
17
+ exports.requirePattern = requirePattern;
18
+ exports.codesMatching = codesMatching;
19
+ exports.withoutTags = withoutTags;
20
+ exports.isoDateOf = isoDateOf;
21
+ const node_vm_1 = require("node:vm");
22
+ const messageText_js_1 = require("./messageText.js");
23
+ /** The longest a caller's pattern may run over one message, in milliseconds. */
24
+ exports.PATTERN_TIME_LIMIT_MS = 250;
25
+ /**
26
+ * `limit` when it is a whole number of at least 1, and `fallback` when it is `undefined` or `null`. Anything else, `0`,
27
+ * a negative or fractional number, `NaN`, `Infinity` and text among them, throws an `Error` before any mail is read.
28
+ *
29
+ * @param limit The most messages a read gives, as the caller gave it.
30
+ * @param fallback The limit when none is given.
31
+ */
32
+ function requireLimit(limit, fallback) {
33
+ if (limit === undefined || limit === null)
34
+ return fallback;
35
+ if (typeof limit === 'number' && Number.isSafeInteger(limit) && limit >= 1)
36
+ return limit;
37
+ throw new Error('The limit was refused: give it as a whole number of at least 1');
38
+ }
39
+ /**
40
+ * An ISO 8601 calendar date, alone or followed by a time and an offset, in the forms ECMAScript's date time string
41
+ * format writes: `2026-10-15`, `2026-10-15T09:30`, `2026-10-15T09:30:00`, `2026-10-15T09:30:00.000`, each of the last
42
+ * three with `Z` or an offset such as `-05:00` after it or not.
43
+ */
44
+ const ISO_DATE = /^(\d{4})-(\d{2})-(\d{2})(?:T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})?)?$/;
45
+ /** The days of `month`, from 1 to 12, in `year` of the Gregorian calendar. */
46
+ function daysIn(year, month) {
47
+ if (month === 2)
48
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0 ? 29 : 28;
49
+ return month === 4 || month === 6 || month === 9 || month === 11 ? 30 : 31;
50
+ }
51
+ /**
52
+ * The day a search starts from, as `YYYY-MM-DD`: the date `since` names, as written, or `undefined` when `since` is
53
+ * `undefined`, `null` or the empty string, for no bound. `since` is an ISO 8601 date, such as `2026-10-15`, alone or
54
+ * followed by a time and an offset, such as `2026-10-15T09:30:00-05:00`, which are checked and then disregarded.
55
+ * Anything else throws an `Error` before any mail is read: a value that is not text, a number among them; text in
56
+ * another form, which `Date` reads by guesswork and in the local time zone; and a day its month lacks, such as
57
+ * `2026-02-30`, which `Date` rolls over into the next month.
58
+ *
59
+ * @param since The day to search from, as the caller gave it.
60
+ */
61
+ function requireSince(since) {
62
+ if (since === undefined || since === null || since === '')
63
+ return undefined;
64
+ const parts = typeof since === 'string' ? ISO_DATE.exec(since) : null;
65
+ if (parts !== null) {
66
+ const year = Number(parts[1]);
67
+ const month = Number(parts[2]);
68
+ const day = Number(parts[3]);
69
+ // Date takes a day from 1 to 31 in any month and rolls one past the month's end over into the next, so the day is
70
+ // checked against its month here; Date checks the time and the offset.
71
+ if (month >= 1 && month <= 12 && day >= 1 && day <= daysIn(year, month) && !Number.isNaN(Date.parse(parts[0]))) {
72
+ return parts[0].slice(0, 10);
73
+ }
74
+ }
75
+ throw new Error('The since date was refused: give it as a date, such as 2026-10-15');
76
+ }
77
+ /**
78
+ * `pattern` compiled as a JavaScript regular expression with the `g` flag, or `undefined` when it is `undefined`, `null`
79
+ * or the empty string, for the default. A pattern that is not a string throws a `TypeError`, and one that does not
80
+ * compile an `Error` holding the compiler's message, both before any mail is read.
81
+ *
82
+ * @param pattern The pattern as the caller gave it.
83
+ */
84
+ function requirePattern(pattern) {
85
+ const source = (0, messageText_js_1.optionalText)('pattern', pattern);
86
+ if (source === undefined || source === '')
87
+ return undefined;
88
+ try {
89
+ return new RegExp(source, 'g');
90
+ }
91
+ catch (error) {
92
+ throw new Error(`The pattern was refused: ${error instanceof Error ? error.message : String(error)}`);
93
+ }
94
+ }
95
+ /** The script `codesMatching` runs its search in, so that the search has a time limit. */
96
+ const UNDER_A_TIME_LIMIT = new node_vm_1.Script('found = search()');
97
+ /**
98
+ * The codes `pattern` finds in `text`, in the order found: each match's first group, or the whole match when that group
99
+ * takes no part in it, leaving out each that is empty. A match of empty text moves the search on by one character, as
100
+ * `String.prototype.matchAll` does. The search runs for at most `PATTERN_TIME_LIMIT_MS`, timed by a `node:vm` script's
101
+ * timeout, which V8 honours at each step back a regular expression takes; one that runs longer, such as `(\d+)+$` over
102
+ * a long run of digits, throws an `Error` that names the limit. The `vm` module serves as a timer here, not a sandbox:
103
+ * the pattern runs in this realm, and a regular expression runs no code of its own.
104
+ *
105
+ * @param pattern A pattern `requirePattern` compiled, with the `g` flag.
106
+ * @param text The message's text.
107
+ */
108
+ function codesMatching(pattern, text) {
109
+ const search = () => {
110
+ const codes = [];
111
+ for (const match of text.matchAll(pattern)) {
112
+ const code = match[1] ?? match[0];
113
+ if (code !== '')
114
+ codes.push(code);
115
+ }
116
+ return codes;
117
+ };
118
+ const context = (0, node_vm_1.createContext)({ search, found: undefined });
119
+ try {
120
+ UNDER_A_TIME_LIMIT.runInContext(context, { timeout: exports.PATTERN_TIME_LIMIT_MS });
121
+ }
122
+ catch (error) {
123
+ if (error?.code === 'ERR_SCRIPT_EXECUTION_TIMEOUT') {
124
+ throw new Error(`The pattern was refused: it ran longer than ${exports.PATTERN_TIME_LIMIT_MS} ms over the message`);
125
+ }
126
+ throw error;
127
+ }
128
+ return context.found ?? [];
129
+ }
130
+ /**
131
+ * `html` with each tag written as a space, a tag running from a `<` to the first `>` after it with at least one
132
+ * character between them, as `/<[^>]+>/g` reads one: `<>` is no tag, and neither is a `<` with no `>` after it, and
133
+ * both stay in the text. The tags are found in one pass, in time growing with the length of `html`, where that
134
+ * expression takes time growing with the square of a run of `<` with no `>` after it.
135
+ *
136
+ * @param html The HTML part's text.
137
+ */
138
+ function withoutTags(html) {
139
+ const pieces = [];
140
+ let copied = 0;
141
+ let open = html.indexOf('<');
142
+ while (open !== -1) {
143
+ const close = html.indexOf('>', open + 1);
144
+ // No ">" after this "<" means none after any later "<" either.
145
+ if (close === -1)
146
+ break;
147
+ if (close === open + 1) {
148
+ open = html.indexOf('<', close);
149
+ continue;
150
+ }
151
+ pieces.push(html.slice(copied, open), ' ');
152
+ copied = close + 1;
153
+ open = html.indexOf('<', copied);
154
+ }
155
+ pieces.push(html.slice(copied));
156
+ return pieces.join('');
157
+ }
158
+ /**
159
+ * A message's date as ISO 8601 text: the `Date` imapflow read from its Date field, or the empty string when the field
160
+ * is missing or does not read as a date, in which case imapflow gives the field's text instead.
161
+ *
162
+ * @param date The envelope's `date` as imapflow gives it.
163
+ */
164
+ function isoDateOf(date) {
165
+ return date instanceof Date && !Number.isNaN(date.getTime()) ? date.toISOString() : '';
166
+ }
167
+ //# sourceMappingURL=reads.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reads.js","sourceRoot":"","sources":["../src/reads.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,11 @@ export declare class EmailExtractCodesTool {
23
23
  required: string[];
24
24
  };
25
25
  constructor(service: EmailService);
26
+ /**
27
+ * Extracts the codes through `EmailService.extractCodes`. A `pattern` that is not a string or does not compile is
28
+ * refused before any mail is read, and one that runs longer than 250 ms over the message is stopped, each with
29
+ * `{ success: false, error }`.
30
+ */
26
31
  execute(args: {
27
32
  messageId: string;
28
33
  pattern?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"extractCodes.d.ts","sourceRoot":"","sources":["../../src/tools/extractCodes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,qBAAa,qBAAqB;IAkBpB,OAAO,CAAC,OAAO;IAjB3B,QAAQ,CAAC,EAAE,uBAAuB;IAClC,QAAQ,CAAC,IAAI,uBAAuB;IACpC,QAAQ,CAAC,WAAW,gCAAgC;IACpD,QAAQ,CAAC,WAAW,mFAAmF;IACvG,QAAQ,CAAC,QAAQ,mBAAmB;IACpC,QAAQ,CAAC,OAAO,WAAW;IAC3B,QAAQ,CAAC,cAAc,SAAS;IAEhC,QAAQ,CAAC,WAAW;;;;;;;;;;;;;MAOlB;gBAEkB,OAAO,EAAE,YAAY;IAEnC,OAAO,CAAC,IAAI,EAAE;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAQ9D"}
1
+ {"version":3,"file":"extractCodes.d.ts","sourceRoot":"","sources":["../../src/tools/extractCodes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,qBAAa,qBAAqB;IAsBpB,OAAO,CAAC,OAAO;IArB3B,QAAQ,CAAC,EAAE,uBAAuB;IAClC,QAAQ,CAAC,IAAI,uBAAuB;IACpC,QAAQ,CAAC,WAAW,gCAAgC;IACpD,QAAQ,CAAC,WAAW,mFAAmF;IACvG,QAAQ,CAAC,QAAQ,mBAAmB;IACpC,QAAQ,CAAC,OAAO,WAAW;IAC3B,QAAQ,CAAC,cAAc,SAAS;IAEhC,QAAQ,CAAC,WAAW;;;;;;;;;;;;;MAWlB;gBAEkB,OAAO,EAAE,YAAY;IAEzC;;;;OAIG;IACG,OAAO,CAAC,IAAI,EAAE;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAQ9D"}
@@ -14,12 +14,20 @@ class EmailExtractCodesTool {
14
14
  this.inputSchema = {
15
15
  type: 'object',
16
16
  properties: {
17
- messageId: { type: 'string', description: 'Message ID of the email to extract codes from' },
18
- pattern: { type: 'string', description: 'Optional regex pattern to match codes (default: 4-8 digit numbers)' },
17
+ messageId: { type: 'string', description: 'Message ID of the email to extract codes from, as emailRead or emailSearch gives it, such as <id@example.com>' },
18
+ pattern: {
19
+ type: 'string',
20
+ description: "Optional JavaScript regular expression for the codes: each match's first group, or the whole match, is a code. It runs for at most 250 ms (default: runs of 4 to 8 digits)",
21
+ },
19
22
  },
20
23
  required: ['messageId'],
21
24
  };
22
25
  }
26
+ /**
27
+ * Extracts the codes through `EmailService.extractCodes`. A `pattern` that is not a string or does not compile is
28
+ * refused before any mail is read, and one that runs longer than 250 ms over the message is stopped, each with
29
+ * `{ success: false, error }`.
30
+ */
23
31
  async execute(args) {
24
32
  try {
25
33
  const result = await this.service.extractCodes(args.messageId, args.pattern);