@remit/mailbox-service 0.0.74 → 0.0.76
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/filters/pipeline.test.ts +38 -2
- package/src/message-delete-junk-reconcile.test.ts +2 -1
- package/src/message-delete-no-trash.test.ts +2 -0
- package/src/message-move-copy.test.ts +6 -1
- package/src/message-move-empty-trash.test.ts +2 -0
- package/src/message-move-same-mailbox.test.ts +2 -1
- package/src/message-move.ts +16 -2
- package/src/message-sync-junk-harvest.test.ts +3 -1
- package/src/message-sync.ts +23 -6
- package/src/placement-move-unsettled.test.ts +2 -0
- package/src/placement-move.ts +15 -1
- package/src/spam-report.test.ts +2 -1
- package/src/test-helpers/folder-roles.ts +16 -1
package/package.json
CHANGED
|
@@ -44,11 +44,11 @@ const senderFilter = (destinationMailboxId: string): FilterItem =>
|
|
|
44
44
|
updatedAt: 1,
|
|
45
45
|
}) as unknown as FilterItem;
|
|
46
46
|
|
|
47
|
-
const buildPipeline = (
|
|
47
|
+
const buildPipeline = (...filters: FilterItem[]) => {
|
|
48
48
|
const state = { embedCalls: 0 };
|
|
49
49
|
const config: FilterConfig = {
|
|
50
50
|
filterService: {
|
|
51
|
-
listByAccountAndState: async () =>
|
|
51
|
+
listByAccountAndState: async () => filters,
|
|
52
52
|
refreshExpiry: async (f: FilterItem) => f,
|
|
53
53
|
} as unknown as IFilterRepository,
|
|
54
54
|
filterAnchorService: {
|
|
@@ -103,6 +103,42 @@ describe("FilterPipeline — anchorless From/Or filter at index time", () => {
|
|
|
103
103
|
});
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Arbitration survives exactly here: two standing filters colliding over the
|
|
108
|
+
* same message at the same trigger. A move is exclusive, so the pass resolves
|
|
109
|
+
* one destination — the more-recently-changed action wins (reader #497 leaves
|
|
110
|
+
* this untouched while removing arbitration from the user-initiated apply).
|
|
111
|
+
*/
|
|
112
|
+
describe("FilterPipeline — two standing filters colliding on one message", () => {
|
|
113
|
+
const rivalFilter = (): FilterItem =>
|
|
114
|
+
({
|
|
115
|
+
...senderFilter("mbx-projects"),
|
|
116
|
+
filterId: "flt-rival",
|
|
117
|
+
name: "github to projects",
|
|
118
|
+
actionChangedAt: 2,
|
|
119
|
+
}) as unknown as FilterItem;
|
|
120
|
+
|
|
121
|
+
it("resolves a single destination from the more-recently-changed action", async () => {
|
|
122
|
+
const older = {
|
|
123
|
+
...senderFilter("mbx-archive"),
|
|
124
|
+
actionChangedAt: 1,
|
|
125
|
+
} as unknown as FilterItem;
|
|
126
|
+
const { pipeline } = buildPipeline(older, rivalFilter());
|
|
127
|
+
const decision = await pipeline.evaluate("cfg-1", "m-3", {
|
|
128
|
+
from: "npm@github.com",
|
|
129
|
+
fromName: "npm",
|
|
130
|
+
subject: "A new version of left-pad is available",
|
|
131
|
+
text: "body",
|
|
132
|
+
listId: "",
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
assert.deepEqual(decision.move, {
|
|
136
|
+
destinationMailboxId: "mbx-projects",
|
|
137
|
+
filterId: "flt-rival",
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
106
142
|
describe("FilterPipeline — anchor lazy re-embed on model drift (reader #295)", () => {
|
|
107
143
|
const anchoredFilter = (): FilterItem =>
|
|
108
144
|
({
|
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
IThreadMessageRepository,
|
|
9
9
|
} from "@remit/data-ports";
|
|
10
10
|
import { type MessageMoveConfig, MessageMoveService } from "./message-move.js";
|
|
11
|
-
import { trashRole } from "./test-helpers/folder-roles.js";
|
|
11
|
+
import { NO_JUNK_ROLES, trashRole } from "./test-helpers/folder-roles.js";
|
|
12
12
|
|
|
13
13
|
const ACCOUNT = "acc-1";
|
|
14
14
|
const ACCOUNT_CONFIG = "cfg-1";
|
|
@@ -57,6 +57,7 @@ const buildWorld = (trashExists: boolean) => {
|
|
|
57
57
|
const mailboxSpecialUseService = {
|
|
58
58
|
findTrashMailbox: async () => trash,
|
|
59
59
|
resolveTrashRole: async () => trashRole(trash),
|
|
60
|
+
resolveJunkRolesForConfig: async () => NO_JUNK_ROLES,
|
|
60
61
|
} as unknown as IMailboxSpecialUseRepository;
|
|
61
62
|
|
|
62
63
|
const addressService = {
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
StaleTrashAppointmentError,
|
|
16
16
|
UnconfirmedTrashMailboxError,
|
|
17
17
|
} from "./message-move.js";
|
|
18
|
+
import { NO_JUNK_ROLES } from "./test-helpers/folder-roles.js";
|
|
18
19
|
|
|
19
20
|
const ACCOUNT = "acc-1";
|
|
20
21
|
const ACCOUNT_CONFIG = "cfg-1";
|
|
@@ -80,6 +81,7 @@ const buildWorld = (
|
|
|
80
81
|
|
|
81
82
|
const mailboxSpecialUseService = {
|
|
82
83
|
resolveTrashRole: async () => trashResolution,
|
|
84
|
+
resolveJunkRolesForConfig: async () => NO_JUNK_ROLES,
|
|
83
85
|
} as unknown as IMailboxSpecialUseRepository;
|
|
84
86
|
|
|
85
87
|
const addressService = {
|
|
@@ -15,7 +15,11 @@ import { MailboxCursorState } from "@remit/domain-enums";
|
|
|
15
15
|
import type { ManagedConnectionFactory } from "./connection-factory.js";
|
|
16
16
|
import { type MessageMoveConfig, MessageMoveService } from "./message-move.js";
|
|
17
17
|
import { MessageSyncService } from "./message-sync.js";
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
NO_JUNK_ROLES,
|
|
20
|
+
noFolderRoles,
|
|
21
|
+
trashRole,
|
|
22
|
+
} from "./test-helpers/folder-roles.js";
|
|
19
23
|
import type { IImapConnection, ImapMessage } from "./types.js";
|
|
20
24
|
|
|
21
25
|
const stubAddressService = (): IAddressRepository =>
|
|
@@ -176,6 +180,7 @@ const buildWorld = () => {
|
|
|
176
180
|
const mailboxSpecialUseService = {
|
|
177
181
|
findTrashMailbox: async () => null,
|
|
178
182
|
resolveTrashRole: async () => trashRole(null),
|
|
183
|
+
resolveJunkRolesForConfig: async () => NO_JUNK_ROLES,
|
|
179
184
|
} as unknown as IMailboxSpecialUseRepository;
|
|
180
185
|
|
|
181
186
|
const config: MessageMoveConfig = {
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
StaleTrashAppointmentError,
|
|
23
23
|
UnconfirmedTrashMailboxError,
|
|
24
24
|
} from "./message-move.js";
|
|
25
|
+
import { NO_JUNK_ROLES } from "./test-helpers/folder-roles.js";
|
|
25
26
|
|
|
26
27
|
const ACCOUNT = "acc-1";
|
|
27
28
|
const ACCOUNT_CONFIG = "cfg-1";
|
|
@@ -100,6 +101,7 @@ const buildWorld = (
|
|
|
100
101
|
} as unknown as IAddressRepository,
|
|
101
102
|
mailboxSpecialUseService: {
|
|
102
103
|
resolveTrashRole: async () => trashResolution,
|
|
104
|
+
resolveJunkRolesForConfig: async () => NO_JUNK_ROLES,
|
|
103
105
|
} as unknown as IMailboxSpecialUseRepository,
|
|
104
106
|
threadMessageService,
|
|
105
107
|
sqsQueueUrl: "http://localhost:9324/000000000000/remit-messages.fifo",
|
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
IThreadMessageRepository,
|
|
9
9
|
} from "@remit/data-ports";
|
|
10
10
|
import { type MessageMoveConfig, MessageMoveService } from "./message-move.js";
|
|
11
|
-
import { trashRole } from "./test-helpers/folder-roles.js";
|
|
11
|
+
import { NO_JUNK_ROLES, trashRole } from "./test-helpers/folder-roles.js";
|
|
12
12
|
|
|
13
13
|
const stubAddressService = (): IAddressRepository =>
|
|
14
14
|
({
|
|
@@ -68,6 +68,7 @@ const buildWorld = () => {
|
|
|
68
68
|
const mailboxSpecialUseService = {
|
|
69
69
|
findTrashMailbox: async () => null,
|
|
70
70
|
resolveTrashRole: async () => trashRole(null),
|
|
71
|
+
resolveJunkRolesForConfig: async () => NO_JUNK_ROLES,
|
|
71
72
|
} as unknown as IMailboxSpecialUseRepository;
|
|
72
73
|
|
|
73
74
|
const config: MessageMoveConfig = {
|
package/src/message-move.ts
CHANGED
|
@@ -395,6 +395,12 @@ export class MessageMoveService {
|
|
|
395
395
|
|
|
396
396
|
// Process move to trash
|
|
397
397
|
if (trashMailbox && moveToTrashMessages.length > 0) {
|
|
398
|
+
// Every message in the batch shares the config, so its Junk and Trash
|
|
399
|
+
// folders resolve once rather than once per message.
|
|
400
|
+
const configJunkRoles =
|
|
401
|
+
await this.mailboxSpecialUseService.resolveJunkRolesForConfig(
|
|
402
|
+
accountConfigId,
|
|
403
|
+
);
|
|
398
404
|
for (const { messageId, message, sourceMailbox } of moveToTrashMessages) {
|
|
399
405
|
// Update local state optimistically
|
|
400
406
|
await this.messageService.updateForMove(messageId, {
|
|
@@ -413,7 +419,10 @@ export class MessageMoveService {
|
|
|
413
419
|
true,
|
|
414
420
|
);
|
|
415
421
|
|
|
416
|
-
await this.addressService.reconcileJunkOnlyForMessage(
|
|
422
|
+
await this.addressService.reconcileJunkOnlyForMessage(
|
|
423
|
+
messageId,
|
|
424
|
+
configJunkRoles,
|
|
425
|
+
);
|
|
417
426
|
|
|
418
427
|
events.push({
|
|
419
428
|
type: "MESSAGE_DELETE",
|
|
@@ -548,7 +557,12 @@ export class MessageMoveService {
|
|
|
548
557
|
isMovingToTrash,
|
|
549
558
|
);
|
|
550
559
|
|
|
551
|
-
await this.addressService.reconcileJunkOnlyForMessage(
|
|
560
|
+
await this.addressService.reconcileJunkOnlyForMessage(
|
|
561
|
+
messageId,
|
|
562
|
+
await this.mailboxSpecialUseService.resolveJunkRolesForConfig(
|
|
563
|
+
accountConfigId,
|
|
564
|
+
),
|
|
565
|
+
);
|
|
552
566
|
|
|
553
567
|
// If moving FROM Trash, clear isDeleted
|
|
554
568
|
if (isMovingFromTrash) {
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
addressSightingIn,
|
|
20
20
|
MessageSyncService,
|
|
21
21
|
} from "./message-sync.js";
|
|
22
|
-
import { folderRoles } from "./test-helpers/folder-roles.js";
|
|
22
|
+
import { folderRoles, NO_JUNK_ROLES } from "./test-helpers/folder-roles.js";
|
|
23
23
|
import type { ImapEnvelope, ImapMessage } from "./types.js";
|
|
24
24
|
|
|
25
25
|
const stub = <T>(): T => ({}) as T;
|
|
@@ -141,6 +141,7 @@ const save = async (
|
|
|
141
141
|
).saveMessage(mailbox, "acct-1", "cfg-1", msg, {
|
|
142
142
|
junkMailboxId: role === "Junk" ? mailbox.mailboxId : null,
|
|
143
143
|
trashMailboxId: role === "Trash" ? mailbox.mailboxId : null,
|
|
144
|
+
configJunkRoles: NO_JUNK_ROLES,
|
|
144
145
|
});
|
|
145
146
|
|
|
146
147
|
return saved;
|
|
@@ -155,6 +156,7 @@ describe("what a mailbox says about the addresses on its messages", () => {
|
|
|
155
156
|
const ROLES = (junk: string | null, trash: string | null) => ({
|
|
156
157
|
junkMailboxId: junk,
|
|
157
158
|
trashMailboxId: trash,
|
|
159
|
+
configJunkRoles: NO_JUNK_ROLES,
|
|
158
160
|
});
|
|
159
161
|
|
|
160
162
|
it("reads the folder the account appointed, not the folder’s name", () => {
|
package/src/message-sync.ts
CHANGED
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
ThreadMessageItem,
|
|
14
14
|
} from "@remit/data-ports";
|
|
15
15
|
import { storedDisplayName } from "@remit/data-ports/display-name";
|
|
16
|
+
import type { JunkRoleMailboxes } from "@remit/data-ports/folder-role";
|
|
16
17
|
import {
|
|
17
18
|
deriveAddressId,
|
|
18
19
|
deriveBodyPartId,
|
|
@@ -92,6 +93,14 @@ export type AddressSighting = "junk" | "discarded" | "correspondent";
|
|
|
92
93
|
export interface AccountFolderRoles {
|
|
93
94
|
readonly junkMailboxId: string | null;
|
|
94
95
|
readonly trashMailboxId: string | null;
|
|
96
|
+
/**
|
|
97
|
+
* The same two roles across every account under the config, for the
|
|
98
|
+
* reconcile that follows a save into Junk. The address book is keyed by
|
|
99
|
+
* config, so a predicate over an address weighs sightings from all of them —
|
|
100
|
+
* resolving only this account withholds a sender on one and restores it on
|
|
101
|
+
* the next.
|
|
102
|
+
*/
|
|
103
|
+
readonly configJunkRoles: JunkRoleMailboxes;
|
|
95
104
|
}
|
|
96
105
|
|
|
97
106
|
/**
|
|
@@ -462,7 +471,7 @@ export class MessageSyncService {
|
|
|
462
471
|
// which catches its own error and reports a `failed` outcome instead of
|
|
463
472
|
// rejecting. So one bad message can no longer abort the whole batch (the
|
|
464
473
|
// poison pill that previously froze the mailbox, #817).
|
|
465
|
-
const roles = await this.folderRolesFor(accountId);
|
|
474
|
+
const roles = await this.folderRolesFor(accountId, accountConfigId);
|
|
466
475
|
const outcomes = await pMap(
|
|
467
476
|
applicable,
|
|
468
477
|
(msg) =>
|
|
@@ -697,7 +706,7 @@ export class MessageSyncService {
|
|
|
697
706
|
const newMessages =
|
|
698
707
|
newUids.length > 0 ? await this.fetchMessageBatch(newUids) : [];
|
|
699
708
|
const applicable = newMessages.filter((msg) => msg.envelope !== undefined);
|
|
700
|
-
const roles = await this.folderRolesFor(accountId);
|
|
709
|
+
const roles = await this.folderRolesFor(accountId, accountConfigId);
|
|
701
710
|
const outcomes = await pMap(
|
|
702
711
|
applicable,
|
|
703
712
|
(msg) =>
|
|
@@ -925,7 +934,7 @@ export class MessageSyncService {
|
|
|
925
934
|
);
|
|
926
935
|
}
|
|
927
936
|
|
|
928
|
-
const roles = await this.folderRolesFor(accountId);
|
|
937
|
+
const roles = await this.folderRolesFor(accountId, accountConfigId);
|
|
929
938
|
const outcomes = await pMap(
|
|
930
939
|
applicable,
|
|
931
940
|
(msg) =>
|
|
@@ -1280,14 +1289,19 @@ export class MessageSyncService {
|
|
|
1280
1289
|
* batch rather than per message: the lookup reads an appointment row and
|
|
1281
1290
|
* the account’s mailbox list, and every message in a batch shares both.
|
|
1282
1291
|
*/
|
|
1283
|
-
private async folderRolesFor(
|
|
1284
|
-
|
|
1292
|
+
private async folderRolesFor(
|
|
1293
|
+
accountId: string,
|
|
1294
|
+
accountConfigId: string,
|
|
1295
|
+
): Promise<AccountFolderRoles> {
|
|
1296
|
+
const [junk, trash, configJunkRoles] = await Promise.all([
|
|
1285
1297
|
this.mailboxSpecialUseService.findJunkMailbox(accountId),
|
|
1286
1298
|
this.mailboxSpecialUseService.findTrashMailbox(accountId),
|
|
1299
|
+
this.mailboxSpecialUseService.resolveJunkRolesForConfig(accountConfigId),
|
|
1287
1300
|
]);
|
|
1288
1301
|
return {
|
|
1289
1302
|
junkMailboxId: junk?.mailboxId ?? null,
|
|
1290
1303
|
trashMailboxId: trash?.mailboxId ?? null,
|
|
1304
|
+
configJunkRoles,
|
|
1291
1305
|
};
|
|
1292
1306
|
}
|
|
1293
1307
|
|
|
@@ -1432,7 +1446,10 @@ export class MessageSyncService {
|
|
|
1432
1446
|
owned = created || item.mailboxId === mailboxId;
|
|
1433
1447
|
|
|
1434
1448
|
if (sighting === "junk") {
|
|
1435
|
-
await repos.address.reconcileJunkOnlyForMessage(
|
|
1449
|
+
await repos.address.reconcileJunkOnlyForMessage(
|
|
1450
|
+
messageId,
|
|
1451
|
+
roles.configJunkRoles,
|
|
1452
|
+
);
|
|
1436
1453
|
}
|
|
1437
1454
|
|
|
1438
1455
|
await this.createThreadForMessage(
|
|
@@ -20,6 +20,7 @@ import type {
|
|
|
20
20
|
UpdateMessageMoveInput,
|
|
21
21
|
} from "@remit/data-ports";
|
|
22
22
|
import { PlacementMoveService } from "./placement-move.js";
|
|
23
|
+
import { noFolderRoles } from "./test-helpers/folder-roles.js";
|
|
23
24
|
|
|
24
25
|
const ACCOUNT_ID = "acc-pm";
|
|
25
26
|
const ACCOUNT_CONFIG_ID = "cfg-pm";
|
|
@@ -111,6 +112,7 @@ const buildHarness = (): Harness => {
|
|
|
111
112
|
threadMessageService,
|
|
112
113
|
markerService,
|
|
113
114
|
addressService,
|
|
115
|
+
mailboxSpecialUseService: noFolderRoles,
|
|
114
116
|
sqsQueueUrl: "https://sqs.eu-west-1.amazonaws.com/000/message-mgmt",
|
|
115
117
|
moveSettleTimeoutMs: 200,
|
|
116
118
|
moveSettlePollMs: 10,
|
package/src/placement-move.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { SendMessageCommand, type SQSClient } from "@aws-sdk/client-sqs";
|
|
3
3
|
import type {
|
|
4
4
|
IAddressRepository,
|
|
5
|
+
IMailboxSpecialUseRepository,
|
|
5
6
|
IMessagePlacementMoveRepository,
|
|
6
7
|
IMessageRepository,
|
|
7
8
|
IThreadMessageRepository,
|
|
@@ -44,6 +45,12 @@ export interface PlacementMoveConfig {
|
|
|
44
45
|
threadMessageService: IThreadMessageRepository;
|
|
45
46
|
markerService: IMessagePlacementMoveRepository;
|
|
46
47
|
addressService: IAddressRepository;
|
|
48
|
+
/**
|
|
49
|
+
* Required, not optional: this mover carries automatic spam placement, and
|
|
50
|
+
* one built without the folder-role seam would silently leave the sender of
|
|
51
|
+
* every auto-filed spam suggestible in the compose picker.
|
|
52
|
+
*/
|
|
53
|
+
mailboxSpecialUseService: IMailboxSpecialUseRepository;
|
|
47
54
|
sqsQueueUrl: string;
|
|
48
55
|
sqsEndpoint?: string;
|
|
49
56
|
logger?: PlacementMoveLogger;
|
|
@@ -87,6 +94,7 @@ export class PlacementMoveService {
|
|
|
87
94
|
private threadMessageService: IThreadMessageRepository;
|
|
88
95
|
private markerService: IMessagePlacementMoveRepository;
|
|
89
96
|
private addressService: IAddressRepository;
|
|
97
|
+
private mailboxSpecialUseService: IMailboxSpecialUseRepository;
|
|
90
98
|
private sqs: SQSClient;
|
|
91
99
|
private queueUrl: string;
|
|
92
100
|
private log: PlacementMoveLogger;
|
|
@@ -98,6 +106,7 @@ export class PlacementMoveService {
|
|
|
98
106
|
this.threadMessageService = config.threadMessageService;
|
|
99
107
|
this.markerService = config.markerService;
|
|
100
108
|
this.addressService = config.addressService;
|
|
109
|
+
this.mailboxSpecialUseService = config.mailboxSpecialUseService;
|
|
101
110
|
this.queueUrl = config.sqsQueueUrl;
|
|
102
111
|
this.log = config.logger ?? noopLogger;
|
|
103
112
|
this.moveSettleTimeoutMs =
|
|
@@ -200,7 +209,12 @@ export class PlacementMoveService {
|
|
|
200
209
|
originalUid: message.uid,
|
|
201
210
|
});
|
|
202
211
|
|
|
203
|
-
await this.addressService.reconcileJunkOnlyForMessage(
|
|
212
|
+
await this.addressService.reconcileJunkOnlyForMessage(
|
|
213
|
+
messageId,
|
|
214
|
+
await this.mailboxSpecialUseService.resolveJunkRolesForConfig(
|
|
215
|
+
accountConfigId,
|
|
216
|
+
),
|
|
217
|
+
);
|
|
204
218
|
|
|
205
219
|
// The queue kick is a serious operational step, not a routine one — a
|
|
206
220
|
// failure here MUST propagate (never swallowed): the marker stays
|
package/src/spam-report.test.ts
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
NoJunkMailboxError,
|
|
20
20
|
SpamReportService,
|
|
21
21
|
} from "./spam-report.js";
|
|
22
|
-
import { trashRole } from "./test-helpers/folder-roles.js";
|
|
22
|
+
import { NO_JUNK_ROLES, trashRole } from "./test-helpers/folder-roles.js";
|
|
23
23
|
|
|
24
24
|
const ACCOUNT = "acc-1";
|
|
25
25
|
const ACCOUNT_CONFIG = "cfg-1";
|
|
@@ -235,6 +235,7 @@ const buildWorld = (
|
|
|
235
235
|
findJunkMailbox: async () => junkMailbox,
|
|
236
236
|
findTrashMailbox: async () => null,
|
|
237
237
|
resolveTrashRole: async () => trashRole(null),
|
|
238
|
+
resolveJunkRolesForConfig: async () => NO_JUNK_ROLES,
|
|
238
239
|
} as unknown as IMailboxSpecialUseRepository;
|
|
239
240
|
|
|
240
241
|
const mailboxService = {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { IMailboxSpecialUseRepository } from "@remit/data-ports";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
JunkRoleMailboxes,
|
|
4
|
+
RoleResolution,
|
|
5
|
+
} from "@remit/data-ports/folder-role";
|
|
3
6
|
|
|
4
7
|
type RoleMailbox = { mailboxId: string; fullPath: string };
|
|
5
8
|
|
|
@@ -24,6 +27,7 @@ export const noFolderRoles = {
|
|
|
24
27
|
findJunkMailbox: async () => null,
|
|
25
28
|
findTrashMailbox: async () => null,
|
|
26
29
|
resolveTrashRole: async () => trashRole(null),
|
|
30
|
+
resolveJunkRolesForConfig: async () => NO_JUNK_ROLES,
|
|
27
31
|
} as unknown as IMailboxSpecialUseRepository;
|
|
28
32
|
|
|
29
33
|
/** A folder-role map naming the mailboxes that hold Junk and Trash. */
|
|
@@ -41,11 +45,22 @@ export const folderRoles = (roles: {
|
|
|
41
45
|
: null,
|
|
42
46
|
findTrashMailbox: async () => trash,
|
|
43
47
|
resolveTrashRole: async () => trashRole(trash),
|
|
48
|
+
resolveJunkRolesForConfig: async () => ({
|
|
49
|
+
junkMailboxIds: roles.junkMailboxId ? [roles.junkMailboxId] : [],
|
|
50
|
+
trashMailboxIds: roles.trashMailboxId ? [roles.trashMailboxId] : [],
|
|
51
|
+
}),
|
|
44
52
|
} as unknown as IMailboxSpecialUseRepository;
|
|
45
53
|
};
|
|
46
54
|
|
|
55
|
+
/** An account whose config resolves no Junk and no Trash anywhere. */
|
|
56
|
+
export const NO_JUNK_ROLES: JunkRoleMailboxes = {
|
|
57
|
+
junkMailboxIds: [],
|
|
58
|
+
trashMailboxIds: [],
|
|
59
|
+
};
|
|
60
|
+
|
|
47
61
|
/** The same account, as the resolved map `saveMessage` is handed. */
|
|
48
62
|
export const NO_FOLDER_ROLES = {
|
|
49
63
|
junkMailboxId: null,
|
|
50
64
|
trashMailboxId: null,
|
|
65
|
+
configJunkRoles: NO_JUNK_ROLES,
|
|
51
66
|
};
|