@remit/imap-worker 0.0.89 → 0.0.91
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-check.d.ts +27 -0
- package/dist/account-check.js +70 -0
- package/dist/body-sync-gate.d.ts +12 -0
- package/dist/body-sync-gate.js +53 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +193 -0
- package/dist/config-env.d.ts +5 -0
- package/dist/config-env.js +37 -0
- package/dist/connection-pool.d.ts +22 -0
- package/dist/connection-pool.js +97 -0
- package/dist/connection-scope.d.ts +8 -0
- package/dist/connection-scope.js +10 -0
- package/dist/context.d.ts +46 -0
- package/dist/context.js +15 -0
- package/dist/e2e-processor-shim.d.ts +2 -0
- package/dist/e2e-processor-shim.js +181 -0
- package/dist/emit.d.ts +15 -0
- package/dist/emit.js +76 -0
- package/dist/events.d.ts +203 -0
- package/dist/events.js +1 -0
- package/dist/failure-kind.d.ts +10 -0
- package/{src/failure-kind.ts → dist/failure-kind.js} +6 -6
- package/dist/handlers/append-sent-message.d.ts +3 -0
- package/dist/handlers/append-sent-message.js +152 -0
- package/dist/handlers/delete-account-objects.d.ts +3 -0
- package/dist/handlers/delete-account-objects.js +35 -0
- package/dist/handlers/empty-trash.d.ts +14 -0
- package/dist/handlers/empty-trash.js +217 -0
- package/dist/handlers/flag-push.d.ts +38 -0
- package/dist/handlers/flag-push.js +195 -0
- package/dist/handlers/imported-folder.d.ts +4 -0
- package/dist/handlers/imported-folder.js +23 -0
- package/dist/handlers/mailbox-management.d.ts +6 -0
- package/dist/handlers/mailbox-management.js +294 -0
- package/dist/handlers/mailbox-sync-order.d.ts +21 -0
- package/dist/handlers/mailbox-sync-order.js +50 -0
- package/dist/handlers/message-copy.d.ts +40 -0
- package/dist/handlers/message-copy.js +371 -0
- package/dist/handlers/message-delete-terminal.d.ts +60 -0
- package/dist/handlers/message-delete-terminal.js +77 -0
- package/dist/handlers/message-delete.d.ts +57 -0
- package/dist/handlers/message-delete.js +520 -0
- package/{src/handlers/message-move-terminal.ts → dist/handlers/message-move-terminal.d.ts} +18 -105
- package/dist/handlers/message-move-terminal.js +94 -0
- package/dist/handlers/message-move.d.ts +146 -0
- package/dist/handlers/message-move.js +490 -0
- package/dist/handlers/placement-move-push.d.ts +62 -0
- package/dist/handlers/placement-move-push.js +307 -0
- package/dist/handlers/probe-message-id.d.ts +2 -0
- package/dist/handlers/probe-message-id.js +6 -0
- package/dist/handlers/sync-mailboxes.d.ts +45 -0
- package/dist/handlers/sync-mailboxes.js +233 -0
- package/dist/handlers/sync-message-body.d.ts +23 -0
- package/dist/handlers/sync-message-body.js +182 -0
- package/dist/handlers/sync-messages.d.ts +29 -0
- package/dist/handlers/sync-messages.js +285 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +67 -0
- package/dist/lambda-context.d.ts +8 -0
- package/dist/lambda-context.js +33 -0
- package/dist/mailbox-row.d.ts +9 -0
- package/dist/mailbox-row.js +6 -0
- package/dist/poller.d.ts +1 -0
- package/dist/poller.js +51 -0
- package/dist/process.d.ts +6 -0
- package/dist/process.js +3 -0
- package/dist/processor.d.ts +3 -0
- package/dist/processor.js +44 -0
- package/dist/queues.d.ts +10 -0
- package/dist/queues.js +1 -0
- package/dist/s3-account-objects.d.ts +3 -0
- package/dist/s3-account-objects.js +26 -0
- package/dist/scheduler/attachment-sweep.d.ts +11 -0
- package/dist/scheduler/attachment-sweep.js +23 -0
- package/dist/scheduler/calendar-subscriptions.d.ts +26 -0
- package/dist/scheduler/calendar-subscriptions.js +35 -0
- package/dist/scheduler/config.d.ts +27 -0
- package/{src/scheduler/config.ts → dist/scheduler/config.js} +7 -26
- package/{src/scheduler/decide-due.ts → dist/scheduler/decide-due.d.ts} +3 -12
- package/dist/scheduler/decide-due.js +17 -0
- package/dist/scheduler/handler.d.ts +21 -0
- package/{src/scheduler/handler.ts → dist/scheduler/handler.js} +24 -36
- package/{src/scheduler/loop.ts → dist/scheduler/loop.d.ts} +7 -23
- package/dist/scheduler/loop.js +8 -0
- package/dist/scheduler/run-tick.d.ts +62 -0
- package/dist/scheduler/run-tick.js +164 -0
- package/dist/scheduler/runner.d.ts +2 -0
- package/{src/scheduler/runner.ts → dist/scheduler/runner.js} +58 -84
- package/dist/scheduler/stranded-placement-redrive.d.ts +16 -0
- package/dist/scheduler/stranded-placement-redrive.js +172 -0
- package/dist/sqs-queues.d.ts +4 -0
- package/dist/sqs-queues.js +22 -0
- package/dist/with-oauth-lifecycle-deps.d.ts +5 -0
- package/dist/with-oauth-lifecycle-deps.js +17 -0
- package/dist/with-oauth-lifecycle.d.ts +56 -0
- package/dist/with-oauth-lifecycle.js +92 -0
- package/{src/worker-version.ts → dist/worker-version.d.ts} +1 -2
- package/dist/worker-version.js +12 -0
- package/package.json +75 -69
- package/src/account-check.test.ts +0 -149
- package/src/account-check.ts +0 -101
- package/src/body-sync-gate.test.ts +0 -185
- package/src/body-sync-gate.ts +0 -86
- package/src/cli.ts +0 -224
- package/src/connection-scope.test.ts +0 -266
- package/src/connection-scope.ts +0 -314
- package/src/credential-less-sync-fence.test.ts +0 -152
- package/src/e2e-processor-shim.ts +0 -232
- package/src/emit.test.ts +0 -127
- package/src/emit.ts +0 -117
- package/src/events.ts +0 -252
- package/src/failure-kind.test.ts +0 -42
- package/src/handlers/append-sent-message.sqlite.test.ts +0 -176
- package/src/handlers/append-sent-message.ts +0 -264
- package/src/handlers/delete-account-objects.test.ts +0 -128
- package/src/handlers/delete-account-objects.ts +0 -94
- package/src/handlers/empty-trash.sqlite.test.ts +0 -209
- package/src/handlers/empty-trash.ts +0 -352
- package/src/handlers/flag-push.ts +0 -285
- package/src/handlers/imported-folder.test.ts +0 -63
- package/src/handlers/imported-folder.ts +0 -38
- package/src/handlers/mailbox-management.sqlite.test.ts +0 -265
- package/src/handlers/mailbox-management.ts +0 -486
- package/src/handlers/mailbox-sync-order.test.ts +0 -93
- package/src/handlers/mailbox-sync-order.ts +0 -65
- package/src/handlers/message-copy.sqlite.test.ts +0 -298
- package/src/handlers/message-copy.ts +0 -566
- package/src/handlers/message-delete-terminal.ts +0 -147
- package/src/handlers/message-delete.sqlite.test.ts +0 -291
- package/src/handlers/message-delete.ts +0 -885
- package/src/handlers/message-move.sqlite.test.ts +0 -271
- package/src/handlers/message-move.test.ts +0 -216
- package/src/handlers/message-move.ts +0 -752
- package/src/handlers/placement-move-push.test.ts +0 -137
- package/src/handlers/placement-move-push.ts +0 -475
- package/src/handlers/placement-push.sqlite.test.ts +0 -298
- package/src/handlers/probe-message-id.sqlite.test.ts +0 -192
- package/src/handlers/probe-message-id.ts +0 -15
- package/src/handlers/sync-mailboxes.test.ts +0 -183
- package/src/handlers/sync-mailboxes.ts +0 -421
- package/src/handlers/sync-message-body.sqlite.test.ts +0 -314
- package/src/handlers/sync-message-body.test.ts +0 -150
- package/src/handlers/sync-message-body.ts +0 -365
- package/src/handlers/sync-messages.sqlite.test.ts +0 -183
- package/src/handlers/sync-messages.ts +0 -525
- package/src/index.test.ts +0 -22
- package/src/index.ts +0 -86
- package/src/mail-sync-selection.sqlite.test.ts +0 -234
- package/src/mailbox-row.ts +0 -20
- package/src/pending-sync.sqlite.test.ts +0 -166
- package/src/poller.ts +0 -55
- package/src/processor.ts +0 -60
- package/src/queues.ts +0 -49
- package/src/scheduler/attachment-sweep.sqlite.test.ts +0 -75
- package/src/scheduler/attachment-sweep.ts +0 -46
- package/src/scheduler/calendar-subscriptions.sqlite.test.ts +0 -147
- package/src/scheduler/calendar-subscriptions.ts +0 -103
- package/src/scheduler/config.test.ts +0 -40
- package/src/scheduler/decide-due.test.ts +0 -44
- package/src/scheduler/half-deleted-accounts.sqlite.test.ts +0 -196
- package/src/scheduler/loop.test.ts +0 -143
- package/src/scheduler/run-tick.sqlite.test.ts +0 -191
- package/src/scheduler/run-tick.ts +0 -281
- package/src/scheduler/stranded-placement-redrive.ts +0 -265
- package/src/stranded-placement.sqlite.test.ts +0 -794
- package/src/test-helpers/test-world.ts +0 -427
- package/src/thread-join.sqlite.test.ts +0 -106
- package/src/with-oauth-lifecycle-deps.sqlite.test.ts +0 -269
- package/src/with-oauth-lifecycle-deps.ts +0 -71
- package/src/with-oauth-lifecycle.ts +0 -158
- package/tsconfig.json +0 -8
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { MessageMutation, MessageSyncStatus } from "@remit/domain-enums";
|
|
2
|
+
import { isMessageGoneFromOpenMailbox, restoreSourcePlacement, } from "@remit/mailbox-service";
|
|
3
|
+
/**
|
|
4
|
+
* Resolve a MESSAGE_MOVE failure that has exhausted the message queue's
|
|
5
|
+
* redelivery budget into exactly one of two terminal outcomes, mirroring
|
|
6
|
+
* `resolveExhaustedPlacementMoveFailure` and `resolveExhaustedFlagPushFailure`
|
|
7
|
+
* for the same failure taxonomy (issue #655) — no third, softer outcome.
|
|
8
|
+
*
|
|
9
|
+
* The move's pending state is the Message row itself (`status: moving`,
|
|
10
|
+
* `originalMailboxId`/`originalUid`, `mailboxId` already pointing at the
|
|
11
|
+
* destination), not a separate marker, so the outcomes act on that row.
|
|
12
|
+
*
|
|
13
|
+
* 1. RECONCILED (expected) — the message no longer exists at the move's source
|
|
14
|
+
* on IMAP, confirmed by {@link isMessageGoneFromOpenMailbox} rather than by
|
|
15
|
+
* a FETCH coming back empty. Either the MOVE did execute server-side and
|
|
16
|
+
* the connection dropped before the tagged OK was read, or a foreign client
|
|
17
|
+
* moved or expunged the message; from here those are indistinguishable and
|
|
18
|
+
* have the same answer. The stale Message/ThreadMessage rows are deleted
|
|
19
|
+
* via `deleteIfPlacement` and the caller resyncs both folders, so
|
|
20
|
+
* whichever folder actually holds the message re-projects it with the
|
|
21
|
+
* server's own UID. Metric only, no alarm — routine.
|
|
22
|
+
* 2. BROKEN — the message is still at the source, so the move never took
|
|
23
|
+
* effect, but it keeps failing: broken code or a broken account, not a
|
|
24
|
+
* transient blip. The row is put back where the server has just said the
|
|
25
|
+
* message is, as `resolveExhaustedMessageDeleteFailure` does (#1098). Logged
|
|
26
|
+
* with an `alert`-shaped entry for an operator alarm; never re-thrown
|
|
27
|
+
* (terminal — the caller acks either way, since retrying a
|
|
28
|
+
* permanently-broken move can never succeed).
|
|
29
|
+
*
|
|
30
|
+
* Leaving the row `moving` is what this replaces. BROKEN is terminal, so
|
|
31
|
+
* nothing was ever going to come back and settle it, and only `updateUid`
|
|
32
|
+
* clears `moving` — the row kept a `mailboxId` naming the destination and a
|
|
33
|
+
* `uid` belonging to the source, which `bindsForeignUid` refuses to act on,
|
|
34
|
+
* leaving the message undeletable and unmovable for good (issue #1005).
|
|
35
|
+
* Restoring the pair is not the revert-on-ambiguity PR #652 was pulled for:
|
|
36
|
+
* the presence probe above has just answered, so this writes a placement the
|
|
37
|
+
* server confirmed, and a MOVE that lands after all is re-projected by the
|
|
38
|
+
* resync the caller runs.
|
|
39
|
+
*
|
|
40
|
+
* A server that cannot be reached at exhaustion time never reaches either
|
|
41
|
+
* verdict: the probe throws and the record dead-letters with the row untouched.
|
|
42
|
+
* Absence is only ever concluded from an answer the server gave.
|
|
43
|
+
*
|
|
44
|
+
* An operator reading `message_move_failed` should know one case where the
|
|
45
|
+
* message is not actually at the source: a message another client expunged
|
|
46
|
+
* mid-session can answer an empty FETCH while the server still lists its UID
|
|
47
|
+
* in SEARCH, until it is allowed to send the untagged EXPUNGE. That message
|
|
48
|
+
* lands in BROKEN, and BROKEN is terminal — the row is bound to a source the
|
|
49
|
+
* message has already left and the alert stands until someone clears it. The
|
|
50
|
+
* reverse mistake discards the row for live mail, so the cost is paid
|
|
51
|
+
* deliberately.
|
|
52
|
+
*/
|
|
53
|
+
export const resolveExhaustedMessageMoveFailure = async (deps, input) => {
|
|
54
|
+
const { accountId, accountConfigId, messageId, sourceMailboxId, uid, sourceMailboxPath, getConnection, } = input;
|
|
55
|
+
const connection = await getConnection();
|
|
56
|
+
await connection.openBox(sourceMailboxPath, true);
|
|
57
|
+
if (await isMessageGoneFromOpenMailbox(connection, uid)) {
|
|
58
|
+
const reconciled = await deps.messageService.deleteIfPlacement(messageId, input.placement);
|
|
59
|
+
deps.log.info({
|
|
60
|
+
metric: "message_move_stale_row_reconciled",
|
|
61
|
+
accountId,
|
|
62
|
+
accountConfigId,
|
|
63
|
+
messageId,
|
|
64
|
+
uid,
|
|
65
|
+
sourceMailboxPath,
|
|
66
|
+
reconciled,
|
|
67
|
+
}, "Message no longer at its move source after retry exhaustion (move landed server-side, or an external delete or move); stale row reconciled");
|
|
68
|
+
return { outcome: "reconciled" };
|
|
69
|
+
}
|
|
70
|
+
deps.log.error({
|
|
71
|
+
alert: "message_move_failed",
|
|
72
|
+
accountId,
|
|
73
|
+
accountConfigId,
|
|
74
|
+
messageId,
|
|
75
|
+
uid,
|
|
76
|
+
sourceMailboxId,
|
|
77
|
+
sourceMailboxPath,
|
|
78
|
+
}, "Message move could not be pushed to IMAP after retry exhaustion; the message is still at its source — row moved back to the source folder");
|
|
79
|
+
await restoreSourcePlacement(deps, {
|
|
80
|
+
accountConfigId,
|
|
81
|
+
messageId,
|
|
82
|
+
sourceMailboxId,
|
|
83
|
+
uid,
|
|
84
|
+
// The probe above just confirmed the pair, so the row is a faithful
|
|
85
|
+
// projection of the source again — and the move the user asked for did
|
|
86
|
+
// not happen, with nothing left to try it. `abandoned` is the value that
|
|
87
|
+
// says so without claiming a retry is coming (imap-mutations R3); the
|
|
88
|
+
// alert stays for the operator, and the mutation names itself so the
|
|
89
|
+
// reading pane offers the right way out (issue #1229).
|
|
90
|
+
syncStatus: MessageSyncStatus.abandoned,
|
|
91
|
+
abandonedMutation: MessageMutation.move,
|
|
92
|
+
});
|
|
93
|
+
return { outcome: "broken" };
|
|
94
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { ThreadMessageItem } from "@remit/data-ports";
|
|
2
|
+
import { type IImapConnection } from "@remit/mailbox-service";
|
|
3
|
+
import type { ProcessContext } from "../context.js";
|
|
4
|
+
import type { MessageMoveEvent, SyncMessagesEvent } from "../events.js";
|
|
5
|
+
type EmitSyncMessages = (event: Omit<SyncMessagesEvent, "eventId" | "timestamp">) => Promise<unknown>;
|
|
6
|
+
/**
|
|
7
|
+
* Re-read both folders' counts from IMAP after a move by enqueuing the existing
|
|
8
|
+
* per-folder SYNC_MESSAGES sync. Counts are a projection of IMAP, never mutated
|
|
9
|
+
* locally — the move shifted a message between source and destination, so both
|
|
10
|
+
* folders must refresh through the one-way pipeline.
|
|
11
|
+
*/
|
|
12
|
+
export declare const emitMoveResync: (emit: EmitSyncMessages, params: {
|
|
13
|
+
accountId: string;
|
|
14
|
+
sourceMailboxId: string;
|
|
15
|
+
destinationMailboxId: string;
|
|
16
|
+
}) => Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* The one-folder half of {@link emitMoveResync}, for a mutation that has no
|
|
19
|
+
* destination — a permanent delete removed local rows the server still holds,
|
|
20
|
+
* and only a resync of that folder puts them back.
|
|
21
|
+
*/
|
|
22
|
+
export declare const emitMailboxResync: (emit: EmitSyncMessages, params: {
|
|
23
|
+
accountId: string;
|
|
24
|
+
mailboxId: string;
|
|
25
|
+
}) => Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* SEARCH a mailbox for copies of a message identified by its RFC822 Message-ID
|
|
28
|
+
* header and answer the HIGHEST matching uid. Read-only (EXAMINE, not SELECT) —
|
|
29
|
+
* this is a verification probe, never a write. `null` when nothing matched.
|
|
30
|
+
*
|
|
31
|
+
* The highest uid is the freshly delivered copy only when one exists; deciding
|
|
32
|
+
* that it does is the caller's job, not this function's. Every probe in this
|
|
33
|
+
* package wants the highest: uids ascend with arrival inside a UIDVALIDITY, so a
|
|
34
|
+
* copy that has just landed outranks every copy of the same Message-ID the
|
|
35
|
+
* folder already held. Where nothing was delivered, the highest is simply the
|
|
36
|
+
* newest pre-existing copy, and a caller that has not ruled that case out binds
|
|
37
|
+
* itself to unrelated mail.
|
|
38
|
+
* One Message-ID can have several server copies in one account (a sieve
|
|
39
|
+
* `fileinto` + `keep`, a resend, a repeated COPY — `deriveCopyMessageId` reuses
|
|
40
|
+
* the row, so the second copy is a second server COPY), while `deriveMessageId`
|
|
41
|
+
* is folder-independent and gives them one local row. Taking the lowest bound
|
|
42
|
+
* the fresh copy's row to an older copy's uid and orphaned the fresh one, which
|
|
43
|
+
* a later delete or Empty Trash then expunged by the wrong uid (issue #1122).
|
|
44
|
+
*
|
|
45
|
+
* The maximum is computed rather than read off the tail: RFC 3501 leaves the
|
|
46
|
+
* order of a SEARCH response unspecified, so "last returned" is not "highest".
|
|
47
|
+
*/
|
|
48
|
+
export declare const searchMailboxForHighestMessageIdUid: (connection: IImapConnection, mailboxPath: string, messageIdHeader: string) => Promise<number | null>;
|
|
49
|
+
/**
|
|
50
|
+
* Which side of a paused mutation the server actually holds the message on.
|
|
51
|
+
* `gone` is an answer both folders gave; `unprobeable` is the row that carries
|
|
52
|
+
* no Message-ID header, where the server was never asked and its silence says
|
|
53
|
+
* nothing.
|
|
54
|
+
*/
|
|
55
|
+
export type PausedPlacement = {
|
|
56
|
+
kind: "at-source";
|
|
57
|
+
} | {
|
|
58
|
+
kind: "at-destination";
|
|
59
|
+
uid: number;
|
|
60
|
+
} | {
|
|
61
|
+
kind: "gone";
|
|
62
|
+
} | {
|
|
63
|
+
kind: "unprobeable";
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Ask the server, by Message-ID, which folder holds a message whose mutation a
|
|
67
|
+
* paused cursor interrupted (issue #1203).
|
|
68
|
+
*
|
|
69
|
+
* Reached only on a redelivery, where "the command never left" has stopped
|
|
70
|
+
* being provable: the earlier attempt's tagged OK can be lost with the
|
|
71
|
+
* connection, so restoring the source pair on that assumption would write a
|
|
72
|
+
* settled placement naming a folder the server has already moved the mail out
|
|
73
|
+
* of. `message-copy.ts` asks the same question for the same reason.
|
|
74
|
+
*
|
|
75
|
+
* The SOURCE is asked first and a hit ends it, the gate `confirmTrashMoveUid`
|
|
76
|
+
* documents (#1122): where the mutation never ran, every hit at the destination
|
|
77
|
+
* is an older copy of the same Message-ID — a sieve `fileinto` + `keep`, a
|
|
78
|
+
* multi-label store, a resend — that `deriveMessageId` folds into this one local
|
|
79
|
+
* row, and binding to it hands a later delete somebody else's uid.
|
|
80
|
+
*
|
|
81
|
+
* The source's silence, however, is not the mirror image of its hit. Every
|
|
82
|
+
* caller here reaches this on a paused cursor, which means the source's
|
|
83
|
+
* UIDVALIDITY has moved and the folder may have been recreated holding nothing
|
|
84
|
+
* it held before — so "not at the source" is an answer about the folder, not
|
|
85
|
+
* about our mutation, and `at-destination` is a sighting rather than a
|
|
86
|
+
* confirmation. Callers settle it as unproven; see `settlePausedMove`.
|
|
87
|
+
*
|
|
88
|
+
* Identity, not position, is what makes this askable at all: a paused cursor
|
|
89
|
+
* means the source's UIDVALIDITY has moved, so every stored uid for it names
|
|
90
|
+
* something else, but a SEARCH by header is independent of the axis and answers
|
|
91
|
+
* the same question the cursor rebuild itself matches rows by. For that reason
|
|
92
|
+
* the caller passes the UNGUARDED connection — a `guardConnectionCursor` wrap
|
|
93
|
+
* refuses to open the mailbox at all, and it is bound to the source's snapshot
|
|
94
|
+
* anyway, so opening the destination through it would trip that mailbox too.
|
|
95
|
+
*/
|
|
96
|
+
export declare const probePausedPlacement: (connection: IImapConnection, input: {
|
|
97
|
+
messageIdHeader: string | undefined;
|
|
98
|
+
sourceMailboxPath: string;
|
|
99
|
+
destinationMailboxPath: string | undefined;
|
|
100
|
+
}) => Promise<PausedPlacement>;
|
|
101
|
+
/**
|
|
102
|
+
* Resync the affected folders only once the IMAP move has resolved. A move that
|
|
103
|
+
* fails (or is retried) must not refresh counts off a move that didn't happen,
|
|
104
|
+
* so the resync is sequenced strictly after `performMove`.
|
|
105
|
+
*/
|
|
106
|
+
export declare const moveThenResync: (performMove: () => Promise<void>, resync: () => Promise<void>) => Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Build the `set` and `composites` payload for the ThreadMessage update on a
|
|
109
|
+
* MESSAGE_MOVE.
|
|
110
|
+
*
|
|
111
|
+
* The CURRENT row state goes in `composites`; the NEW values go in `set`.
|
|
112
|
+
* ElectroDB uses `composites` to run the conditional check on the existing row
|
|
113
|
+
* AND to compute the previous sort-key values needed to recompute the new ones.
|
|
114
|
+
* Passing the NEW values in `composites` makes the conditional check fail with
|
|
115
|
+
* ConditionalCheckFailedException, which ElectroDB wraps as NotFoundError, and
|
|
116
|
+
* the caller silently drops the update. Same root cause as PR #186 fixed for
|
|
117
|
+
* `flag-queue.ts`.
|
|
118
|
+
*/
|
|
119
|
+
export declare const buildThreadMessageMoveUpdate: (threadMessage: Pick<ThreadMessageItem, "sentDate" | "mailboxId" | "isRead" | "isDeleted" | "hasStars" | "hasAttachment">, newUid: number, destinationMailboxId: string) => {
|
|
120
|
+
set: {
|
|
121
|
+
uid: number;
|
|
122
|
+
mailboxId: string;
|
|
123
|
+
isDeleted: boolean;
|
|
124
|
+
};
|
|
125
|
+
composites: {
|
|
126
|
+
sentDate: number;
|
|
127
|
+
mailboxId: string;
|
|
128
|
+
isRead: boolean;
|
|
129
|
+
isDeleted: boolean;
|
|
130
|
+
hasStars: boolean;
|
|
131
|
+
hasAttachment: boolean;
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Handle MESSAGE_MOVE events.
|
|
136
|
+
* Executes IMAP MOVE command and updates local state with new UID.
|
|
137
|
+
*
|
|
138
|
+
* A failing move retries on SQS redelivery until `receiveCount` reaches
|
|
139
|
+
* the `config.attempts.messageMove` budget, at which point
|
|
140
|
+
* {@link resolveExhaustedMessageMoveFailure} asks IMAP where the message
|
|
141
|
+
* actually is and settles the row into one terminal outcome (issue #655).
|
|
142
|
+
* Before that, `syncStatus: failed` marked every attempt and nothing ever
|
|
143
|
+
* settled the row, so an exhausted move sat `moving`/`failed` forever.
|
|
144
|
+
*/
|
|
145
|
+
export declare const handleMessageMove: (event: MessageMoveEvent, ctx: ProcessContext, receiveCount: number) => Promise<void>;
|
|
146
|
+
export {};
|