@remit/imap-worker 0.0.12 → 0.0.13
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
CHANGED
|
@@ -12,8 +12,10 @@ const buildConnection = (opts: {
|
|
|
12
12
|
moveError?: Error;
|
|
13
13
|
/** Message-ID search hits in the destination mailbox — the verification probe. */
|
|
14
14
|
destinationSearchUids?: number[];
|
|
15
|
-
/** Whether the
|
|
15
|
+
/** Whether the message is still in the source mailbox. */
|
|
16
16
|
stillAtSource?: boolean;
|
|
17
|
+
/** The source FETCH drops the row for a message that is still there. */
|
|
18
|
+
sourceFetchDrops?: boolean;
|
|
17
19
|
}): IImapConnection =>
|
|
18
20
|
({
|
|
19
21
|
openBox: async () => ({}) as never,
|
|
@@ -25,9 +27,15 @@ const buildConnection = (opts: {
|
|
|
25
27
|
uidMap: opts.uidMap ?? new Map(),
|
|
26
28
|
};
|
|
27
29
|
},
|
|
28
|
-
search: async (
|
|
30
|
+
search: async (criteria: unknown[]) => {
|
|
31
|
+
const [first] = criteria;
|
|
32
|
+
if (Array.isArray(first) && first[0] === "UID") {
|
|
33
|
+
return opts.stillAtSource ? [Number(first[1])] : [];
|
|
34
|
+
}
|
|
35
|
+
return opts.destinationSearchUids ?? [];
|
|
36
|
+
},
|
|
29
37
|
fetchMessages: async (uids: number[]) =>
|
|
30
|
-
opts.stillAtSource
|
|
38
|
+
opts.stillAtSource && !opts.sourceFetchDrops
|
|
31
39
|
? uids.map((uid) => ({ uid }) as unknown as never)
|
|
32
40
|
: [],
|
|
33
41
|
}) as unknown as IImapConnection;
|
|
@@ -151,7 +159,29 @@ describe("attemptMove — the IMAP push (issue #1271)", () => {
|
|
|
151
159
|
);
|
|
152
160
|
});
|
|
153
161
|
|
|
154
|
-
it("
|
|
162
|
+
it("throws (never deletes) when the source FETCH drops the row for a message the server still lists", async () => {
|
|
163
|
+
const connection = buildConnection({
|
|
164
|
+
uidMap: new Map(),
|
|
165
|
+
destinationSearchUids: [],
|
|
166
|
+
stillAtSource: true,
|
|
167
|
+
sourceFetchDrops: true,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
await assert.rejects(
|
|
171
|
+
() =>
|
|
172
|
+
attemptMove(
|
|
173
|
+
connection,
|
|
174
|
+
connection,
|
|
175
|
+
"Junk",
|
|
176
|
+
"INBOX",
|
|
177
|
+
42,
|
|
178
|
+
MESSAGE_ID_HEADER,
|
|
179
|
+
),
|
|
180
|
+
/unresolved/,
|
|
181
|
+
);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("not-found: confirmed absent from BOTH destination (search miss) AND source (search miss)", async () => {
|
|
155
185
|
const connection = buildConnection({
|
|
156
186
|
uidMap: new Map(),
|
|
157
187
|
destinationSearchUids: [],
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
guardConnectionCursor,
|
|
6
6
|
type IImapConnection,
|
|
7
7
|
isCursorRebuildNeeded,
|
|
8
|
+
isMessageGoneFromOpenMailbox,
|
|
8
9
|
MailboxCursorPausedError,
|
|
9
10
|
reconcileStaleMessage,
|
|
10
11
|
resolveExhaustedPlacementMoveFailure,
|
|
@@ -90,7 +91,8 @@ const searchMailboxByMessageId = async (
|
|
|
90
91
|
* gone (PR #1289 review finding 2) — deleting the local row on that
|
|
91
92
|
* ambiguity would be data loss. `not-found` is only returned once BOTH a
|
|
92
93
|
* destination Message-ID search (when the header is known) misses AND
|
|
93
|
-
* the message
|
|
94
|
+
* {@link isMessageGoneFromOpenMailbox} confirms the message absent from the
|
|
95
|
+
* source — a FETCH returning no row does not, on its own, confirm anything.
|
|
94
96
|
*/
|
|
95
97
|
export const attemptMove = async (
|
|
96
98
|
sourceConnection: IImapConnection,
|
|
@@ -135,8 +137,7 @@ export const attemptMove = async (
|
|
|
135
137
|
}
|
|
136
138
|
|
|
137
139
|
await sourceConnection.openBox(sourceMailboxPath, true);
|
|
138
|
-
|
|
139
|
-
if (stillAtSource.length > 0) {
|
|
140
|
+
if (!(await isMessageGoneFromOpenMailbox(sourceConnection, uid))) {
|
|
140
141
|
throw new Error(
|
|
141
142
|
"Placement move unresolved (unconfirmed at destination, still present at source) — retrying",
|
|
142
143
|
);
|