@remit/data-ports 0.0.31 → 0.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/data-ports",
3
- "version": "0.0.31",
3
+ "version": "0.0.32",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -5,9 +5,6 @@ import { storedDisplayName } from "./display-name.js";
5
5
  const OWN = "matthijs@ischen.nl";
6
6
  const SPOOFED = "aramirez@secresaludguaviare.gov.co";
7
7
 
8
- /**
9
- * The name is a stranger's address and nothing else, so nothing survives it.
10
- */
11
8
  const emptied: ReadonlyArray<readonly [string, string]> = [
12
9
  ["the incident", "matthijs@ischen.nl"],
13
10
  ["quoted", '"matthijs@ischen.nl"'],
@@ -21,11 +18,6 @@ const emptied: ReadonlyArray<readonly [string, string]> = [
21
18
  ["a non-ASCII local part", "Özcan@example.com"],
22
19
  ];
23
20
 
24
- /**
25
- * The name says something besides the address, and that something is what its
26
- * recipient knows the sender by. Removing it to remove the address destroys
27
- * real text on a live instance.
28
- */
29
21
  const stripped: ReadonlyArray<readonly [string, string, string]> = [
30
22
  ["parenthesised", "Support (support@acme.com)", "Support"],
31
23
  ["angle-bracketed", "Matthijs <matthijs@ischen.nl>", "Matthijs"],
@@ -84,11 +76,6 @@ describe("a display name that claims to be an address", () => {
84
76
  assert.equal(storedDisplayName(name, OWN), name);
85
77
  });
86
78
 
87
- /**
88
- * SQLite folds `A-Z` and stops; JS folds the whole of Unicode. A rule spelled
89
- * once in SQL and once in TypeScript disagrees exactly here, and the
90
- * disagreement rewrites a name on a live database.
91
- */
92
79
  it("folds case beyond ASCII on both sides", () => {
93
80
  assert.equal(
94
81
  storedDisplayName("Özcan@example.com", "özcan@example.com"),
@@ -1,86 +1,40 @@
1
- /**
2
- * A display name that claims to be an email address (issue #826).
3
- *
4
- * The name on an envelope address is free text its sender chooses; the address
5
- * is verifiable. A spam envelope of
6
- * `matthijs@ischen.nl <aramirez@secresaludguaviare.gov.co>` therefore labels a
7
- * stranger's address with the reader's own, and every surface that shows a name
8
- * — the message list, the message header, contact autocomplete — repeats the
9
- * claim. On 2026-08-18 autocomplete offered such a row back and private mail
10
- * left the instance.
11
- *
12
- * The rule is one function because it has to hold in two places at once: the
13
- * harvest that decides what to store, and the repair that rewrites what is
14
- * already stored. Two spellings of it disagree at the edges — SQL `lower()`
15
- * folds ASCII only where JS folds all of Unicode — and every disagreement
16
- * rewrites a name on a live database that holds the only copy of it.
17
- *
18
- * What comes out is the name minus the claim, not the empty string. A sender
19
- * signing itself `Support (support@acme.com)` from `noreply@acme.com` is
20
- * ordinary mail, and `Support` is the name its recipient knows it by; deleting
21
- * that to remove the address takes real text off a live instance. Only the
22
- * address is removed, with the punctuation that was holding it, and a name that
23
- * is nothing but the claim comes back empty.
24
- *
25
- * An address the name shares with the address it labels is not a claim about
26
- * anyone else, so it stays — `Matthijs <matthijs@ischen.nl>` on
27
- * `matthijs@ischen.nl` is the same information twice.
28
- */
29
-
30
1
  const EMBEDDED_ADDRESS =
31
2
  /[^\s@<>()[\],;:"'\\]+@[^\s@<>()[\],;:"'\\]+\.[^\s@<>()[\],;:"'\\.]{2,}/gu;
32
3
 
33
- /** `( )`, `< >`, `""` — a delimiter pair the removed address was sitting in. */
34
- const EMPTIED_PAIR = /[<([{"']\s*[>)\]}"']/gu;
4
+ const DELIMITER_PAIR_LEFT_EMPTY = /[<([{"']\s*[>)\]}"']/gu;
35
5
 
36
- /**
37
- * What is left holding the address once it is gone: the separators around it.
38
- * Brackets and quotes are not trimmed here — EMPTIED_PAIR has already taken the
39
- * ones that were wrapping the address, and a name can legitimately end in one.
40
- */
41
- const EDGE_SEPARATORS = /^[\s,;:|/\\-]+|[\s,;:|/\\-]+$/gu;
6
+ const LEADING_OR_TRAILING_SEPARATORS = /^[\s,;:|/\\-]+|[\s,;:|/\\-]+$/gu;
42
7
 
43
- const tidy = (text: string): string =>
8
+ const tidyAfterAddressRemoval = (text: string): string =>
44
9
  text
45
- .replace(EMPTIED_PAIR, " ")
10
+ .replace(DELIMITER_PAIR_LEFT_EMPTY, " ")
46
11
  .replace(/\s+/gu, " ")
47
- .replace(EDGE_SEPARATORS, "")
12
+ .replace(LEADING_OR_TRAILING_SEPARATORS, "")
48
13
  .trim();
49
14
 
50
- /**
51
- * `normalizedEmail` absent means the envelope carried no address this name
52
- * could be describing, so any address in the name is a claim about nobody.
53
- */
54
- const claimsAnotherAddress = (
15
+ const carriesAddressOtherThan = (
55
16
  text: string,
56
- own: string | undefined,
17
+ ownAddress: string | undefined,
57
18
  ): boolean => {
58
19
  for (const [address] of text.matchAll(EMBEDDED_ADDRESS)) {
59
- if (address.toLowerCase() !== own) return true;
20
+ if (address.toLowerCase() !== ownAddress) return true;
60
21
  }
61
22
  return false;
62
23
  };
63
24
 
64
- /**
65
- * The display name as it should be stored, for both the harvest and the repair.
66
- * Unchanged when the name claims nothing.
67
- */
68
25
  export const storedDisplayName = (
69
26
  displayName: string | undefined,
70
27
  normalizedEmail: string | undefined,
71
28
  ): string => {
72
29
  if (!displayName) return "";
73
- const own = normalizedEmail?.toLowerCase();
74
- if (!claimsAnotherAddress(displayName, own)) return displayName;
30
+ const ownAddress = normalizedEmail?.toLowerCase();
31
+ if (!carriesAddressOtherThan(displayName, ownAddress)) return displayName;
75
32
 
76
- const remainder = tidy(
33
+ const remainder = tidyAfterAddressRemoval(
77
34
  displayName.replace(EMBEDDED_ADDRESS, (address) =>
78
- address.toLowerCase() === own ? address : " ",
35
+ address.toLowerCase() === ownAddress ? address : " ",
79
36
  ),
80
37
  );
81
- // Stripping leaves at most the row's own address behind, so the re-check is a
82
- // backstop rather than a case: nothing that still claims another address is
83
- // ever stored, whatever the shape it was hiding in.
84
- if (!remainder || claimsAnotherAddress(remainder, own)) return "";
38
+ if (!remainder || carriesAddressOtherThan(remainder, ownAddress)) return "";
85
39
  return remainder;
86
40
  };