@remit/data-ports 0.0.82 → 0.0.84

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 (174) hide show
  1. package/dist/account-settings.d.ts +371 -0
  2. package/dist/account-settings.js +114 -0
  3. package/dist/conformance/account.d.ts +3 -0
  4. package/dist/conformance/account.js +118 -0
  5. package/dist/conformance/calendar-collection.d.ts +3 -0
  6. package/dist/conformance/calendar-collection.js +251 -0
  7. package/dist/conformance/calendar-event-index.d.ts +3 -0
  8. package/dist/conformance/calendar-event-index.js +114 -0
  9. package/dist/conformance/calendar-feed-token.d.ts +3 -0
  10. package/dist/conformance/calendar-feed-token.js +97 -0
  11. package/dist/conformance/calendar-object.d.ts +3 -0
  12. package/dist/conformance/calendar-object.js +156 -0
  13. package/dist/conformance/calendar-suggestion.d.ts +3 -0
  14. package/dist/conformance/calendar-suggestion.js +255 -0
  15. package/{src/conformance/harness.ts → dist/conformance/harness.d.ts} +4 -4
  16. package/dist/conformance/harness.js +1 -0
  17. package/dist/conformance/index.js +8 -0
  18. package/dist/conformance/label.d.ts +3 -0
  19. package/dist/conformance/label.js +92 -0
  20. package/dist/conformance/sender-signer-standing.d.ts +3 -0
  21. package/dist/conformance/sender-signer-standing.js +81 -0
  22. package/dist/display-name.d.ts +1 -0
  23. package/dist/display-name.js +26 -0
  24. package/dist/errors.d.ts +150 -0
  25. package/dist/errors.js +181 -0
  26. package/dist/filter-ttl.d.ts +7 -0
  27. package/{src/filter-ttl.ts → dist/filter-ttl.js} +8 -11
  28. package/dist/folder-role.d.ts +239 -0
  29. package/dist/folder-role.js +276 -0
  30. package/dist/id.d.ts +124 -0
  31. package/{src/id.ts → dist/id.js} +41 -127
  32. package/dist/index.d.ts +37 -0
  33. package/dist/index.js +5 -0
  34. package/dist/interfaces/account-config.d.ts +15 -0
  35. package/dist/interfaces/account-config.js +1 -0
  36. package/dist/interfaces/account-export-request.d.ts +10 -0
  37. package/dist/interfaces/account-export-request.js +1 -0
  38. package/dist/interfaces/account-setting.d.ts +7 -0
  39. package/dist/interfaces/account-setting.js +1 -0
  40. package/dist/interfaces/account.d.ts +29 -0
  41. package/dist/interfaces/account.js +1 -0
  42. package/dist/interfaces/address.d.ts +69 -0
  43. package/dist/interfaces/address.js +1 -0
  44. package/dist/interfaces/calendar-collection.d.ts +38 -0
  45. package/dist/interfaces/calendar-collection.js +1 -0
  46. package/dist/interfaces/calendar-event-index.d.ts +19 -0
  47. package/dist/interfaces/calendar-event-index.js +1 -0
  48. package/dist/interfaces/calendar-feed-token.d.ts +31 -0
  49. package/dist/interfaces/calendar-feed-token.js +1 -0
  50. package/dist/interfaces/calendar-object.d.ts +36 -0
  51. package/dist/interfaces/calendar-object.js +1 -0
  52. package/dist/interfaces/calendar-store.d.ts +93 -0
  53. package/dist/interfaces/calendar-store.js +1 -0
  54. package/dist/interfaces/calendar-suggestion.d.ts +62 -0
  55. package/dist/interfaces/calendar-suggestion.js +1 -0
  56. package/dist/interfaces/config-import.d.ts +8 -0
  57. package/dist/interfaces/config-import.js +1 -0
  58. package/dist/interfaces/envelope.d.ts +24 -0
  59. package/dist/interfaces/envelope.js +1 -0
  60. package/dist/interfaces/filter.d.ts +28 -0
  61. package/dist/interfaces/filter.js +1 -0
  62. package/dist/interfaces/label.d.ts +17 -0
  63. package/dist/interfaces/label.js +1 -0
  64. package/dist/interfaces/mailbox-lock.d.ts +12 -0
  65. package/dist/interfaces/mailbox-lock.js +1 -0
  66. package/dist/interfaces/mailbox-special-use.d.ts +54 -0
  67. package/dist/interfaces/mailbox-special-use.js +1 -0
  68. package/dist/interfaces/mailbox.d.ts +79 -0
  69. package/dist/interfaces/mailbox.js +1 -0
  70. package/dist/interfaces/message-flag-push.d.ts +9 -0
  71. package/dist/interfaces/message-flag-push.js +1 -0
  72. package/dist/interfaces/message-flag.d.ts +14 -0
  73. package/dist/interfaces/message-flag.js +1 -0
  74. package/dist/interfaces/message-label.d.ts +21 -0
  75. package/dist/interfaces/message-label.js +1 -0
  76. package/dist/interfaces/message-placement-move.d.ts +8 -0
  77. package/dist/interfaces/message-placement-move.js +1 -0
  78. package/dist/interfaces/message.d.ts +60 -0
  79. package/dist/interfaces/message.js +1 -0
  80. package/dist/interfaces/organize-job-request.d.ts +10 -0
  81. package/dist/interfaces/organize-job-request.js +1 -0
  82. package/dist/interfaces/outbox-attachment.d.ts +101 -0
  83. package/dist/interfaces/outbox-attachment.js +7 -0
  84. package/dist/interfaces/outbox-message.d.ts +70 -0
  85. package/dist/interfaces/outbox-message.js +20 -0
  86. package/dist/interfaces/quarantine.d.ts +20 -0
  87. package/dist/interfaces/quarantine.js +1 -0
  88. package/dist/interfaces/sender-signer-standing.d.ts +26 -0
  89. package/dist/interfaces/sender-signer-standing.js +1 -0
  90. package/dist/interfaces/sync-warning.d.ts +11 -0
  91. package/dist/interfaces/sync-warning.js +1 -0
  92. package/dist/interfaces/thread-message.d.ts +202 -0
  93. package/dist/interfaces/thread-message.js +1 -0
  94. package/dist/mailbox-name.d.ts +35 -0
  95. package/dist/mailbox-name.js +68 -0
  96. package/dist/message-parts.d.ts +22 -0
  97. package/dist/message-parts.js +18 -0
  98. package/dist/message-settlement.d.ts +35 -0
  99. package/{src/message-settlement.ts → dist/message-settlement.js} +5 -19
  100. package/dist/mutation-events.d.ts +19 -0
  101. package/{src/mutation-events.ts → dist/mutation-events.js} +2 -5
  102. package/dist/sender-mute.d.ts +26 -0
  103. package/{src/sender-mute.ts → dist/sender-mute.js} +12 -34
  104. package/dist/sync-warning-state.d.ts +3 -0
  105. package/dist/sync-warning-state.js +5 -0
  106. package/dist/types.d.ts +491 -0
  107. package/dist/types.js +1 -0
  108. package/dist/update-manifest.d.ts +24 -0
  109. package/dist/update-manifest.js +20 -0
  110. package/dist/virtual-copy.d.ts +8 -0
  111. package/{src/virtual-copy.ts → dist/virtual-copy.js} +10 -15
  112. package/dist/wellknown.d.ts +47 -0
  113. package/{src/wellknown.ts → dist/wellknown.js} +26 -49
  114. package/package.json +44 -79
  115. package/src/account-settings.ts +0 -194
  116. package/src/conformance/account.ts +0 -104
  117. package/src/conformance/calendar-collection.ts +0 -342
  118. package/src/conformance/calendar-event-index.ts +0 -203
  119. package/src/conformance/calendar-feed-token.ts +0 -148
  120. package/src/conformance/calendar-object.ts +0 -269
  121. package/src/conformance/calendar-suggestion.ts +0 -490
  122. package/src/conformance/label.ts +0 -136
  123. package/src/conformance/sender-signer-standing.ts +0 -117
  124. package/src/display-name.test.ts +0 -101
  125. package/src/display-name.ts +0 -40
  126. package/src/errors.ts +0 -226
  127. package/src/folder-role.test.ts +0 -422
  128. package/src/folder-role.ts +0 -447
  129. package/src/id.test.ts +0 -352
  130. package/src/index.ts +0 -170
  131. package/src/interfaces/account-config.ts +0 -25
  132. package/src/interfaces/account-export-request.ts +0 -21
  133. package/src/interfaces/account-setting.ts +0 -14
  134. package/src/interfaces/account.ts +0 -43
  135. package/src/interfaces/address.ts +0 -122
  136. package/src/interfaces/calendar-collection.ts +0 -60
  137. package/src/interfaces/calendar-event-index.ts +0 -34
  138. package/src/interfaces/calendar-feed-token.ts +0 -38
  139. package/src/interfaces/calendar-object.ts +0 -58
  140. package/src/interfaces/calendar-suggestion.ts +0 -93
  141. package/src/interfaces/calendar-unit-of-work.ts +0 -47
  142. package/src/interfaces/config-import.ts +0 -16
  143. package/src/interfaces/envelope.ts +0 -32
  144. package/src/interfaces/filter-anchor-transaction.ts +0 -23
  145. package/src/interfaces/filter-anchor.ts +0 -11
  146. package/src/interfaces/filter.ts +0 -41
  147. package/src/interfaces/label.ts +0 -30
  148. package/src/interfaces/mailbox-lock.ts +0 -32
  149. package/src/interfaces/mailbox-special-use.ts +0 -60
  150. package/src/interfaces/mailbox.ts +0 -115
  151. package/src/interfaces/message-flag-push.ts +0 -17
  152. package/src/interfaces/message-flag.ts +0 -15
  153. package/src/interfaces/message-label.ts +0 -25
  154. package/src/interfaces/message-placement-move.ts +0 -15
  155. package/src/interfaces/message.ts +0 -56
  156. package/src/interfaces/organize-job-request.ts +0 -19
  157. package/src/interfaces/outbox-attachment.ts +0 -132
  158. package/src/interfaces/outbox-message.ts +0 -106
  159. package/src/interfaces/quarantine.ts +0 -22
  160. package/src/interfaces/sender-signer-standing.ts +0 -37
  161. package/src/interfaces/thread-message.ts +0 -264
  162. package/src/interfaces/unit-of-work.ts +0 -28
  163. package/src/mailbox-name.test.ts +0 -106
  164. package/src/mailbox-name.ts +0 -90
  165. package/src/mailbox-state-doors.test.ts +0 -104
  166. package/src/message-placement-doors.test.ts +0 -100
  167. package/src/message-settlement.test.ts +0 -132
  168. package/src/sender-mute.test.ts +0 -76
  169. package/src/types.ts +0 -820
  170. package/src/update-manifest.test.ts +0 -130
  171. package/src/update-manifest.ts +0 -22
  172. package/src/virtual-copy.test.ts +0 -34
  173. package/tsconfig.json +0 -8
  174. /package/{src/conformance/index.ts → dist/conformance/index.d.ts} +0 -0
