@remit/data-ports 0.0.83 → 0.0.85
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/dist/account-settings.d.ts +371 -0
- package/dist/account-settings.js +114 -0
- package/dist/conformance/account.d.ts +3 -0
- package/dist/conformance/account.js +118 -0
- package/dist/conformance/calendar-collection.d.ts +3 -0
- package/dist/conformance/calendar-collection.js +251 -0
- package/dist/conformance/calendar-event-index.d.ts +3 -0
- package/dist/conformance/calendar-event-index.js +114 -0
- package/dist/conformance/calendar-feed-token.d.ts +3 -0
- package/dist/conformance/calendar-feed-token.js +97 -0
- package/dist/conformance/calendar-object.d.ts +3 -0
- package/dist/conformance/calendar-object.js +156 -0
- package/dist/conformance/calendar-suggestion.d.ts +3 -0
- package/dist/conformance/calendar-suggestion.js +255 -0
- package/{src/conformance/harness.ts → dist/conformance/harness.d.ts} +4 -4
- package/dist/conformance/harness.js +1 -0
- package/dist/conformance/index.js +8 -0
- package/dist/conformance/label.d.ts +3 -0
- package/dist/conformance/label.js +92 -0
- package/dist/conformance/sender-signer-standing.d.ts +3 -0
- package/dist/conformance/sender-signer-standing.js +81 -0
- package/dist/display-name.d.ts +1 -0
- package/dist/display-name.js +26 -0
- package/dist/errors.d.ts +150 -0
- package/dist/errors.js +181 -0
- package/dist/filter-ttl.d.ts +7 -0
- package/{src/filter-ttl.ts → dist/filter-ttl.js} +8 -11
- package/dist/folder-role.d.ts +239 -0
- package/dist/folder-role.js +276 -0
- package/dist/id.d.ts +124 -0
- package/{src/id.ts → dist/id.js} +41 -127
- package/dist/index.d.ts +37 -0
- package/dist/index.js +5 -0
- package/dist/interfaces/account-config.d.ts +15 -0
- package/dist/interfaces/account-config.js +1 -0
- package/dist/interfaces/account-export-request.d.ts +10 -0
- package/dist/interfaces/account-export-request.js +1 -0
- package/dist/interfaces/account-setting.d.ts +8 -0
- package/dist/interfaces/account-setting.js +1 -0
- package/dist/interfaces/account.d.ts +29 -0
- package/dist/interfaces/account.js +1 -0
- package/dist/interfaces/address.d.ts +70 -0
- package/dist/interfaces/address.js +1 -0
- package/dist/interfaces/calendar-collection.d.ts +38 -0
- package/dist/interfaces/calendar-collection.js +1 -0
- package/dist/interfaces/calendar-event-index.d.ts +19 -0
- package/dist/interfaces/calendar-event-index.js +1 -0
- package/dist/interfaces/calendar-feed-token.d.ts +31 -0
- package/dist/interfaces/calendar-feed-token.js +1 -0
- package/dist/interfaces/calendar-object.d.ts +36 -0
- package/dist/interfaces/calendar-object.js +1 -0
- package/dist/interfaces/calendar-store.d.ts +93 -0
- package/dist/interfaces/calendar-store.js +1 -0
- package/dist/interfaces/calendar-suggestion.d.ts +62 -0
- package/dist/interfaces/calendar-suggestion.js +1 -0
- package/dist/interfaces/config-import.d.ts +8 -0
- package/dist/interfaces/config-import.js +1 -0
- package/dist/interfaces/envelope.d.ts +24 -0
- package/dist/interfaces/envelope.js +1 -0
- package/dist/interfaces/filter.d.ts +28 -0
- package/dist/interfaces/filter.js +1 -0
- package/dist/interfaces/label.d.ts +17 -0
- package/dist/interfaces/label.js +1 -0
- package/dist/interfaces/mailbox-lock.d.ts +12 -0
- package/dist/interfaces/mailbox-lock.js +1 -0
- package/dist/interfaces/mailbox-special-use.d.ts +54 -0
- package/dist/interfaces/mailbox-special-use.js +1 -0
- package/dist/interfaces/mailbox.d.ts +79 -0
- package/dist/interfaces/mailbox.js +1 -0
- package/dist/interfaces/message-flag-push.d.ts +9 -0
- package/dist/interfaces/message-flag-push.js +1 -0
- package/dist/interfaces/message-flag.d.ts +14 -0
- package/dist/interfaces/message-flag.js +1 -0
- package/dist/interfaces/message-label.d.ts +21 -0
- package/dist/interfaces/message-label.js +1 -0
- package/dist/interfaces/message-placement-move.d.ts +8 -0
- package/dist/interfaces/message-placement-move.js +1 -0
- package/dist/interfaces/message.d.ts +60 -0
- package/dist/interfaces/message.js +1 -0
- package/dist/interfaces/organize-job-request.d.ts +10 -0
- package/dist/interfaces/organize-job-request.js +1 -0
- package/dist/interfaces/outbox-attachment.d.ts +101 -0
- package/dist/interfaces/outbox-attachment.js +7 -0
- package/dist/interfaces/outbox-message.d.ts +70 -0
- package/dist/interfaces/outbox-message.js +20 -0
- package/dist/interfaces/quarantine.d.ts +20 -0
- package/dist/interfaces/quarantine.js +1 -0
- package/dist/interfaces/sender-signer-standing.d.ts +26 -0
- package/dist/interfaces/sender-signer-standing.js +1 -0
- package/dist/interfaces/sync-warning.d.ts +11 -0
- package/dist/interfaces/sync-warning.js +1 -0
- package/dist/interfaces/thread-message.d.ts +202 -0
- package/dist/interfaces/thread-message.js +1 -0
- package/dist/mailbox-name.d.ts +35 -0
- package/dist/mailbox-name.js +68 -0
- package/dist/message-parts.d.ts +22 -0
- package/dist/message-parts.js +18 -0
- package/dist/message-settlement.d.ts +35 -0
- package/{src/message-settlement.ts → dist/message-settlement.js} +5 -19
- package/dist/mutation-events.d.ts +19 -0
- package/{src/mutation-events.ts → dist/mutation-events.js} +2 -5
- package/dist/sender-mute.d.ts +26 -0
- package/{src/sender-mute.ts → dist/sender-mute.js} +12 -34
- package/dist/sync-warning-state.d.ts +3 -0
- package/dist/sync-warning-state.js +5 -0
- package/dist/types.d.ts +491 -0
- package/dist/types.js +1 -0
- package/dist/update-manifest.d.ts +24 -0
- package/dist/update-manifest.js +20 -0
- package/dist/virtual-copy.d.ts +8 -0
- package/{src/virtual-copy.ts → dist/virtual-copy.js} +10 -15
- package/dist/wellknown.d.ts +47 -0
- package/{src/wellknown.ts → dist/wellknown.js} +26 -49
- package/package.json +44 -79
- package/src/account-settings.ts +0 -194
- package/src/conformance/account.ts +0 -104
- package/src/conformance/calendar-collection.ts +0 -342
- package/src/conformance/calendar-event-index.ts +0 -203
- package/src/conformance/calendar-feed-token.ts +0 -148
- package/src/conformance/calendar-object.ts +0 -269
- package/src/conformance/calendar-suggestion.ts +0 -490
- package/src/conformance/label.ts +0 -136
- package/src/conformance/sender-signer-standing.ts +0 -117
- package/src/display-name.test.ts +0 -101
- package/src/display-name.ts +0 -40
- package/src/errors.ts +0 -244
- package/src/folder-role.test.ts +0 -422
- package/src/folder-role.ts +0 -447
- package/src/id.test.ts +0 -352
- package/src/index.ts +0 -170
- package/src/interfaces/account-config.ts +0 -25
- package/src/interfaces/account-export-request.ts +0 -21
- package/src/interfaces/account-setting.ts +0 -14
- package/src/interfaces/account.ts +0 -43
- package/src/interfaces/address.ts +0 -122
- package/src/interfaces/calendar-collection.ts +0 -60
- package/src/interfaces/calendar-event-index.ts +0 -34
- package/src/interfaces/calendar-feed-token.ts +0 -38
- package/src/interfaces/calendar-object.ts +0 -58
- package/src/interfaces/calendar-suggestion.ts +0 -93
- package/src/interfaces/calendar-unit-of-work.ts +0 -47
- package/src/interfaces/config-import.ts +0 -16
- package/src/interfaces/envelope.ts +0 -32
- package/src/interfaces/filter-anchor-transaction.ts +0 -23
- package/src/interfaces/filter-anchor.ts +0 -11
- package/src/interfaces/filter.ts +0 -41
- package/src/interfaces/label.ts +0 -30
- package/src/interfaces/mailbox-lock.ts +0 -32
- package/src/interfaces/mailbox-special-use.ts +0 -60
- package/src/interfaces/mailbox.ts +0 -115
- package/src/interfaces/message-flag-push.ts +0 -17
- package/src/interfaces/message-flag.ts +0 -15
- package/src/interfaces/message-label.ts +0 -25
- package/src/interfaces/message-placement-move.ts +0 -15
- package/src/interfaces/message.ts +0 -65
- package/src/interfaces/organize-job-request.ts +0 -19
- package/src/interfaces/outbox-attachment.ts +0 -132
- package/src/interfaces/outbox-message.ts +0 -106
- package/src/interfaces/quarantine.ts +0 -22
- package/src/interfaces/sender-signer-standing.ts +0 -37
- package/src/interfaces/thread-message.ts +0 -264
- package/src/interfaces/unit-of-work.ts +0 -28
- package/src/mailbox-name.test.ts +0 -106
- package/src/mailbox-name.ts +0 -90
- package/src/mailbox-state-doors.test.ts +0 -104
- package/src/message-placement-doors.test.ts +0 -100
- package/src/message-settlement.test.ts +0 -132
- package/src/sender-mute.test.ts +0 -76
- package/src/types.ts +0 -820
- package/src/update-manifest.test.ts +0 -130
- package/src/update-manifest.ts +0 -22
- package/src/virtual-copy.test.ts +0 -34
- package/tsconfig.json +0 -8
- /package/{src/conformance/index.ts → dist/conformance/index.d.ts} +0 -0
package/dist/errors.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
export const isPublicApiError = (value) => {
|
|
2
|
+
if (typeof value !== "object" || value === null)
|
|
3
|
+
return false;
|
|
4
|
+
if (!("code" in value) || typeof value.code !== "string")
|
|
5
|
+
return false;
|
|
6
|
+
if (!("details" in value) || value.details === undefined)
|
|
7
|
+
return true;
|
|
8
|
+
if (typeof value.details !== "object" || value.details === null)
|
|
9
|
+
return false;
|
|
10
|
+
return Object.values(value.details).every((detail) => typeof detail === "string");
|
|
11
|
+
};
|
|
12
|
+
export class HTTPError extends Error {
|
|
13
|
+
statusCode = 500;
|
|
14
|
+
publicApiError;
|
|
15
|
+
}
|
|
16
|
+
export class BadRequestError extends HTTPError {
|
|
17
|
+
name = "BadRequestError";
|
|
18
|
+
statusCode = 400;
|
|
19
|
+
}
|
|
20
|
+
export class ClientError extends HTTPError {
|
|
21
|
+
name = "ClientError";
|
|
22
|
+
statusCode = 401;
|
|
23
|
+
}
|
|
24
|
+
export class ForbiddenError extends HTTPError {
|
|
25
|
+
name = "ForbiddenError";
|
|
26
|
+
statusCode = 403;
|
|
27
|
+
}
|
|
28
|
+
export class NotFoundError extends HTTPError {
|
|
29
|
+
name = "NotFoundError";
|
|
30
|
+
statusCode = 404;
|
|
31
|
+
}
|
|
32
|
+
export class ConflictError extends HTTPError {
|
|
33
|
+
name = "ConflictError";
|
|
34
|
+
statusCode = 409;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A destructive action refused because the role it needs is unresolved. The
|
|
38
|
+
* client reads `code` and words its prompt from `details` — never from the
|
|
39
|
+
* message — so the copy can change without breaking the branch, and the
|
|
40
|
+
* account the prompt has to appoint a folder on travels with the refusal
|
|
41
|
+
* (the delete endpoint's body carries no accountId).
|
|
42
|
+
*/
|
|
43
|
+
export class FolderRoleUnresolvedError extends ConflictError {
|
|
44
|
+
name = "FolderRoleUnresolvedError";
|
|
45
|
+
constructor(message, role, reason, accountId) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.publicApiError = {
|
|
48
|
+
code: "folder_role_unresolved",
|
|
49
|
+
details: { role, reason, accountId },
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A durable reference cannot be bound to a mailbox whose own mutation has not
|
|
55
|
+
* settled (folder-rename-and-delete.md D12, imap-mutations R2: wait). Its own
|
|
56
|
+
* code, because the role is not unresolved — the target is: the client words a
|
|
57
|
+
* wait, not a retry. Clearing a role is never refused this way.
|
|
58
|
+
*
|
|
59
|
+
* **422, not 409** (D4). 409 already means "this folder is being changed right
|
|
60
|
+
* now, refresh"; this means "the folder you are pointing at has not settled
|
|
61
|
+
* yet", which is a different sentence and a different remedy. The status is
|
|
62
|
+
* what separates them for a client that reads no further.
|
|
63
|
+
*/
|
|
64
|
+
export class MailboxNotSettledError extends HTTPError {
|
|
65
|
+
name = "MailboxNotSettledError";
|
|
66
|
+
statusCode = 422;
|
|
67
|
+
constructor(message, mailboxId, syncStatus) {
|
|
68
|
+
super(message);
|
|
69
|
+
this.publicApiError = {
|
|
70
|
+
code: "mailbox_not_settled",
|
|
71
|
+
details: { mailboxId, syncStatus },
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export class MessagePlacementUnsettledError extends ConflictError {
|
|
76
|
+
name = "MessagePlacementUnsettledError";
|
|
77
|
+
constructor(message, accountId, messageId, reason) {
|
|
78
|
+
super(message);
|
|
79
|
+
this.publicApiError = {
|
|
80
|
+
code: "message_placement_unsettled",
|
|
81
|
+
details: { accountId, messageId, reason },
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The row named a message the mail server no longer holds at that UID —
|
|
87
|
+
* another client expunged it or moved it away. Distinct from a plain
|
|
88
|
+
* not-found so the client can say where the message went rather than
|
|
89
|
+
* claiming the user deleted it.
|
|
90
|
+
*/
|
|
91
|
+
export class MessageGoneFromServerError extends NotFoundError {
|
|
92
|
+
name = "MessageGoneFromServerError";
|
|
93
|
+
constructor(messageId) {
|
|
94
|
+
super("This message is no longer on the mail server.");
|
|
95
|
+
this.publicApiError = {
|
|
96
|
+
code: "message_gone_from_server",
|
|
97
|
+
details: { messageId },
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* A configuration import refused because this configuration is not empty
|
|
103
|
+
* (#1021). The default is to refuse rather than to fold the file in: a person
|
|
104
|
+
* importing over a configuration they forgot they had should hear about it
|
|
105
|
+
* before anything moves. `details` counts what is already here, so the client
|
|
106
|
+
* can say what would be merged, and the same request with `onExisting: merge`
|
|
107
|
+
* goes through.
|
|
108
|
+
*/
|
|
109
|
+
export class ConfigNotEmptyError extends ConflictError {
|
|
110
|
+
name = "ConfigNotEmptyError";
|
|
111
|
+
constructor(message, details) {
|
|
112
|
+
super(message);
|
|
113
|
+
this.publicApiError = { code: "config_not_empty", details };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* A message exists but its body could not be fetched/parsed after every
|
|
118
|
+
* body-sync retry was spent (issue #1270 / epic #1281 invariant 3). This is
|
|
119
|
+
* distinct from `NotFoundError`: the message is real, but its content is
|
|
120
|
+
* permanently unavailable — the client must show an explicit, actionable
|
|
121
|
+
* error instead of retrying (no 202, no silent skip).
|
|
122
|
+
*/
|
|
123
|
+
export class UnrecoverableBodyError extends HTTPError {
|
|
124
|
+
name = "UnrecoverableBodyError";
|
|
125
|
+
statusCode = 422;
|
|
126
|
+
}
|
|
127
|
+
export class CreateFailedConflictError extends ConflictError {
|
|
128
|
+
statusCode = 409;
|
|
129
|
+
name = "CreateFailedConflictError";
|
|
130
|
+
constructor(resourceType, params) {
|
|
131
|
+
super(`${resourceType} with properties "${JSON.stringify(params)}" already exists.`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export class UnhandledError extends HTTPError {
|
|
135
|
+
name = "UnhandledError";
|
|
136
|
+
statusCode = 500;
|
|
137
|
+
cause;
|
|
138
|
+
constructor(message, cause) {
|
|
139
|
+
super(message);
|
|
140
|
+
this.message = message;
|
|
141
|
+
if (cause)
|
|
142
|
+
this.cause = cause;
|
|
143
|
+
}
|
|
144
|
+
toJSON() {
|
|
145
|
+
return {
|
|
146
|
+
message: this.message,
|
|
147
|
+
cause: this.cause?.message,
|
|
148
|
+
stack: this.cause?.stack || this.stack,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export class InternalServerError extends UnhandledError {
|
|
153
|
+
name = "InternalServerError";
|
|
154
|
+
statusCode = 500;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* A repository lookup or write for a row that no longer exists rejects with a
|
|
158
|
+
* `NotFoundError`, name-matched rather than by `instanceof`: the class crosses
|
|
159
|
+
* the adapter boundary and an adapter may throw its own.
|
|
160
|
+
*
|
|
161
|
+
* A worker event that references a deliberately-deleted mailbox is completed or
|
|
162
|
+
* moot work, never a transient fault: every redelivery re-throws the same
|
|
163
|
+
* `NotFoundError`, and because the sync queues carry `MessageGroupId=accountId`
|
|
164
|
+
* that permanently-failing head message stalls the whole account's per-group
|
|
165
|
+
* FIFO. Handlers use this predicate to resolve such an event terminally (ack
|
|
166
|
+
* with a WARN) instead of retrying forever (issues #287, #289, #290).
|
|
167
|
+
*
|
|
168
|
+
* The guard is narrow on purpose: only a genuine not-found terminates. Real
|
|
169
|
+
* IMAP/infra failures carry other errors and must still propagate to be retried.
|
|
170
|
+
*/
|
|
171
|
+
export const isNotFoundError = (error) => error?.name === "NotFoundError";
|
|
172
|
+
export class MailSyncOffError extends BadRequestError {
|
|
173
|
+
name = "MailSyncOffError";
|
|
174
|
+
constructor(message, accountId) {
|
|
175
|
+
super(message);
|
|
176
|
+
this.publicApiError = {
|
|
177
|
+
code: "mail_sync_off",
|
|
178
|
+
details: { accountId },
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Epoch-seconds `ttl` derived from `expiresAt`, set only for a `Temporary`
|
|
3
|
+
* filter (RFC 034 Decision 1.3). A `Standing` filter never carries `ttl` — the
|
|
4
|
+
* reserved table-wide TTL attribute must stay absent, or the row would be swept
|
|
5
|
+
* (Decision 1.4).
|
|
6
|
+
*/
|
|
7
|
+
export declare const deriveFilterTtl: (scope: string, expiresAt: string | undefined) => number | undefined;
|
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import { FilterScope } from "@remit/domain-enums";
|
|
2
2
|
import { BadRequestError } from "./errors.js";
|
|
3
|
-
|
|
4
3
|
/**
|
|
5
4
|
* Epoch-seconds `ttl` derived from `expiresAt`, set only for a `Temporary`
|
|
6
5
|
* filter (RFC 034 Decision 1.3). A `Standing` filter never carries `ttl` — the
|
|
7
6
|
* reserved table-wide TTL attribute must stay absent, or the row would be swept
|
|
8
7
|
* (Decision 1.4).
|
|
9
8
|
*/
|
|
10
|
-
export const deriveFilterTtl = (
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
return Math.floor(ms / 1000);
|
|
9
|
+
export const deriveFilterTtl = (scope, expiresAt) => {
|
|
10
|
+
if (scope !== FilterScope.Temporary || !expiresAt)
|
|
11
|
+
return undefined;
|
|
12
|
+
const ms = new Date(expiresAt).getTime();
|
|
13
|
+
if (Number.isNaN(ms)) {
|
|
14
|
+
throw new BadRequestError(`Invalid expiresAt: ${expiresAt}`);
|
|
15
|
+
}
|
|
16
|
+
return Math.floor(ms / 1000);
|
|
20
17
|
};
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { CanonicalMailboxRole, FolderAppointmentSource } from "@remit/domain-enums";
|
|
2
|
+
import { type MailboxNameCandidate } from "./mailbox-name.js";
|
|
3
|
+
export type CanonicalMailboxRoleValue = (typeof CanonicalMailboxRole)[keyof typeof CanonicalMailboxRole];
|
|
4
|
+
/** The fixed anchor set, in the RFC's canonical display order. */
|
|
5
|
+
export declare const CANONICAL_ROLES: readonly CanonicalMailboxRoleValue[];
|
|
6
|
+
/**
|
|
7
|
+
* The stored `AccountSetting` name holding one role's appointment. Composed
|
|
8
|
+
* here rather than at either reader, so the backend that writes it and the
|
|
9
|
+
* repository that reads it can never disagree about the key.
|
|
10
|
+
*/
|
|
11
|
+
export declare const composeFolderRoleAppointmentName: (accountId: string, role: CanonicalMailboxRoleValue) => string;
|
|
12
|
+
/**
|
|
13
|
+
* The stored `AccountSetting` name holding the path one role's appointed
|
|
14
|
+
* mailbox had at the moment it was appointed. A sibling row rather than a field
|
|
15
|
+
* on the appointment, so "never read by resolution" is structural: the
|
|
16
|
+
* repository composes only the appointment name and cannot reach this one.
|
|
17
|
+
*/
|
|
18
|
+
export declare const composeFolderRoleAppointmentLabelName: (accountId: string, role: CanonicalMailboxRoleValue) => string;
|
|
19
|
+
/**
|
|
20
|
+
* Split a stored appointment name back into its two-part target. Unlike the
|
|
21
|
+
* single-target composites (`MailboxRole#<id>`), this setting composes two ids
|
|
22
|
+
* after the base, so it parses the suffix itself rather than reusing
|
|
23
|
+
* `targetIdOf`.
|
|
24
|
+
*/
|
|
25
|
+
export declare const parseFolderRoleAppointmentName: (name: string) => {
|
|
26
|
+
accountId: string;
|
|
27
|
+
role: CanonicalMailboxRoleValue;
|
|
28
|
+
} | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* The label row's counterpart, so the config-wide settings batch can recover
|
|
31
|
+
* the recorded path for a role the same way it recovers the appointment.
|
|
32
|
+
*/
|
|
33
|
+
export declare const parseFolderRoleAppointmentLabelName: (name: string) => {
|
|
34
|
+
accountId: string;
|
|
35
|
+
role: CanonicalMailboxRoleValue;
|
|
36
|
+
} | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* RFC 6154 SPECIAL-USE flag per role. Inbox has no SPECIAL-USE flag (RFC 3501
|
|
39
|
+
* reserves the name itself); a role with no entry here is matched by name hint
|
|
40
|
+
* only.
|
|
41
|
+
*/
|
|
42
|
+
export declare const ROLE_SPECIAL_USE: Partial<Record<CanonicalMailboxRoleValue, string>>;
|
|
43
|
+
/**
|
|
44
|
+
* Weak name hints, ordered best-first and lower case: the last resort for a
|
|
45
|
+
* provider that advertises no SPECIAL-USE and whose user has appointed
|
|
46
|
+
* nothing. Deliberately small — a guess that seeds a proposal, never a
|
|
47
|
+
* substitute for server truth or for what the user chose.
|
|
48
|
+
*/
|
|
49
|
+
export declare const ROLE_NAME_HINTS: Partial<Record<CanonicalMailboxRoleValue, readonly string[]>>;
|
|
50
|
+
/**
|
|
51
|
+
* The role a folder's own leaf name is most conventionally for. A name that
|
|
52
|
+
* several roles list goes to the role that ranks it highest, so `All Mail` is
|
|
53
|
+
* an All folder rather than a lookalike of Archive.
|
|
54
|
+
*
|
|
55
|
+
* `name` is the leaf segment, lower case.
|
|
56
|
+
*/
|
|
57
|
+
export declare const roleForFolderName: (name: string) => CanonicalMailboxRoleValue | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* Whether the mailbox sweep would read a folder with this leaf name as a
|
|
60
|
+
* duplicate of a role's canonical folder — and so delete its row, and under D8
|
|
61
|
+
* the mail in it, as soon as another folder carries the role's SPECIAL-USE
|
|
62
|
+
* flag. It is the sweep's own predicate, so a rename refused on it and a folder
|
|
63
|
+
* reaped by it can never disagree (docs/architecture/folder-rename-and-delete.md
|
|
64
|
+
* D5).
|
|
65
|
+
*
|
|
66
|
+
* A role with no flag to be a duplicate of is not reserved: nothing reaps a
|
|
67
|
+
* folder for it.
|
|
68
|
+
*/
|
|
69
|
+
export declare const isReservedFolderName: (name: string) => boolean;
|
|
70
|
+
/**
|
|
71
|
+
* The pane where a user appoints a folder to a role, named as it is labelled.
|
|
72
|
+
* Every refusal that asks for an appointment points here, in one wording — the
|
|
73
|
+
* settings screen is titled "Folder roles", and copies of this sentence had
|
|
74
|
+
* already drifted to "Folders".
|
|
75
|
+
*/
|
|
76
|
+
export declare const FOLDER_ROLES_SETTINGS_PATH = "Settings \u203A Folder roles";
|
|
77
|
+
/**
|
|
78
|
+
* Why an operation that files or destroys mail was refused. A delete, an
|
|
79
|
+
* Empty Trash and the count the handler reports before one all act on the
|
|
80
|
+
* folder the user appointed or the server flagged, and all refuse in these
|
|
81
|
+
* words — one sentence, so two surfaces cannot tell a user two things about
|
|
82
|
+
* one account.
|
|
83
|
+
*/
|
|
84
|
+
export declare const NO_TRASH_FOLDER_REASON = "This account has no Trash folder, so nothing was deleted. Appoint one under Settings \u203A Folder roles, then try again.";
|
|
85
|
+
/**
|
|
86
|
+
* The folder somebody chose as this account's Trash is no longer on the mail
|
|
87
|
+
* server. Reader will not quietly file mail into whatever it would have picked
|
|
88
|
+
* had nobody chosen: the user's choice went missing and only they can replace
|
|
89
|
+
* it.
|
|
90
|
+
*/
|
|
91
|
+
export declare const STALE_TRASH_FOLDER_REASON = "The folder appointed as this account's Trash is no longer on the mail server, so nothing was deleted. Appoint one under Settings \u203A Folder roles, then try again.";
|
|
92
|
+
/**
|
|
93
|
+
* A folder matches the Trash name hint and nothing else — enough to file a
|
|
94
|
+
* delete somewhere retrievable, never enough to erase what is already in it.
|
|
95
|
+
* Emptying that folder and deleting a message already inside it both land here
|
|
96
|
+
* (#876), so the sentence names neither verb: "nothing was deleted" is true of
|
|
97
|
+
* both, the way its two siblings above are.
|
|
98
|
+
*/
|
|
99
|
+
export declare const UNCONFIRMED_TRASH_FOLDER_REASON = "Nobody has confirmed which folder is this account's Trash, so nothing was deleted. Appoint one under Settings \u203A Folder roles, then try again.";
|
|
100
|
+
/**
|
|
101
|
+
* The mailboxes holding Junk and Trash across a set of accounts, as ids. The
|
|
102
|
+
* shape a SQL predicate needs when it must ask "is this message in Junk"
|
|
103
|
+
* without spelling the rule a second time: resolution happens once, here, and
|
|
104
|
+
* SQL compares mailbox ids.
|
|
105
|
+
*
|
|
106
|
+
* A role resolves to at most one mailbox per account, so each list holds one id
|
|
107
|
+
* per account that has that folder — and no entry at all for an account that
|
|
108
|
+
* has none.
|
|
109
|
+
*/
|
|
110
|
+
export interface JunkRoleMailboxes {
|
|
111
|
+
readonly junkMailboxIds: readonly string[];
|
|
112
|
+
readonly trashMailboxIds: readonly string[];
|
|
113
|
+
}
|
|
114
|
+
/** The minimal mailbox shape role resolution reads. */
|
|
115
|
+
export interface RoleMailboxCandidate extends MailboxNameCandidate {
|
|
116
|
+
specialUse?: readonly string[];
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* What an account with no appointment for the role resolves to, and equally
|
|
120
|
+
* what a stale appointment falls back to — the fallback can never itself be an
|
|
121
|
+
* appointment, stale or otherwise.
|
|
122
|
+
*/
|
|
123
|
+
export type UnappointedRoleResolution<T> = {
|
|
124
|
+
kind: "flagged";
|
|
125
|
+
mailbox: T;
|
|
126
|
+
} | {
|
|
127
|
+
kind: "reserved";
|
|
128
|
+
mailbox: T;
|
|
129
|
+
} | {
|
|
130
|
+
kind: "proposed";
|
|
131
|
+
mailbox: T;
|
|
132
|
+
} | {
|
|
133
|
+
kind: "none";
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* What resolving a role for an account answered, and on what evidence. Total:
|
|
137
|
+
* there is a member for every outcome, so a caller that must weigh the evidence
|
|
138
|
+
* — Empty Trash may only expunge what somebody designated — reads the tag
|
|
139
|
+
* instead of inferring it from a `null` that means four different things.
|
|
140
|
+
*
|
|
141
|
+
* `appointment_stale` is the case a nullable answer cannot express: the user
|
|
142
|
+
* appointed a mailbox the account no longer holds. It carries the id they chose
|
|
143
|
+
* so a surface can offer the repair, and the resolution that would have applied
|
|
144
|
+
* had they appointed nothing.
|
|
145
|
+
*/
|
|
146
|
+
export type RoleResolution<T> = {
|
|
147
|
+
kind: "appointed";
|
|
148
|
+
mailbox: T;
|
|
149
|
+
} | UnappointedRoleResolution<T> | {
|
|
150
|
+
kind: "appointment_stale";
|
|
151
|
+
appointedMailboxId: string;
|
|
152
|
+
fallback: UnappointedRoleResolution<T>;
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* The single mailbox that holds a canonical role for an account (RFC 032
|
|
156
|
+
* exclusive-folder-appointment, #976), in one precedence order every caller
|
|
157
|
+
* shares:
|
|
158
|
+
*
|
|
159
|
+
* 1. the mailbox the user appointed;
|
|
160
|
+
* 2. the server's own SPECIAL-USE flag (RFC 6154) — language-independent truth;
|
|
161
|
+
* 3. the reserved `INBOX` name, for the Inbox role only (RFC 3501);
|
|
162
|
+
* 4. a conventional name, matched on the folder's own leaf segment so it
|
|
163
|
+
* resolves at any depth under any prefix.
|
|
164
|
+
*
|
|
165
|
+
* The last of those is a guess: a folder named `Deleted` is not evidence that
|
|
166
|
+
* the user means it as Trash. Use it only where being wrong misfiles a message
|
|
167
|
+
* a user can move back — never where it destroys mail.
|
|
168
|
+
*/
|
|
169
|
+
export declare const resolveRoleForAccount: <T extends RoleMailboxCandidate>(role: CanonicalMailboxRoleValue, mailboxes: readonly T[], appointedMailboxId?: string) => RoleResolution<T>;
|
|
170
|
+
/**
|
|
171
|
+
* How much evidence a Trash verb demands. `confirmed` is what an expunge
|
|
172
|
+
* requires: somebody designated this folder, either the user or the server.
|
|
173
|
+
* `resolved` additionally accepts the name guess, for filing mail somewhere the
|
|
174
|
+
* user can retrieve it from. Trash only, deliberately — no other role and no
|
|
175
|
+
* other verb weighs its evidence, and a matrix over eight roles would be five
|
|
176
|
+
* gates nobody asked for.
|
|
177
|
+
*/
|
|
178
|
+
export type TrashAssuranceLevel = "confirmed" | "resolved";
|
|
179
|
+
/**
|
|
180
|
+
* Why a canonical role names no folder an action may act on. `none`: the
|
|
181
|
+
* account has no candidate at all. `stale`: the folder the user appointed is
|
|
182
|
+
* gone from the server. `unconfirmed`: a folder matches by name, but nobody —
|
|
183
|
+
* neither the user nor the server's own flag — ever said it holds the role.
|
|
184
|
+
* These three and no others; a target that is merely unsettled is a different
|
|
185
|
+
* refusal under its own code.
|
|
186
|
+
*/
|
|
187
|
+
export type FolderRoleUnresolvedReason = "none" | "stale" | "unconfirmed";
|
|
188
|
+
export type TrashGateOutcome<T> = {
|
|
189
|
+
allowed: true;
|
|
190
|
+
mailbox: T;
|
|
191
|
+
} | {
|
|
192
|
+
allowed: false;
|
|
193
|
+
reason: FolderRoleUnresolvedReason;
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* The Trash folder a verb at this assurance level may act on, or which of the
|
|
197
|
+
* three reasons stops it. One switch: the delete, the expunge and the count
|
|
198
|
+
* reported before one all decide here, so no two of them can answer a user
|
|
199
|
+
* differently about the same account.
|
|
200
|
+
*/
|
|
201
|
+
export declare const trashMailboxAt: <T>(resolution: RoleResolution<T>, level: TrashAssuranceLevel) => TrashGateOutcome<T>;
|
|
202
|
+
export declare const meetsTrashAssurance: <T>(resolution: RoleResolution<T>, level: TrashAssuranceLevel) => boolean;
|
|
203
|
+
export type FolderAppointmentSourceValue = (typeof FolderAppointmentSource)[keyof typeof FolderAppointmentSource];
|
|
204
|
+
/**
|
|
205
|
+
* The same gate as {@link trashMailboxAt}, asked of the source `/config`
|
|
206
|
+
* reports rather than of the resolution the server computed — so a client can
|
|
207
|
+
* word a delete from the answer the service will give it.
|
|
208
|
+
*
|
|
209
|
+
* It runs that switch rather than restating it. A hand-written mirror gets
|
|
210
|
+
* `Reserved` wrong: it reads as designated, and the gate accepts only the user's
|
|
211
|
+
* appointment and the server's flag.
|
|
212
|
+
*/
|
|
213
|
+
export declare const trashSourceMeetsAssurance: (source: FolderAppointmentSourceValue, level: TrashAssuranceLevel) => boolean;
|
|
214
|
+
/**
|
|
215
|
+
* The mailbox a role is CONFIRMED to hold: the one the user appointed, or the
|
|
216
|
+
* one the server flagged (RFC 6154). No name guessing — `null` here means
|
|
217
|
+
* nobody has said which folder this is, only that a folder happens to be named
|
|
218
|
+
* something plausible. An operation that destroys mail resolves through this
|
|
219
|
+
* and refuses when it comes back empty; `resolveMailboxForRole` adds the guess
|
|
220
|
+
* on top for the operations where being wrong only misfiles a message.
|
|
221
|
+
*
|
|
222
|
+
* An appointment naming a mailbox this account does not hold — deleted,
|
|
223
|
+
* renamed, or carried in from elsewhere — is stale, and answers `null` here
|
|
224
|
+
* too: the fallback is the folder the user did not choose, which is fine to
|
|
225
|
+
* file mail into and no basis at all for destroying it.
|
|
226
|
+
*/
|
|
227
|
+
export declare const resolveConfirmedMailboxForRole: <T extends RoleMailboxCandidate>(role: CanonicalMailboxRoleValue, mailboxes: readonly T[], appointedMailboxId?: string) => T | null;
|
|
228
|
+
/**
|
|
229
|
+
* The role's mailbox including the name guess: `null` only when nothing
|
|
230
|
+
* matches at all, so the caller says the role has no folder rather than picking
|
|
231
|
+
* one. A stale appointment answers with its fallback — every role but Trash
|
|
232
|
+
* files mail on this read, and refusing them all would be a behaviour change
|
|
233
|
+
* with no surface to explain it.
|
|
234
|
+
*
|
|
235
|
+
* Built on `resolveRoleForAccount` directly, never on the confirmed adapter: a
|
|
236
|
+
* stale appointment must still reach the `\Sent` flag here, not fall past it
|
|
237
|
+
* into a folder that merely reads like one.
|
|
238
|
+
*/
|
|
239
|
+
export declare const resolveMailboxForRole: <T extends RoleMailboxCandidate>(role: CanonicalMailboxRoleValue, mailboxes: readonly T[], appointedMailboxId?: string) => T | null;
|