@remit/data-ports 0.0.36 → 0.0.38

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.36",
3
+ "version": "0.0.38",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
package/src/errors.ts CHANGED
@@ -1,5 +1,29 @@
1
+ import type { CanonicalMailboxRoleValue } from "./folder-role.js";
2
+
3
+ /**
4
+ * The half of an error a client may read: a stable `code` it branches on and
5
+ * string `details` it words its own prompt from. Opt-in — an error that
6
+ * declares none keeps today's `{ message }` body, so no response gains a
7
+ * shape the API never promised and no internal failure leaks its innards.
8
+ */
9
+ export interface PublicApiError {
10
+ code: string;
11
+ details?: Record<string, string>;
12
+ }
13
+
14
+ export const isPublicApiError = (value: unknown): value is PublicApiError => {
15
+ if (typeof value !== "object" || value === null) return false;
16
+ if (!("code" in value) || typeof value.code !== "string") return false;
17
+ if (!("details" in value) || value.details === undefined) return true;
18
+ if (typeof value.details !== "object" || value.details === null) return false;
19
+ return Object.values(value.details).every(
20
+ (detail) => typeof detail === "string",
21
+ );
22
+ };
23
+
1
24
  export class HTTPError extends Error {
2
25
  public statusCode = 500;
26
+ publicApiError?: PublicApiError;
3
27
  }
4
28
 
5
29
  export class BadRequestError extends HTTPError {
@@ -27,6 +51,40 @@ export class ConflictError extends HTTPError {
27
51
  public statusCode = 409;
28
52
  }
29
53
 
54
+ /**
55
+ * Why a canonical role names no folder this action may act on. `none`: the
56
+ * account has no candidate at all. `stale`: the folder the user appointed is
57
+ * gone from the server. `unconfirmed`: a folder matches by name, but nobody —
58
+ * neither the user nor the server's own flag — ever said it holds the role.
59
+ * These three and no others; a target that is merely unsettled is a different
60
+ * refusal under its own code.
61
+ */
62
+ export type FolderRoleUnresolvedReason = "none" | "stale" | "unconfirmed";
63
+
64
+ /**
65
+ * A destructive action refused because the role it needs is unresolved. The
66
+ * client reads `code` and words its prompt from `details` — never from the
67
+ * message — so the copy can change without breaking the branch, and the
68
+ * account the prompt has to appoint a folder on travels with the refusal
69
+ * (the delete endpoint's body carries no accountId).
70
+ */
71
+ export class FolderRoleUnresolvedError extends ConflictError {
72
+ name = "FolderRoleUnresolvedError";
73
+
74
+ constructor(
75
+ message: string,
76
+ role: CanonicalMailboxRoleValue,
77
+ reason: FolderRoleUnresolvedReason,
78
+ accountId: string,
79
+ ) {
80
+ super(message);
81
+ this.publicApiError = {
82
+ code: "folder_role_unresolved",
83
+ details: { role, reason, accountId },
84
+ };
85
+ }
86
+ }
87
+
30
88
  /**
31
89
  * A message exists but its body could not be fetched/parsed after every
32
90
  * body-sync retry was spent (issue #1270 / epic #1281 invariant 3). This is
package/src/id.ts CHANGED
@@ -7,13 +7,6 @@ const translator = shortUuid.createTranslator(shortUuid.constants.uuid25Base36);
7
7
 
8
8
  export const base36uuid = (): string => translator.generate();
9
9
 
10
- export const fromUUID = (uuid: string): string => translator.fromUUID(uuid);
11
-
12
- export const toUUID = (shortId: string): string => translator.toUUID(shortId);
13
-
14
- export const generateUuidv5 = (name: string, namespace: string): string =>
15
- uuidv5(name, namespace);
16
-
17
10
  export const base36uuidv5 = (name: string, namespace: string): string =>
18
11
  translator.fromUUID(uuidv5(name, namespace));
19
12
 
@@ -118,16 +111,6 @@ export const ROOT_PART_PATH = "0";
118
111
  export const deriveBodyPartId = (messageId: string, partPath: string): string =>
119
112
  base36uuidv5(`bodypart:${messageId}:${partPath}`, REMIT_NAMESPACE);
120
113
 
121
- export const deriveBodyPartParameterId = (
122
- messageId: string,
123
- partPath: string,
124
- parameterName: string,
125
- ): string =>
126
- base36uuidv5(
127
- `bodypartparam:${messageId}:${partPath}:${parameterName.toLowerCase()}`,
128
- REMIT_NAMESPACE,
129
- );
130
-
131
114
  /**
132
115
  * Identity of a quarantined message (issue #72). Derived rather than generated
133
116
  * so re-quarantining the same message rewrites its own row: the sync path can
@@ -1,5 +1,5 @@
1
1
  import { CanonicalMailboxRole } from "@remit/domain-enums";
2
- import { ROLE_NAME_HINTS, resolveMailboxForRole } from "./folder-role.js";
2
+ import { ROLE_NAME_HINTS } from "./folder-role.js";
3
3
 
4
4
  /**
5
5
  * The conventional names for a role, taken from the one table every
@@ -12,34 +12,3 @@ export const JUNK_FOLDER_NAMES: readonly string[] =
12
12
 
13
13
  export const TRASH_FOLDER_NAMES: readonly string[] =
14
14
  ROLE_NAME_HINTS[CanonicalMailboxRole.Trash] ?? [];
15
-
16
- export interface MailboxRole {
17
- readonly fullPath: string;
18
- readonly hierarchyDelimiter?: string;
19
- readonly specialUse?: readonly string[];
20
- }
21
-
22
- /**
23
- * Whether one mailbox, considered alone, carries a role: its SPECIAL-USE flag
24
- * or its conventional name. A single mailbox carries no account context, so
25
- * this cannot see the user's appointment — a caller holding an accountId asks
26
- * `IMailboxSpecialUseRepository` instead, which does.
27
- */
28
- const carriesRole = (
29
- mailbox: MailboxRole,
30
- role: (typeof CanonicalMailboxRole)[keyof typeof CanonicalMailboxRole],
31
- ): boolean =>
32
- resolveMailboxForRole(role, [
33
- {
34
- mailboxId: "",
35
- fullPath: mailbox.fullPath,
36
- hierarchyDelimiter: mailbox.hierarchyDelimiter ?? "",
37
- specialUse: mailbox.specialUse,
38
- },
39
- ]) !== null;
40
-
41
- export const isJunkMailbox = (mailbox: MailboxRole): boolean =>
42
- carriesRole(mailbox, CanonicalMailboxRole.Junk);
43
-
44
- export const isTrashMailbox = (mailbox: MailboxRole): boolean =>
45
- carriesRole(mailbox, CanonicalMailboxRole.Trash);