@@ -1,117 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { after, before, describe, test } from "node:test";
3
- import type { ISenderSignerStandingRepository } from "../interfaces/sender-signer-standing.js";
4
- import type { RepositoryConformanceHarness } from "./harness.js";
5
-
6
- export function senderSignerStandingRepositoryConformance(
7
- harness: RepositoryConformanceHarness<ISenderSignerStandingRepository>,
8
- ): void {
9
- describe("ISenderSignerStandingRepository conformance", () => {
10
- let repo: ISenderSignerStandingRepository;
11
-
12
- before(async () => {
13
- repo = await harness.createRepository();
14
- });
15
-
16
- after(() => harness.teardown());
17
-
18
- test("the first observation creates the row at a count of one", async () => {
19
- const accountConfigId = harness.makeId();
20
- const observedAt = 1_700_000_000_000;
21
-
22
- const standing = await repo.observe({
23
- accountConfigId,
24
- senderKey: "example.com",
25
- signerDomain: "esp.example",
26
- observedAt,
27
- });
28
-
29
- assert.equal(standing.messageCount, 1);
30
- assert.equal(standing.firstSeenAt, observedAt);
31
- assert.equal(standing.lastSeenAt, observedAt);
32
- assert.equal(standing.userAffirmedAt, 0);
33
- });
34
-
35
- test("a repeat observation increments the count", async () => {
36
- const accountConfigId = harness.makeId();
37
- const key = {
38
- accountConfigId,
39
- senderKey: "example.com",
40
- signerDomain: "esp.example",
41
- };
42
-
43
- await repo.observe({ ...key, observedAt: 1_700_000_000_000 });
44
- const second = await repo.observe({
45
- ...key,
46
- observedAt: 1_700_000_060_000,
47
- });
48
-
49
- assert.equal(second.messageCount, 2);
50
- assert.equal(
51
- (await repo.get(accountConfigId, key.senderKey, key.signerDomain))
52
- .messageCount,
53
- 2,
54
- );
55
- });
56
-
57
- test("firstSeenAt survives every later observation while lastSeenAt advances", async () => {
58
- const accountConfigId = harness.makeId();
59
- const key = {
60
- accountConfigId,
61
- senderKey: "news.example.com",
62
- signerDomain: "unverified",
63
- };
64
- const first = 1_700_000_000_000;
65
-
66
- await repo.observe({ ...key, observedAt: first });
67
- await repo.observe({ ...key, observedAt: first + 60_000 });
68
- const third = await repo.observe({ ...key, observedAt: first + 120_000 });
69
-
70
- assert.equal(third.messageCount, 3);
71
- assert.equal(third.firstSeenAt, first);
72
- assert.equal(third.lastSeenAt, first + 120_000);
73
-
74
- const reread = await repo.get(
75
- accountConfigId,
76
- key.senderKey,
77
- key.signerDomain,
78
- );
79
- assert.equal(reread.firstSeenAt, first);
80
- assert.equal(reread.lastSeenAt, first + 120_000);
81
- });
82
-
83
- test("a key differing only in signer domain is a separate row", async () => {
84
- const accountConfigId = harness.makeId();
85
- const observedAt = 1_700_000_000_000;
86
-
87
- await repo.observe({
88
- accountConfigId,
89
- senderKey: "example.com",
90
- signerDomain: "esp-one.example",
91
- observedAt,
92
- });
93
- const other = await repo.observe({
94
- accountConfigId,
95
- senderKey: "example.com",
96
- signerDomain: "esp-two.example",
97
- observedAt,
98
- });
99
-
100
- assert.equal(other.messageCount, 1);
101
- });
102
-
103
- test("get reports not-found for a key never observed, and does not create it", async () => {
104
- const accountConfigId = harness.makeId();
105
-
106
- await assert.rejects(
107
- repo.get(accountConfigId, "stranger.example", "esp.example"),
108
- (error) => harness.isNotFoundError(error),
109
- );
110
-
111
- await assert.rejects(
112
- repo.get(accountConfigId, "stranger.example", "esp.example"),
113
- (error) => harness.isNotFoundError(error),
114
- );
115
- });
116
- });
117
- }
@@ -1,101 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { describe, it } from "node:test";
3
- import { storedDisplayName } from "./display-name.js";
4
-
5
- const OWN = "matthijs@ischen.nl";
6
- const SPOOFED = "aramirez@secresaludguaviare.gov.co";
7
-
8
- const emptied: ReadonlyArray<readonly [string, string]> = [
9
- ["the incident", "matthijs@ischen.nl"],
10
- ["quoted", '"matthijs@ischen.nl"'],
11
- ["angle-bracketed", "<matthijs@ischen.nl>"],
12
- ["BATV-wrapped", "prvs=0068b51f37=matthijs@ischen.nl"],
13
- ["upper case", "MATTHIJS@ISCHEN.NL"],
14
- ["a subdomain apart", "matthijs@mail.ischen.nl"],
15
- ["separated by a tab", "\tmatthijs@ischen.nl"],
16
- ["separated by a no-break space", " matthijs@ischen.nl"],
17
- ["separated by a zero-width space", "​matthijs@ischen.nl"],
18
- ["a non-ASCII local part", "Özcan@example.com"],
19
- ];
20
-
21
- const stripped: ReadonlyArray<readonly [string, string, string]> = [
22
- ["parenthesised", "Support (support@acme.com)", "Support"],
23
- ["angle-bracketed", "Matthijs <matthijs@ischen.nl>", "Matthijs"],
24
- ["comma-separated", "matthijs@ischen.nl, team", "team"],
25
- ["semicolon-separated", "Team; matthijs@ischen.nl", "Team"],
26
- ["colon-separated", "Reply to: matthijs@ischen.nl", "Reply to"],
27
- ["leading and parenthesised", "(matthijs@ischen.nl) Support", "Support"],
28
- ["mid-name", "Support <matthijs@ischen.nl> Team", "Support Team"],
29
- ["with a trailing verb", "matthijs@ischen.nl wrote", "wrote"],
30
- [
31
- "quoted around a name",
32
- 'Bob "The Builder" <bob@acme.com>',
33
- 'Bob "The Builder"',
34
- ],
35
- ["a tab away from the name", "Support\tmatthijs@ischen.nl", "Support"],
36
- ];
37
-
38
- const kept: ReadonlyArray<readonly [string, string]> = [
39
- ["an ordinary human name", "Matthijs van Henten"],
40
- ["a name with a full stop", "Dr. M. van Henten"],
41
- ["a company name", "ING Bank N.V."],
42
- ["a bare word", "Newsletter"],
43
- ["an empty name", ""],
44
- ["a name with an at-sign but no domain", "me @ home"],
45
- ["a social handle", "@matthijs"],
46
- ["a version string", "release 2.11"],
47
- ["a domain with no local part", "ischen.nl"],
48
- ["a single-letter tld", "a@b.c"],
49
- ];
50
-
51
- describe("a display name that claims to be an address", () => {
52
- for (const [what, name] of emptied) {
53
- it(`empties ${what}`, () => {
54
- assert.equal(storedDisplayName(name, SPOOFED), "");
55
- });
56
- }
57
-
58
- for (const [what, name, remainder] of stripped) {
59
- it(`keeps what is left after ${what}`, () => {
60
- assert.equal(storedDisplayName(name, SPOOFED), remainder);
61
- });
62
- }
63
-
64
- for (const [what, name] of kept) {
65
- it(`leaves ${what} alone`, () => {
66
- assert.equal(storedDisplayName(name, OWN), name);
67
- });
68
- }
69
-
70
- it("keeps the address it labels", () => {
71
- assert.equal(storedDisplayName(OWN, OWN), OWN);
72
- });
73
-
74
- it("keeps its own address inside a longer name", () => {
75
- const name = "Matthijs <matthijs@ischen.nl>";
76
- assert.equal(storedDisplayName(name, OWN), name);
77
- });
78
-
79
- it("folds case beyond ASCII on both sides", () => {
80
- assert.equal(
81
- storedDisplayName("Özcan@example.com", "özcan@example.com"),
82
- "Özcan@example.com",
83
- );
84
- assert.equal(
85
- storedDisplayName("ÖZCAN@EXAMPLE.COM", "özcan@example.com"),
86
- "ÖZCAN@EXAMPLE.COM",
87
- );
88
- });
89
-
90
- it("removes any address when the envelope carried none", () => {
91
- assert.equal(storedDisplayName(`Support ${OWN}`, undefined), "Support");
92
- assert.equal(storedDisplayName("Matthijs", undefined), "Matthijs");
93
- });
94
-
95
- it("removes a second address and keeps its own", () => {
96
- assert.equal(
97
- storedDisplayName(`${OWN} on behalf of ${SPOOFED}`, OWN),
98
- `${OWN} on behalf of`,
99
- );
100
- });
101
- });
@@ -1,40 +0,0 @@
1
- const EMBEDDED_ADDRESS =
2
- /[^\s@<>()[\],;:"'\\]+@[^\s@<>()[\],;:"'\\]+\.[^\s@<>()[\],;:"'\\.]{2,}/gu;
3
-
4
- const DELIMITER_PAIR_LEFT_EMPTY = /[<([{"']\s*[>)\]}"']/gu;
5
-
6
- const LEADING_OR_TRAILING_SEPARATORS = /^[\s,;:|/\\-]+|[\s,;:|/\\-]+$/gu;
7
-
8
- const tidyAfterAddressRemoval = (text: string): string =>
9
- text
10
- .replace(DELIMITER_PAIR_LEFT_EMPTY, " ")
11
- .replace(/\s+/gu, " ")
12
- .replace(LEADING_OR_TRAILING_SEPARATORS, "")
13
- .trim();
14
-
15
- const carriesAddressOtherThan = (
16
- text: string,
17
- ownAddress: string | undefined,
18
- ): boolean => {
19
- for (const [address] of text.matchAll(EMBEDDED_ADDRESS)) {
20
- if (address.toLowerCase() !== ownAddress) return true;
21
- }
22
- return false;
23
- };
24
-
25
- export const storedDisplayName = (
26
- displayName: string | undefined,
27
- normalizedEmail: string | undefined,
28
- ): string => {
29
- if (!displayName) return "";
30
- const ownAddress = normalizedEmail?.toLowerCase();
31
- if (!carriesAddressOtherThan(displayName, ownAddress)) return displayName;
32
-
33
- const remainder = tidyAfterAddressRemoval(
34
- displayName.replace(EMBEDDED_ADDRESS, (address) =>
35
- address.toLowerCase() === ownAddress ? address : " ",
36
- ),
37
- );
38
- if (!remainder || carriesAddressOtherThan(remainder, ownAddress)) return "";
39
- return remainder;
40
- };
package/src/errors.ts DELETED
@@ -1,226 +0,0 @@
1
- import type {
2
- CanonicalMailboxRoleValue,
3
- FolderRoleUnresolvedReason,
4
- } from "./folder-role.js";
5
-
6
- export type { FolderRoleUnresolvedReason };
7
-
8
- /**
9
- * The half of an error a client may read: a stable `code` it branches on and
10
- * string `details` it words its own prompt from. Opt-in — an error that
11
- * declares none keeps today's `{ message }` body, so no response gains a
12
- * shape the API never promised and no internal failure leaks its innards.
13
- */
14
- export interface PublicApiError {
15
- code: string;
16
- details?: Record<string, string>;
17
- }
18
-
19
- export const isPublicApiError = (value: unknown): value is PublicApiError => {
20
- if (typeof value !== "object" || value === null) return false;
21
- if (!("code" in value) || typeof value.code !== "string") return false;
22
- if (!("details" in value) || value.details === undefined) return true;
23
- if (typeof value.details !== "object" || value.details === null) return false;
24
- return Object.values(value.details).every(
25
- (detail) => typeof detail === "string",
26
- );
27
- };
28
-
29
- export class HTTPError extends Error {
30
- public statusCode = 500;
31
- publicApiError?: PublicApiError;
32
- }
33
-
34
- export class BadRequestError extends HTTPError {
35
- name = "BadRequestError";
36
- public statusCode = 400;
37
- }
38
-
39
- export class ClientError extends HTTPError {
40
- name = "ClientError";
41
- public statusCode = 401;
42
- }
43
-
44
- export class ForbiddenError extends HTTPError {
45
- name = "ForbiddenError";
46
- public statusCode = 403;
47
- }
48
-
49
- export class NotFoundError extends HTTPError {
50
- name = "NotFoundError";
51
- public statusCode = 404;
52
- }
53
-
54
- export class ConflictError extends HTTPError {
55
- name = "ConflictError";
56
- public statusCode = 409;
57
- }
58
-
59
- /**
60
- * A destructive action refused because the role it needs is unresolved. The
61
- * client reads `code` and words its prompt from `details` — never from the
62
- * message — so the copy can change without breaking the branch, and the
63
- * account the prompt has to appoint a folder on travels with the refusal
64
- * (the delete endpoint's body carries no accountId).
65
- */
66
- export class FolderRoleUnresolvedError extends ConflictError {
67
- name = "FolderRoleUnresolvedError";
68
-
69
- constructor(
70
- message: string,
71
- role: CanonicalMailboxRoleValue,
72
- reason: FolderRoleUnresolvedReason,
73
- accountId: string,
74
- ) {
75
- super(message);
76
- this.publicApiError = {
77
- code: "folder_role_unresolved",
78
- details: { role, reason, accountId },
79
- };
80
- }
81
- }
82
-
83
- /**
84
- * A durable reference cannot be bound to a mailbox whose own mutation has not
85
- * settled (folder-rename-and-delete.md D12, imap-mutations R2: wait). Its own
86
- * code, because the role is not unresolved — the target is: the client words a
87
- * wait, not a retry. Clearing a role is never refused this way.
88
- *
89
- * **422, not 409** (D4). 409 already means "this folder is being changed right
90
- * now, refresh"; this means "the folder you are pointing at has not settled
91
- * yet", which is a different sentence and a different remedy. The status is
92
- * what separates them for a client that reads no further.
93
- */
94
- export class MailboxNotSettledError extends HTTPError {
95
- name = "MailboxNotSettledError";
96
- public statusCode = 422;
97
-
98
- constructor(message: string, mailboxId: string, syncStatus: string) {
99
- super(message);
100
- this.publicApiError = {
101
- code: "mailbox_not_settled",
102
- details: { mailboxId, syncStatus },
103
- };
104
- }
105
- }
106
-
107
- /**
108
- * Why the row's folder and uid do not name the same message. One value, because
109
- * one state produces it: a mutation is still in flight and the pair clears on
110
- * its own, so the client words a wait rather than a repair. A mutation that
111
- * gave up does not reach here — it hands the row back to a placement the server
112
- * confirmed first (docs/architecture/imap-mutations.md R3), which is a pair
113
- * every dependent mutation can act on.
114
- */
115
- export type MessagePlacementUnsettledReason = "in_flight";
116
-
117
- export class MessagePlacementUnsettledError extends ConflictError {
118
- name = "MessagePlacementUnsettledError";
119
-
120
- constructor(
121
- message: string,
122
- accountId: string,
123
- messageId: string,
124
- reason: MessagePlacementUnsettledReason,
125
- ) {
126
- super(message);
127
- this.publicApiError = {
128
- code: "message_placement_unsettled",
129
- details: { accountId, messageId, reason },
130
- };
131
- }
132
- }
133
-
134
- /**
135
- * A configuration import refused because this configuration is not empty
136
- * (#1021). The default is to refuse rather than to fold the file in: a person
137
- * importing over a configuration they forgot they had should hear about it
138
- * before anything moves. `details` counts what is already here, so the client
139
- * can say what would be merged, and the same request with `onExisting: merge`
140
- * goes through.
141
- */
142
- export class ConfigNotEmptyError extends ConflictError {
143
- name = "ConfigNotEmptyError";
144
-
145
- constructor(message: string, details: Record<string, string>) {
146
- super(message);
147
- this.publicApiError = { code: "config_not_empty", details };
148
- }
149
- }
150
-
151
- /**
152
- * A message exists but its body could not be fetched/parsed after every
153
- * body-sync retry was spent (issue #1270 / epic #1281 invariant 3). This is
154
- * distinct from `NotFoundError`: the message is real, but its content is
155
- * permanently unavailable — the client must show an explicit, actionable
156
- * error instead of retrying (no 202, no silent skip).
157
- */
158
- export class UnrecoverableBodyError extends HTTPError {
159
- name = "UnrecoverableBodyError";
160
- public statusCode = 422;
161
- }
162
-
163
- export class CreateFailedConflictError extends ConflictError {
164
- public statusCode = 409;
165
- name = "CreateFailedConflictError";
166
- constructor(resourceType: string, params: unknown) {
167
- super(
168
- `${resourceType} with properties "${JSON.stringify(params)}" already exists.`,
169
- );
170
- }
171
- }
172
-
173
- export class UnhandledError extends HTTPError {
174
- name = "UnhandledError";
175
- public statusCode = 500;
176
- readonly cause: Error | undefined;
177
-
178
- constructor(message: string, cause?: Error) {
179
- super(message);
180
- this.message = message;
181
- if (cause) this.cause = cause;
182
- }
183
-
184
- toJSON() {
185
- return {
186
- message: this.message,
187
- cause: this.cause?.message,
188
- stack: this.cause?.stack || this.stack,
189
- };
190
- }
191
- }
192
-
193
- export class InternalServerError extends UnhandledError {
194
- name = "InternalServerError";
195
- public statusCode = 500;
196
- }
197
-
198
- /**
199
- * A repository lookup or write for a row that no longer exists rejects with a
200
- * `NotFoundError`, name-matched rather than by `instanceof`: the class crosses
201
- * the adapter boundary and an adapter may throw its own.
202
- *
203
- * A worker event that references a deliberately-deleted mailbox is completed or
204
- * moot work, never a transient fault: every redelivery re-throws the same
205
- * `NotFoundError`, and because the sync queues carry `MessageGroupId=accountId`
206
- * that permanently-failing head message stalls the whole account's per-group
207
- * FIFO. Handlers use this predicate to resolve such an event terminally (ack
208
- * with a WARN) instead of retrying forever (issues #287, #289, #290).
209
- *
210
- * The guard is narrow on purpose: only a genuine not-found terminates. Real
211
- * IMAP/infra failures carry other errors and must still propagate to be retried.
212
- */
213
- export const isNotFoundError = (error: unknown): boolean =>
214
- (error as { name?: string })?.name === "NotFoundError";
215
-
216
- export class MailSyncOffError extends BadRequestError {
217
- name = "MailSyncOffError";
218
-
219
- constructor(message: string, accountId: string) {
220
- super(message);
221
- this.publicApiError = {
222
- code: "mail_sync_off",
223
- details: { accountId },
224
- };
225
- }
226
- }