@remit/data-ports 0.0.37 → 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 +1 -1
- package/src/errors.ts +58 -0
package/package.json
CHANGED
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
|