@remit/mailbox-service 0.0.91 → 0.0.93
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/index.ts +16 -2
- package/src/mailbox-management.test.ts +38 -20
- package/src/mailbox-management.ts +47 -22
- package/src/mailbox-presence.test.ts +72 -0
- package/src/mailbox-presence.ts +49 -27
- package/src/mailbox-queue.ts +34 -15
- package/src/mailbox-rename-intent.test.ts +143 -0
- package/src/mailbox-rename-subtree.test.ts +22 -1
- package/src/mailbox-sync.test.ts +170 -28
- package/src/mailbox-sync.ts +57 -18
- package/src/message-delete-junk-reconcile.test.ts +5 -2
- package/src/message-delete-no-trash.test.ts +5 -1
- package/src/message-delete-unsettled-placement.test.ts +25 -17
- package/src/message-move-copy.test.ts +28 -1
- package/src/message-move-empty-trash.test.ts +48 -1
- package/src/message-move-same-mailbox.test.ts +5 -2
- package/src/message-move-unsettled-placement.test.ts +56 -17
- package/src/message-move.ts +189 -62
- package/src/message-sync-repoint.test.ts +22 -2
- package/src/message-sync.ts +9 -10
- package/src/placement-move-terminal.test.ts +198 -133
- package/src/placement-move-terminal.ts +47 -14
- package/src/placement-move-unsettled.test.ts +18 -4
- package/src/placement-move.ts +65 -9
- package/src/placement-settled.test.ts +36 -6
- package/src/placement-settled.ts +28 -25
- package/src/placement-transition-race.test.ts +482 -0
- package/src/restore-source-placement.ts +157 -0
- package/src/spam-report.test.ts +13 -10
- package/src/spam-report.ts +28 -2
- package/src/thread-message-rows.ts +77 -0
package/src/spam-report.ts
CHANGED
|
@@ -239,7 +239,19 @@ export class SpamReportService {
|
|
|
239
239
|
// originalMailboxId, so a stale value left by some earlier, unrelated
|
|
240
240
|
// move would otherwise survive and send a later notSpam to the wrong
|
|
241
241
|
// folder. This report-spam action established no move of its own.
|
|
242
|
-
|
|
242
|
+
//
|
|
243
|
+
// A transition off the row this call read (imap-mutations R3): the pair
|
|
244
|
+
// being cleared belongs to a placement, and a lane that has claimed the
|
|
245
|
+
// row since keeps its own — its settle manages the pair.
|
|
246
|
+
await this.messageService.transitionPlacement(
|
|
247
|
+
messageId,
|
|
248
|
+
{
|
|
249
|
+
status: before.status,
|
|
250
|
+
mailboxId: before.mailboxId,
|
|
251
|
+
uid: before.uid,
|
|
252
|
+
},
|
|
253
|
+
{ originalMailboxId: null, originalUid: null },
|
|
254
|
+
);
|
|
243
255
|
}
|
|
244
256
|
|
|
245
257
|
await this.flagPushService.flip({
|
|
@@ -299,7 +311,21 @@ export class SpamReportService {
|
|
|
299
311
|
messageId,
|
|
300
312
|
accountId,
|
|
301
313
|
);
|
|
302
|
-
|
|
314
|
+
|
|
315
|
+
// Re-read, then clear against what was read. The restore just wrote a
|
|
316
|
+
// placement, so the row here is not the one above; predicating on it is
|
|
317
|
+
// what keeps this from erasing a pair some other lane is relying on
|
|
318
|
+
// (imap-mutations R3).
|
|
319
|
+
const restored = await this.messageService.get(messageId);
|
|
320
|
+
await this.messageService.transitionPlacement(
|
|
321
|
+
messageId,
|
|
322
|
+
{
|
|
323
|
+
status: restored.status,
|
|
324
|
+
mailboxId: restored.mailboxId,
|
|
325
|
+
uid: restored.uid,
|
|
326
|
+
},
|
|
327
|
+
{ originalMailboxId: null, originalUid: null },
|
|
328
|
+
);
|
|
303
329
|
}
|
|
304
330
|
|
|
305
331
|
const current = await this.messageService.get(messageId);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { ThreadMessageItem } from "@remit/data-ports";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Build the `set` and `composites` payload for the ThreadMessage update on a
|
|
5
|
+
* MESSAGE_DELETE move-to-trash.
|
|
6
|
+
*
|
|
7
|
+
* The CURRENT row state goes in `composites`; the NEW values go in `set`.
|
|
8
|
+
* ElectroDB uses `composites` to run the conditional check on the existing row
|
|
9
|
+
* AND to compute the previous sort-key values needed to recompute the new ones.
|
|
10
|
+
* Passing the NEW values in `composites` makes the conditional check fail with
|
|
11
|
+
* ConditionalCheckFailedException, which ElectroDB wraps as NotFoundError, and
|
|
12
|
+
* the caller silently drops the update. Same root cause as PR #186 fixed for
|
|
13
|
+
* `flag-queue.ts`.
|
|
14
|
+
*/
|
|
15
|
+
export type ThreadMessageRowState = Pick<
|
|
16
|
+
ThreadMessageItem,
|
|
17
|
+
| "sentDate"
|
|
18
|
+
| "mailboxId"
|
|
19
|
+
| "isRead"
|
|
20
|
+
| "isDeleted"
|
|
21
|
+
| "hasStars"
|
|
22
|
+
| "hasAttachment"
|
|
23
|
+
>;
|
|
24
|
+
|
|
25
|
+
const currentComposites = (threadMessage: ThreadMessageRowState) => ({
|
|
26
|
+
sentDate: threadMessage.sentDate,
|
|
27
|
+
mailboxId: threadMessage.mailboxId,
|
|
28
|
+
isRead: threadMessage.isRead,
|
|
29
|
+
isDeleted: threadMessage.isDeleted,
|
|
30
|
+
hasStars: threadMessage.hasStars,
|
|
31
|
+
hasAttachment: threadMessage.hasAttachment,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export const buildThreadMessageTrashUpdate = (
|
|
35
|
+
threadMessage: ThreadMessageRowState,
|
|
36
|
+
newUid: number,
|
|
37
|
+
destinationMailboxId: string,
|
|
38
|
+
) => ({
|
|
39
|
+
set: {
|
|
40
|
+
uid: newUid,
|
|
41
|
+
mailboxId: destinationMailboxId,
|
|
42
|
+
isDeleted: true,
|
|
43
|
+
},
|
|
44
|
+
composites: currentComposites(threadMessage),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The inverse payload: put the thread row back where the message still is on
|
|
49
|
+
* the server. The delete was recorded optimistically, so a delete that gives up
|
|
50
|
+
* with the message still at its source has to hand the row back rather than
|
|
51
|
+
* leave it claiming Trash — an invisible `failed` on a row the user cannot see
|
|
52
|
+
* is the shape of the incident this whole change is about.
|
|
53
|
+
*/
|
|
54
|
+
export const buildThreadMessageMoveRevert = (
|
|
55
|
+
threadMessage: ThreadMessageRowState,
|
|
56
|
+
sourceUid: number,
|
|
57
|
+
sourceMailboxId: string,
|
|
58
|
+
) => ({
|
|
59
|
+
set: {
|
|
60
|
+
uid: sourceUid,
|
|
61
|
+
mailboxId: sourceMailboxId,
|
|
62
|
+
isDeleted: false,
|
|
63
|
+
},
|
|
64
|
+
composites: currentComposites(threadMessage),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Hand a row back after an abandoned expunge. The mail never left Trash, so
|
|
69
|
+
* only the deletion mark reverts — the uid and mailbox on the row are still
|
|
70
|
+
* where the server has it.
|
|
71
|
+
*/
|
|
72
|
+
export const buildThreadMessageUndelete = (
|
|
73
|
+
threadMessage: ThreadMessageRowState,
|
|
74
|
+
) => ({
|
|
75
|
+
set: { isDeleted: false },
|
|
76
|
+
composites: currentComposites(threadMessage),
|
|
77
|
+
});
|