@remit/mailbox-service 0.0.111 → 0.0.113
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-credentials.d.ts +83 -0
- package/dist/account-credentials.js +104 -0
- package/dist/attribute-mapper.d.ts +36 -0
- package/dist/attribute-mapper.js +119 -0
- package/dist/body-parse.d.ts +28 -0
- package/{src/body-parse.ts → dist/body-parse.js} +18 -26
- package/dist/body-part-mapper.d.ts +92 -0
- package/dist/body-part-mapper.js +301 -0
- package/dist/body-sync-queue.d.ts +59 -0
- package/dist/body-sync-queue.js +56 -0
- package/dist/body-sync-terminal.d.ts +58 -0
- package/dist/body-sync-terminal.js +95 -0
- package/dist/body-sync.d.ts +351 -0
- package/dist/body-sync.js +1204 -0
- package/dist/calendar-parts.d.ts +23 -0
- package/dist/calendar-parts.js +18 -0
- package/dist/classification-backfill.d.ts +91 -0
- package/dist/classification-backfill.js +180 -0
- package/dist/classify-message.d.ts +31 -0
- package/dist/classify-message.js +89 -0
- package/dist/connection-factory.d.ts +54 -0
- package/dist/connection-factory.js +54 -0
- package/dist/connection-test.d.ts +41 -0
- package/dist/connection-test.js +152 -0
- package/dist/external-move.d.ts +94 -0
- package/dist/external-move.js +173 -0
- package/dist/filter-operations.d.ts +42 -0
- package/dist/filter-operations.js +211 -0
- package/dist/filters/filter-config.d.ts +14 -0
- package/dist/filters/filter-config.js +10 -0
- package/{src/filters/list-id.ts → dist/filters/list-id.d.ts} +2 -16
- package/dist/filters/list-id.js +26 -0
- package/dist/filters/match.d.ts +69 -0
- package/dist/filters/match.js +165 -0
- package/dist/filters/pipeline.d.ts +64 -0
- package/dist/filters/pipeline.js +100 -0
- package/{src/flag-push-terminal.ts → dist/flag-push-terminal.d.ts} +19 -77
- package/dist/flag-push-terminal.js +63 -0
- package/dist/flag-push.d.ts +69 -0
- package/dist/flag-push.js +111 -0
- package/dist/flag-queue.d.ts +175 -0
- package/dist/flag-queue.js +291 -0
- package/dist/folder-operations.d.ts +39 -0
- package/dist/folder-operations.js +112 -0
- package/dist/folder-placement.d.ts +13 -0
- package/dist/folder-placement.js +23 -0
- package/dist/heuristics/authenticationResults.d.ts +10 -0
- package/dist/heuristics/authenticationResults.js +187 -0
- package/dist/heuristics/classifyByHeaders.d.ts +82 -0
- package/dist/heuristics/classifyByHeaders.js +365 -0
- package/dist/heuristics/classifyPlacement.d.ts +67 -0
- package/dist/heuristics/classifyPlacement.js +153 -0
- package/dist/heuristics/machineSenders.d.ts +21 -0
- package/{src/heuristics/machineSenders.ts → dist/heuristics/machineSenders.js} +34 -40
- package/dist/heuristics/senderMismatch.d.ts +81 -0
- package/dist/heuristics/senderMismatch.js +192 -0
- package/dist/heuristics/socialDomains.d.ts +14 -0
- package/{src/heuristics/socialDomains.ts → dist/heuristics/socialDomains.js} +9 -9
- package/dist/heuristics/transactionalDomains.d.ts +18 -0
- package/{src/heuristics/transactionalDomains.ts → dist/heuristics/transactionalDomains.js} +8 -8
- package/dist/imapflow-connection.d.ts +377 -0
- package/dist/imapflow-connection.js +1147 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +65 -0
- package/dist/list-id-backfill.d.ts +83 -0
- package/dist/list-id-backfill.js +132 -0
- package/dist/mailbox-cursor-rebuild.d.ts +74 -0
- package/dist/mailbox-cursor-rebuild.js +71 -0
- package/{src/mailbox-cursor.ts → dist/mailbox-cursor.d.ts} +20 -98
- package/dist/mailbox-cursor.js +102 -0
- package/dist/mailbox-intent.d.ts +41 -0
- package/{src/mailbox-intent.ts → dist/mailbox-intent.js} +39 -63
- package/dist/mailbox-management.d.ts +240 -0
- package/dist/mailbox-management.js +576 -0
- package/dist/mailbox-presence.d.ts +52 -0
- package/{src/mailbox-presence.ts → dist/mailbox-presence.js} +19 -30
- package/dist/mailbox-queue.d.ts +126 -0
- package/dist/mailbox-queue.js +214 -0
- package/dist/mailbox-sync.d.ts +93 -0
- package/dist/mailbox-sync.js +490 -0
- package/dist/mailbox-upstream.d.ts +38 -0
- package/{src/mailbox-upstream.ts → dist/mailbox-upstream.js} +37 -47
- package/dist/message-category.d.ts +51 -0
- package/{src/message-category.ts → dist/message-category.js} +28 -71
- package/dist/message-move.d.ts +285 -0
- package/dist/message-move.js +858 -0
- package/dist/message-parser.d.ts +14 -0
- package/dist/message-parser.js +17 -0
- package/{src/message-presence.ts → dist/message-presence.d.ts} +1 -13
- package/dist/message-presence.js +29 -0
- package/dist/message-read.d.ts +51 -0
- package/dist/message-read.js +31 -0
- package/dist/message-sync.d.ts +345 -0
- package/dist/message-sync.js +1358 -0
- package/dist/mime-walker.d.ts +102 -0
- package/dist/mime-walker.js +198 -0
- package/dist/organize-job.d.ts +20 -0
- package/dist/organize-job.js +37 -0
- package/dist/outbox-attachment-content.d.ts +37 -0
- package/dist/outbox-attachment-content.js +50 -0
- package/dist/outbox-attachment-filename.d.ts +19 -0
- package/dist/outbox-attachment-filename.js +64 -0
- package/dist/outbox-attachment.d.ts +148 -0
- package/dist/outbox-attachment.js +273 -0
- package/dist/outbox-queue.d.ts +73 -0
- package/dist/outbox-queue.js +284 -0
- package/{src/outbox-status.ts → dist/outbox-status.d.ts} +1 -6
- package/dist/outbox-status.js +18 -0
- package/dist/ownership.d.ts +24 -0
- package/dist/ownership.js +83 -0
- package/dist/placement-move-terminal.d.ts +67 -0
- package/dist/placement-move-terminal.js +88 -0
- package/dist/placement-move.d.ts +83 -0
- package/dist/placement-move.js +216 -0
- package/{src/placement-settled.ts → dist/placement-settled.d.ts} +19 -50
- package/dist/placement-settled.js +75 -0
- package/dist/quarantine.d.ts +95 -0
- package/dist/quarantine.js +131 -0
- package/dist/refusal.d.ts +31 -0
- package/dist/refusal.js +3 -0
- package/{src/restore-source-placement.ts → dist/restore-source-placement.d.ts} +29 -101
- package/dist/restore-source-placement.js +79 -0
- package/dist/search-text-backfill.d.ts +15 -0
- package/dist/search-text-backfill.js +33 -0
- package/dist/snippet.d.ts +61 -0
- package/dist/snippet.js +129 -0
- package/dist/spam-report.d.ts +86 -0
- package/dist/spam-report.js +241 -0
- package/dist/sync-warning.d.ts +21 -0
- package/dist/sync-warning.js +26 -0
- package/dist/sync-watermarks.d.ts +138 -0
- package/dist/sync-watermarks.js +205 -0
- package/dist/test-helpers/fake-imap.d.ts +151 -0
- package/dist/test-helpers/fake-imap.js +508 -0
- package/dist/test-helpers/folder-roles.d.ts +34 -0
- package/dist/test-helpers/folder-roles.js +47 -0
- package/dist/test-helpers/mime-tree-generator.d.ts +82 -0
- package/dist/test-helpers/mime-tree-generator.js +329 -0
- package/dist/test-helpers/test-store.d.ts +69 -0
- package/dist/test-helpers/test-store.js +193 -0
- package/dist/text/normalizer.d.ts +20 -0
- package/dist/text/normalizer.js +102 -0
- package/dist/thread-message-rows.d.ts +69 -0
- package/dist/thread-message-rows.js +40 -0
- package/dist/thread-root.d.ts +7 -0
- package/dist/thread-root.js +25 -0
- package/dist/types.d.ts +343 -0
- package/dist/types.js +19 -0
- package/package.json +86 -73
- package/src/account-credentials.ts +0 -210
- package/src/adversarial-mime.e2e.test.ts +0 -307
- package/src/attribute-mapper.test.ts +0 -104
- package/src/attribute-mapper.ts +0 -148
- package/src/body-parse.test.ts +0 -55
- package/src/body-part-mapper.property.test.ts +0 -354
- package/src/body-part-mapper.ts +0 -360
- package/src/body-sync-auto-read-writeonce.test.ts +0 -219
- package/src/body-sync-calendar-suggestion.test.ts +0 -433
- package/src/body-sync-category-denormalize.test.ts +0 -352
- package/src/body-sync-category-override.test.ts +0 -501
- package/src/body-sync-category-writeonce.test.ts +0 -234
- package/src/body-sync-filter.test.ts +0 -237
- package/src/body-sync-message-gone.test.ts +0 -139
- package/src/body-sync-placement-flags.test.ts +0 -347
- package/src/body-sync-placement-writeonce.test.ts +0 -337
- package/src/body-sync-quarantine.test.ts +0 -276
- package/src/body-sync-queue.test.ts +0 -124
- package/src/body-sync-queue.ts +0 -131
- package/src/body-sync-resync.test.ts +0 -605
- package/src/body-sync-terminal.test.ts +0 -310
- package/src/body-sync-terminal.ts +0 -176
- package/src/body-sync-unclassifiable-once.test.ts +0 -260
- package/src/body-sync-unsubscribed-auto-read.test.ts +0 -176
- package/src/body-sync.ts +0 -1970
- package/src/calendar-parts.ts +0 -41
- package/src/classification-backfill.test.ts +0 -615
- package/src/classification-backfill.ts +0 -363
- package/src/classify-message.test.ts +0 -205
- package/src/classify-message.ts +0 -116
- package/src/connection-factory.ts +0 -102
- package/src/connection-test.ts +0 -208
- package/src/credentials.test.ts +0 -181
- package/src/external-move.test.ts +0 -369
- package/src/external-move.ts +0 -272
- package/src/filters/anchor-drift.ts +0 -55
- package/src/filters/filter-config.test.ts +0 -229
- package/src/filters/filter-config.ts +0 -69
- package/src/filters/list-id.test.ts +0 -50
- package/src/filters/match.test.ts +0 -429
- package/src/filters/match.ts +0 -274
- package/src/filters/message-embedder.test.ts +0 -39
- package/src/filters/message-embedder.ts +0 -33
- package/src/filters/pipeline.test.ts +0 -330
- package/src/filters/pipeline.ts +0 -284
- package/src/flag-push-terminal.test.ts +0 -209
- package/src/flag-push.test.ts +0 -408
- package/src/flag-push.ts +0 -198
- package/src/flag-queue-stars.test.ts +0 -171
- package/src/flag-queue.ts +0 -509
- package/src/folder-placement.ts +0 -56
- package/src/heuristics/authenticationResults.test.ts +0 -189
- package/src/heuristics/authenticationResults.ts +0 -212
- package/src/heuristics/classifyByHeaders.test.ts +0 -889
- package/src/heuristics/classifyByHeaders.ts +0 -467
- package/src/heuristics/classifyPlacement.test.ts +0 -366
- package/src/heuristics/classifyPlacement.ts +0 -211
- package/src/heuristics/classifyRealMail.test.ts +0 -242
- package/src/heuristics/machineSenders.test.ts +0 -101
- package/src/heuristics/senderMismatch.test.ts +0 -346
- package/src/heuristics/senderMismatch.ts +0 -259
- package/src/imapflow-connect-retry.test.ts +0 -134
- package/src/imapflow-connection.e2e.test.ts +0 -335
- package/src/imapflow-connection.integ.test.ts +0 -316
- package/src/imapflow-connection.test.ts +0 -469
- package/src/imapflow-connection.ts +0 -1475
- package/src/index.ts +0 -350
- package/src/list-id-backfill.test.ts +0 -573
- package/src/list-id-backfill.ts +0 -274
- package/src/mailbox-cursor-rebuild.test.ts +0 -171
- package/src/mailbox-cursor-rebuild.ts +0 -146
- package/src/mailbox-cursor.test.ts +0 -249
- package/src/mailbox-delete-intent.test.ts +0 -246
- package/src/mailbox-management.test.ts +0 -356
- package/src/mailbox-management.ts +0 -839
- package/src/mailbox-presence.test.ts +0 -72
- package/src/mailbox-queue.ts +0 -415
- package/src/mailbox-rename-intent.test.ts +0 -389
- package/src/mailbox-rename-subtree.test.ts +0 -664
- package/src/mailbox-sync.test.ts +0 -848
- package/src/mailbox-sync.ts +0 -724
- package/src/message-delete-junk-reconcile.test.ts +0 -117
- package/src/message-delete-no-trash.test.ts +0 -245
- package/src/message-delete-unsettled-placement.test.ts +0 -396
- package/src/message-move-copy.test.ts +0 -562
- package/src/message-move-empty-trash.test.ts +0 -326
- package/src/message-move-same-mailbox.test.ts +0 -139
- package/src/message-move-unsettled-placement.test.ts +0 -378
- package/src/message-move.ts +0 -1348
- package/src/message-parser.ts +0 -26
- package/src/message-presence.test.ts +0 -50
- package/src/message-sync-address-spoof.test.ts +0 -293
- package/src/message-sync-category.test.ts +0 -142
- package/src/message-sync-changedsince.test.ts +0 -840
- package/src/message-sync-flags.test.ts +0 -158
- package/src/message-sync-junk-harvest.test.ts +0 -244
- package/src/message-sync-quarantine.test.ts +0 -416
- package/src/message-sync-repoint.test.ts +0 -419
- package/src/message-sync.ts +0 -1963
- package/src/mime-walker.test.ts +0 -311
- package/src/mime-walker.ts +0 -305
- package/src/outbox-attachment-content.test.ts +0 -153
- package/src/outbox-attachment-content.ts +0 -104
- package/src/outbox-attachment-filename.test.ts +0 -132
- package/src/outbox-attachment-filename.ts +0 -76
- package/src/outbox-attachment.test.ts +0 -738
- package/src/outbox-attachment.ts +0 -536
- package/src/outbox-failed-recovery.test.ts +0 -370
- package/src/outbox-queue-settled-row.test.ts +0 -140
- package/src/outbox-queue.test.ts +0 -291
- package/src/outbox-queue.ts +0 -508
- package/src/pass-through-unit-of-work.ts +0 -17
- package/src/placement-move-terminal.test.ts +0 -300
- package/src/placement-move-terminal.ts +0 -148
- package/src/placement-move-unsettled.test.ts +0 -275
- package/src/placement-move.ts +0 -336
- package/src/placement-settled.test.ts +0 -147
- package/src/placement-transition-race.test.ts +0 -482
- package/src/quarantine.test.ts +0 -191
- package/src/quarantine.ts +0 -228
- package/src/snippet.test.ts +0 -213
- package/src/snippet.ts +0 -152
- package/src/spam-report.test.ts +0 -801
- package/src/spam-report.ts +0 -357
- package/src/stale-message-reconcile.test.ts +0 -82
- package/src/stale-message-reconcile.ts +0 -55
- package/src/sync-watermarks.test.ts +0 -409
- package/src/sync-watermarks.ts +0 -319
- package/src/system-flag-wire-format.test.ts +0 -77
- package/src/test-helpers/folder-roles.ts +0 -66
- package/src/test-helpers/isolated-mailbox.ts +0 -44
- package/src/test-helpers/mailfuzz-connection.ts +0 -27
- package/src/test-helpers/mime-tree-generator.ts +0 -461
- package/src/text/normalizer.test.ts +0 -223
- package/src/text/normalizer.ts +0 -140
- package/src/thread-message-rows.ts +0 -77
- package/src/types/mailparser-augment.d.ts +0 -15
- package/src/types/natural-porter-stemmer.d.ts +0 -50
- package/src/types/stopword.d.ts +0 -69
- package/src/types.ts +0 -371
- package/test/fixtures/mime-adversarial-e2e/01-octet-stream-pdf.eml +0 -21
- package/test/fixtures/mime-adversarial-e2e/02-nested-related-cid-image.eml +0 -31
- package/test/fixtures/mime-adversarial-e2e/03-calendar-invite.eml +0 -33
- package/test/fixtures/mime-adversarial-e2e/04-forwarded-rfc822-with-attachments.eml +0 -42
- package/test/fixtures/mime-adversarial-e2e/05-tnef-winmail.eml +0 -25
- package/test/fixtures/mime-adversarial-e2e/06-8bit-non-ascii.eml +0 -15
- package/test/fixtures/mime-adversarial-e2e/07-quoted-printable-soft-breaks.eml +0 -17
- package/test/fixtures/mime-adversarial-e2e/08-alternative-empty-text.eml +0 -18
- package/tsconfig.json +0 -8
|
@@ -1,419 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { describe, it } from "node:test";
|
|
3
|
-
import type {
|
|
4
|
-
IAddressRepository,
|
|
5
|
-
IEnvelopeRepository,
|
|
6
|
-
IMailboxRepository,
|
|
7
|
-
IMessageRepository,
|
|
8
|
-
IThreadMessageRepository,
|
|
9
|
-
MailboxItem,
|
|
10
|
-
MessageItem,
|
|
11
|
-
ThreadMessageItem,
|
|
12
|
-
UpdateThreadMessageInput,
|
|
13
|
-
} from "@remit/data-ports";
|
|
14
|
-
import { deriveMessageId } from "@remit/data-ports/id";
|
|
15
|
-
import { MessageStatus, MessageSyncStatus } from "@remit/domain-enums";
|
|
16
|
-
import type { ManagedConnectionFactory } from "./connection-factory.js";
|
|
17
|
-
import { type DepartureVerdicts, placementKey } from "./external-move.js";
|
|
18
|
-
import { type AccountFolderRoles, MessageSyncService } from "./message-sync.js";
|
|
19
|
-
import { folderRoles, NO_JUNK_ROLES } from "./test-helpers/folder-roles.js";
|
|
20
|
-
import type { ImapEnvelope, ImapMessage } from "./types.js";
|
|
21
|
-
|
|
22
|
-
const stub = <T>(): T => ({}) as T;
|
|
23
|
-
|
|
24
|
-
const envelope: ImapEnvelope = {
|
|
25
|
-
date: new Date(0).toISOString(),
|
|
26
|
-
messageId: "<root@example.com>",
|
|
27
|
-
subject: "Subject",
|
|
28
|
-
from: [{ name: "Pharma Deals", mailbox: "sales", host: "pharma.example" }],
|
|
29
|
-
sender: [],
|
|
30
|
-
replyTo: [],
|
|
31
|
-
to: [{ name: "", mailbox: "victim", host: "ischen.nl" }],
|
|
32
|
-
cc: [],
|
|
33
|
-
bcc: [],
|
|
34
|
-
inReplyTo: "",
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const mailboxAt = (fullPath: string, specialUse?: string[]): MailboxItem =>
|
|
38
|
-
({
|
|
39
|
-
mailboxId: `mbx-${fullPath}`,
|
|
40
|
-
fullPath,
|
|
41
|
-
hierarchyDelimiter: "/",
|
|
42
|
-
...(specialUse ? { specialUse } : {}),
|
|
43
|
-
}) as MailboxItem;
|
|
44
|
-
|
|
45
|
-
const INBOX = mailboxAt("INBOX");
|
|
46
|
-
const JUNK = mailboxAt("INBOX/Rubbish");
|
|
47
|
-
const TRASH = mailboxAt("Trash");
|
|
48
|
-
const ALL_MAIL = mailboxAt("[Gmail]/All Mail", ["All"]);
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* `pending` is what an ordinary inbound row carries: `upsertWithStatus` is
|
|
52
|
-
* called without a `syncStatus` and the repository defaults to it, and nothing
|
|
53
|
-
* on the sync path ever promotes it (#1096). The fixture states the reachable
|
|
54
|
-
* state, not the one the gate used to demand.
|
|
55
|
-
*/
|
|
56
|
-
const storedIn = (
|
|
57
|
-
mailbox: MailboxItem,
|
|
58
|
-
overrides: Partial<MessageItem> = {},
|
|
59
|
-
): MessageItem =>
|
|
60
|
-
({
|
|
61
|
-
mailboxId: mailbox.mailboxId,
|
|
62
|
-
uid: 7,
|
|
63
|
-
status: MessageStatus.active,
|
|
64
|
-
syncStatus: MessageSyncStatus.pending,
|
|
65
|
-
...overrides,
|
|
66
|
-
}) as MessageItem;
|
|
67
|
-
|
|
68
|
-
const threadRowIn = (mailbox: MailboxItem): ThreadMessageItem =>
|
|
69
|
-
({
|
|
70
|
-
threadMessageId: "tm-1",
|
|
71
|
-
accountConfigId: "cfg-1",
|
|
72
|
-
mailboxId: mailbox.mailboxId,
|
|
73
|
-
uid: 7,
|
|
74
|
-
sentDate: 0,
|
|
75
|
-
isRead: false,
|
|
76
|
-
isDeleted: mailbox === TRASH,
|
|
77
|
-
hasStars: false,
|
|
78
|
-
hasAttachment: false,
|
|
79
|
-
}) as ThreadMessageItem;
|
|
80
|
-
|
|
81
|
-
interface Observed {
|
|
82
|
-
repointedTo: Array<{ mailboxId: string; uid: number }>;
|
|
83
|
-
threadUpdates: UpdateThreadMessageInput[];
|
|
84
|
-
reconciled: string[];
|
|
85
|
-
owned: boolean;
|
|
86
|
-
unresolved: boolean;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* What `confirmDepartures` had answered about the stored placement by the time
|
|
91
|
-
* the batch was saved (#1146).
|
|
92
|
-
*
|
|
93
|
-
* - `departed` — the source folder no longer holds it: a move another client made.
|
|
94
|
-
* - `held` — the source folder still holds it: the labelled message, in two
|
|
95
|
-
* folders at once.
|
|
96
|
-
* - `none` — no verdict at all: the source could not be asked this round.
|
|
97
|
-
* - `stale` — a verdict, but for a placement this row no longer has, because it
|
|
98
|
-
* moved between the probe and the save.
|
|
99
|
-
*/
|
|
100
|
-
type Verdict = "departed" | "held" | "none" | "stale";
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Sync one message out of `sighting`, against a database that already holds it
|
|
104
|
-
* under `stored`. The account's Junk folder is `JUNK` and its Trash is `TRASH`,
|
|
105
|
-
* so the sighting's own role follows from which mailbox it is.
|
|
106
|
-
*/
|
|
107
|
-
const sync = async (
|
|
108
|
-
sighting: MailboxItem,
|
|
109
|
-
stored: MessageItem,
|
|
110
|
-
threadRow: ThreadMessageItem | null = threadRowIn(INBOX),
|
|
111
|
-
copiesHere: MessageItem[] = [],
|
|
112
|
-
verdict: Verdict = "departed",
|
|
113
|
-
): Promise<Observed> => {
|
|
114
|
-
const observed: Observed = {
|
|
115
|
-
repointedTo: [],
|
|
116
|
-
threadUpdates: [],
|
|
117
|
-
reconciled: [],
|
|
118
|
-
owned: false,
|
|
119
|
-
unresolved: false,
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const messageService = {
|
|
123
|
-
upsertWithStatus: async () => ({ item: stored, created: false }),
|
|
124
|
-
get: async () => copiesHere,
|
|
125
|
-
updateUid: async (_id: string, uid: number, mailboxId: string) => {
|
|
126
|
-
observed.repointedTo.push({ mailboxId, uid });
|
|
127
|
-
return stored;
|
|
128
|
-
},
|
|
129
|
-
} as unknown as IMessageRepository;
|
|
130
|
-
|
|
131
|
-
const threadMessageService = {
|
|
132
|
-
findByMessageId: async () => threadRow,
|
|
133
|
-
update: async (
|
|
134
|
-
_config: string,
|
|
135
|
-
_id: string,
|
|
136
|
-
input: UpdateThreadMessageInput,
|
|
137
|
-
) => {
|
|
138
|
-
observed.threadUpdates.push(input);
|
|
139
|
-
return threadRow as ThreadMessageItem;
|
|
140
|
-
},
|
|
141
|
-
create: async (input: unknown) => input as ThreadMessageItem,
|
|
142
|
-
} as unknown as IThreadMessageRepository;
|
|
143
|
-
|
|
144
|
-
const envelopeService = {
|
|
145
|
-
upsertEnvelope: async () => {},
|
|
146
|
-
upsertBodyParts: async () => {},
|
|
147
|
-
} as unknown as IEnvelopeRepository;
|
|
148
|
-
|
|
149
|
-
const addressService = {
|
|
150
|
-
upsertCorrespondentAddress: async () => {},
|
|
151
|
-
upsertJunkAddress: async () => {},
|
|
152
|
-
upsertAddress: async () => {},
|
|
153
|
-
upsertEnvelopeAddress: async () => {},
|
|
154
|
-
reconcileJunkOnlyForMessage: async (messageId: string) => {
|
|
155
|
-
observed.reconciled.push(messageId);
|
|
156
|
-
},
|
|
157
|
-
} as unknown as IAddressRepository;
|
|
158
|
-
|
|
159
|
-
const service = new MessageSyncService(
|
|
160
|
-
stub<ManagedConnectionFactory>(),
|
|
161
|
-
stub<IMailboxRepository>(),
|
|
162
|
-
folderRoles({
|
|
163
|
-
junkMailboxId: JUNK.mailboxId,
|
|
164
|
-
trashMailboxId: TRASH.mailboxId,
|
|
165
|
-
}),
|
|
166
|
-
messageService,
|
|
167
|
-
envelopeService,
|
|
168
|
-
addressService,
|
|
169
|
-
threadMessageService,
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
const msg = {
|
|
173
|
-
uid: 42,
|
|
174
|
-
seq: 1,
|
|
175
|
-
size: 100,
|
|
176
|
-
internalDate: new Date(0),
|
|
177
|
-
flags: [],
|
|
178
|
-
envelope,
|
|
179
|
-
} as unknown as ImapMessage;
|
|
180
|
-
|
|
181
|
-
const answeredFor = placementKey(
|
|
182
|
-
deriveMessageId("acct-1", envelope.messageId),
|
|
183
|
-
stored.mailboxId,
|
|
184
|
-
verdict === "stale" ? stored.uid + 1 : stored.uid,
|
|
185
|
-
);
|
|
186
|
-
const departures: DepartureVerdicts = {
|
|
187
|
-
departed:
|
|
188
|
-
verdict === "held" || verdict === "none"
|
|
189
|
-
? new Set()
|
|
190
|
-
: new Set([answeredFor]),
|
|
191
|
-
settled: verdict === "none" ? new Set() : new Set([answeredFor]),
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
const result = (await (
|
|
195
|
-
service as unknown as {
|
|
196
|
-
saveMessage: (
|
|
197
|
-
mailbox: MailboxItem,
|
|
198
|
-
accountId: string,
|
|
199
|
-
accountConfigId: string,
|
|
200
|
-
msg: ImapMessage,
|
|
201
|
-
roles: AccountFolderRoles,
|
|
202
|
-
departures: DepartureVerdicts,
|
|
203
|
-
) => Promise<{ owned: boolean; unresolvedSighting: boolean }>;
|
|
204
|
-
}
|
|
205
|
-
).saveMessage(
|
|
206
|
-
sighting,
|
|
207
|
-
"acct-1",
|
|
208
|
-
"cfg-1",
|
|
209
|
-
msg,
|
|
210
|
-
{
|
|
211
|
-
junkMailboxId: JUNK.mailboxId,
|
|
212
|
-
trashMailboxId: TRASH.mailboxId,
|
|
213
|
-
configJunkRoles: NO_JUNK_ROLES,
|
|
214
|
-
},
|
|
215
|
-
departures,
|
|
216
|
-
)) as { owned: boolean; unresolvedSighting: boolean };
|
|
217
|
-
|
|
218
|
-
observed.owned = result.owned;
|
|
219
|
-
observed.unresolved = result.unresolvedSighting;
|
|
220
|
-
return observed;
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
describe("which folder a message the database already holds lives in", () => {
|
|
224
|
-
it("follows the last move when another client files it into Junk", async () => {
|
|
225
|
-
const observed = await sync(JUNK, storedIn(INBOX));
|
|
226
|
-
|
|
227
|
-
assert.deepEqual(observed.repointedTo, [
|
|
228
|
-
{ mailboxId: JUNK.mailboxId, uid: 42 },
|
|
229
|
-
]);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it("follows the last move back when another client rescues it", async () => {
|
|
233
|
-
const observed = await sync(INBOX, storedIn(JUNK), threadRowIn(JUNK));
|
|
234
|
-
|
|
235
|
-
assert.deepEqual(observed.repointedTo, [
|
|
236
|
-
{ mailboxId: INBOX.mailboxId, uid: 42 },
|
|
237
|
-
]);
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it("takes the UID of the folder that issued it, never the old one", async () => {
|
|
241
|
-
const observed = await sync(JUNK, storedIn(INBOX, { uid: 7 }));
|
|
242
|
-
|
|
243
|
-
assert.equal(observed.repointedTo[0].uid, 42);
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it("leaves a row this folder already owns alone", async () => {
|
|
247
|
-
const observed = await sync(INBOX, storedIn(INBOX));
|
|
248
|
-
|
|
249
|
-
assert.deepEqual(observed.repointedTo, []);
|
|
250
|
-
assert.equal(observed.owned, true);
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
it("holds a row whose own move has not settled", async () => {
|
|
254
|
-
const observed = await sync(
|
|
255
|
-
INBOX,
|
|
256
|
-
storedIn(JUNK, {
|
|
257
|
-
status: MessageStatus.moving,
|
|
258
|
-
syncStatus: MessageSyncStatus.pending,
|
|
259
|
-
}),
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
assert.deepEqual(observed.repointedTo, []);
|
|
263
|
-
assert.equal(observed.owned, false);
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* The reported bug (#1146): a message that legitimately sits in two real
|
|
268
|
-
* folders — a Gmail user label, a Sieve `fileinto` beside a `keep`, a Sent
|
|
269
|
-
* copy a list echoes back — was re-pointed out of the Inbox by whichever
|
|
270
|
-
* folder the round enumerated last.
|
|
271
|
-
*/
|
|
272
|
-
it("leaves a labelled message in the folder that still holds it", async () => {
|
|
273
|
-
const observed = await sync(
|
|
274
|
-
mailboxAt("Receipts"),
|
|
275
|
-
storedIn(INBOX),
|
|
276
|
-
threadRowIn(INBOX),
|
|
277
|
-
[],
|
|
278
|
-
"held",
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
assert.deepEqual(observed.repointedTo, []);
|
|
282
|
-
assert.deepEqual(observed.threadUpdates, []);
|
|
283
|
-
assert.equal(observed.owned, false);
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* A decided sighting is decided for good: the message is in two folders and
|
|
288
|
-
* asking again next round would get the same answer. Only an UNDECIDED one
|
|
289
|
-
* comes back, so a settled decline must not hold the watermark.
|
|
290
|
-
*/
|
|
291
|
-
it("finishes with a labelled message rather than holding the watermark for it", async () => {
|
|
292
|
-
const observed = await sync(
|
|
293
|
-
mailboxAt("Receipts"),
|
|
294
|
-
storedIn(INBOX),
|
|
295
|
-
threadRowIn(INBOX),
|
|
296
|
-
[],
|
|
297
|
-
"held",
|
|
298
|
-
);
|
|
299
|
-
|
|
300
|
-
assert.equal(observed.unresolved, false);
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
it("holds the watermark when the source folder could not be asked", async () => {
|
|
304
|
-
const observed = await sync(
|
|
305
|
-
JUNK,
|
|
306
|
-
storedIn(INBOX),
|
|
307
|
-
threadRowIn(INBOX),
|
|
308
|
-
[],
|
|
309
|
-
"none",
|
|
310
|
-
);
|
|
311
|
-
|
|
312
|
-
assert.deepEqual(observed.repointedTo, []);
|
|
313
|
-
assert.equal(observed.unresolved, true);
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* The probe reads the row outside the transaction that acts on it, so a user
|
|
318
|
-
* move landing in between leaves a verdict about a placement this row no
|
|
319
|
-
* longer has. Spending it would re-point the row against an answer reached
|
|
320
|
-
* for somewhere else.
|
|
321
|
-
*/
|
|
322
|
-
it("spends no verdict reached for a placement the row has since left", async () => {
|
|
323
|
-
const observed = await sync(
|
|
324
|
-
JUNK,
|
|
325
|
-
storedIn(INBOX),
|
|
326
|
-
threadRowIn(INBOX),
|
|
327
|
-
[],
|
|
328
|
-
"stale",
|
|
329
|
-
);
|
|
330
|
-
|
|
331
|
-
assert.deepEqual(observed.repointedTo, []);
|
|
332
|
-
assert.equal(observed.unresolved, true);
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
it("holds the watermark for nothing when the row is already ours", async () => {
|
|
336
|
-
const observed = await sync(
|
|
337
|
-
INBOX,
|
|
338
|
-
storedIn(INBOX),
|
|
339
|
-
threadRowIn(INBOX),
|
|
340
|
-
[],
|
|
341
|
-
"none",
|
|
342
|
-
);
|
|
343
|
-
|
|
344
|
-
assert.equal(observed.unresolved, false);
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
it("declines a sighting in a folder that copies every message", async () => {
|
|
348
|
-
const observed = await sync(ALL_MAIL, storedIn(INBOX));
|
|
349
|
-
|
|
350
|
-
assert.deepEqual(observed.repointedTo, []);
|
|
351
|
-
assert.equal(observed.owned, false);
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
it("declines a sighting of a copy the user put in this folder", async () => {
|
|
355
|
-
const observed = await sync(JUNK, storedIn(INBOX), threadRowIn(INBOX), [
|
|
356
|
-
storedIn(JUNK),
|
|
357
|
-
]);
|
|
358
|
-
|
|
359
|
-
assert.deepEqual(observed.repointedTo, []);
|
|
360
|
-
assert.deepEqual(observed.threadUpdates, []);
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
it("hands the re-pointed message to this folder's body sync", async () => {
|
|
364
|
-
const observed = await sync(JUNK, storedIn(INBOX));
|
|
365
|
-
|
|
366
|
-
assert.equal(observed.owned, true);
|
|
367
|
-
});
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
describe("what a re-pointed message does to the listing that renders it", () => {
|
|
371
|
-
it("moves the row the listing and the folder counts read", async () => {
|
|
372
|
-
const observed = await sync(JUNK, storedIn(INBOX));
|
|
373
|
-
|
|
374
|
-
assert.deepEqual(observed.threadUpdates, [
|
|
375
|
-
{ mailboxId: JUNK.mailboxId, uid: 42, isDeleted: false },
|
|
376
|
-
]);
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
it("marks the row deleted when the move was into Trash", async () => {
|
|
380
|
-
const observed = await sync(TRASH, storedIn(INBOX));
|
|
381
|
-
|
|
382
|
-
assert.equal(observed.threadUpdates[0].isDeleted, true);
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
it("clears the deleted mark when the move was out of Trash", async () => {
|
|
386
|
-
const observed = await sync(INBOX, storedIn(TRASH), threadRowIn(TRASH));
|
|
387
|
-
|
|
388
|
-
assert.equal(observed.threadUpdates[0].isDeleted, false);
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
it("writes nothing when no row renders the message yet", async () => {
|
|
392
|
-
const observed = await sync(JUNK, storedIn(INBOX), null);
|
|
393
|
-
|
|
394
|
-
assert.deepEqual(observed.threadUpdates, []);
|
|
395
|
-
assert.deepEqual(observed.repointedTo, [
|
|
396
|
-
{ mailboxId: JUNK.mailboxId, uid: 42 },
|
|
397
|
-
]);
|
|
398
|
-
});
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
describe("what a re-pointed message does to its sender's standing", () => {
|
|
402
|
-
it("re-asks the question when the move was into Junk", async () => {
|
|
403
|
-
const observed = await sync(JUNK, storedIn(INBOX));
|
|
404
|
-
|
|
405
|
-
assert.equal(observed.reconciled.length, 1);
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
it("re-asks it again when the move was back out of Junk", async () => {
|
|
409
|
-
const observed = await sync(INBOX, storedIn(JUNK), threadRowIn(JUNK));
|
|
410
|
-
|
|
411
|
-
assert.equal(observed.reconciled.length, 1);
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
it("asks nothing when the pointer did not move", async () => {
|
|
415
|
-
const observed = await sync(INBOX, storedIn(INBOX));
|
|
416
|
-
|
|
417
|
-
assert.deepEqual(observed.reconciled, []);
|
|
418
|
-
});
|
|
419
|
-
});
|