@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
package/src/events.ts
DELETED
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
MUTATION_EVENT_SCHEMA_VERSION,
|
|
3
|
-
PATHLESS_MUTATION_EVENT_SCHEMA_VERSION,
|
|
4
|
-
} from "@remit/data-ports/mutation-events";
|
|
5
|
-
|
|
6
|
-
export interface BaseEvent {
|
|
7
|
-
accountId: string;
|
|
8
|
-
eventId: string; // Idempotency key
|
|
9
|
-
timestamp: number; // Unix timestamp
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface SyncMailboxesEvent extends BaseEvent {
|
|
13
|
-
type: "SYNC_MAILBOXES";
|
|
14
|
-
/**
|
|
15
|
-
* Set by POST /sync — the only trigger a person asks for by name. It makes
|
|
16
|
-
* the fan-out sync every mailbox, skipping the freshness gate that keeps
|
|
17
|
-
* incidental triggers (config load, OAuth connect, the scheduled tick) from
|
|
18
|
-
* re-enumerating folders that were just enumerated.
|
|
19
|
-
*/
|
|
20
|
-
explicitRequest?: boolean;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface SyncMessagesEvent extends BaseEvent {
|
|
24
|
-
type: "SYNC_MESSAGES";
|
|
25
|
-
mailboxId: string;
|
|
26
|
-
fullSync?: boolean; // If true, ignore lastSyncUid
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* A message to body-sync, carrying the UID resolved at envelope-sync time so
|
|
31
|
-
* the consumer can issue one ranged FETCH without a per-message DDB lookup.
|
|
32
|
-
*/
|
|
33
|
-
export interface SyncMessageBodyTarget {
|
|
34
|
-
messageId: string;
|
|
35
|
-
uid: number;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface SyncMessageBodyEvent extends BaseEvent {
|
|
39
|
-
type: "SYNC_MESSAGE_BODY";
|
|
40
|
-
mailboxId: string;
|
|
41
|
-
/**
|
|
42
|
-
* Message ids to sync. Always present for backward compatibility; the
|
|
43
|
-
* consumer falls back to this list (looking up each UID) when `messages`
|
|
44
|
-
* is absent.
|
|
45
|
-
*/
|
|
46
|
-
messageIds: string[];
|
|
47
|
-
/**
|
|
48
|
-
* Preferred shape: messageId+uid pairs. When present, the consumer skips the
|
|
49
|
-
* per-message UID lookup and fetches the whole batch in one ranged FETCH.
|
|
50
|
-
* Optional so older in-flight events (ids only) still process.
|
|
51
|
-
*/
|
|
52
|
-
messages?: SyncMessageBodyTarget[];
|
|
53
|
-
/**
|
|
54
|
-
* Set only by the read-miss re-arm cue (`BodySyncQueueService.requestBodySync`,
|
|
55
|
-
* called when a `/content` read finds `bodyStorageKey` set but the storage
|
|
56
|
-
* object missing). Tells the consumer to bypass the "already stored" skip
|
|
57
|
-
* guard and re-fetch + rewrite the body even though the DB row already
|
|
58
|
-
* carries a `bodyStorageKey` — otherwise stale metadata (key set, object
|
|
59
|
-
* gone) makes the read-miss retry loop forever, since the bulk skip guard
|
|
60
|
-
* would keep treating the message as already synced. Optional so older
|
|
61
|
-
* in-flight events (no force) keep the original skip behavior.
|
|
62
|
-
*/
|
|
63
|
-
force?: boolean;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface MailboxCreateEvent extends BaseEvent {
|
|
67
|
-
type: "MAILBOX_CREATE";
|
|
68
|
-
mailboxId: string;
|
|
69
|
-
path: string;
|
|
70
|
-
subscribe?: boolean;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export interface MailboxRenameEvent extends BaseEvent {
|
|
74
|
-
type: "MAILBOX_RENAME";
|
|
75
|
-
mailboxId: string;
|
|
76
|
-
oldPath: string;
|
|
77
|
-
newPath: string;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export interface MailboxDeleteEvent extends BaseEvent {
|
|
81
|
-
type: "MAILBOX_DELETE";
|
|
82
|
-
mailboxId: string;
|
|
83
|
-
path: string;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export type MailboxManagementEvent =
|
|
87
|
-
| MailboxCreateEvent
|
|
88
|
-
| MailboxRenameEvent
|
|
89
|
-
| MailboxDeleteEvent;
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Event for deleting a message (move to trash or permanent delete).
|
|
93
|
-
*/
|
|
94
|
-
interface MessageDeleteEventFields extends BaseEvent {
|
|
95
|
-
type: "MESSAGE_DELETE";
|
|
96
|
-
messageId: string;
|
|
97
|
-
mailboxId: string;
|
|
98
|
-
uid: number;
|
|
99
|
-
operation: "move_to_trash" | "permanent_delete";
|
|
100
|
-
destinationMailboxId?: string;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export interface MessageDeleteEventV2 extends MessageDeleteEventFields {
|
|
104
|
-
schemaVersion: typeof MUTATION_EVENT_SCHEMA_VERSION;
|
|
105
|
-
mailboxPath: string;
|
|
106
|
-
destinationMailboxPath?: string;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export interface MessageDeleteEventV3 extends MessageDeleteEventFields {
|
|
110
|
-
schemaVersion: typeof PATHLESS_MUTATION_EVENT_SCHEMA_VERSION;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export type MessageDeleteEvent = MessageDeleteEventV2 | MessageDeleteEventV3;
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Event for moving a message to another mailbox.
|
|
117
|
-
*/
|
|
118
|
-
interface MessageMoveEventFields extends BaseEvent {
|
|
119
|
-
type: "MESSAGE_MOVE";
|
|
120
|
-
messageId: string;
|
|
121
|
-
sourceMailboxId: string;
|
|
122
|
-
destinationMailboxId: string;
|
|
123
|
-
uid: number;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export interface MessageMoveEventV1 extends MessageMoveEventFields {
|
|
127
|
-
schemaVersion?: undefined;
|
|
128
|
-
sourceMailboxPath: string;
|
|
129
|
-
destinationMailboxPath: string;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export interface MessageMoveEventV3 extends MessageMoveEventFields {
|
|
133
|
-
schemaVersion: typeof PATHLESS_MUTATION_EVENT_SCHEMA_VERSION;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export type MessageMoveEvent = MessageMoveEventV1 | MessageMoveEventV3;
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Event for emptying the Trash mailbox.
|
|
140
|
-
*/
|
|
141
|
-
interface EmptyTrashEventFields extends BaseEvent {
|
|
142
|
-
type: "EMPTY_TRASH";
|
|
143
|
-
trashMailboxId: string;
|
|
144
|
-
/**
|
|
145
|
-
* The Trash folder's UIDVALIDITY at the moment the user consented. The
|
|
146
|
-
* handler compares it against what the SELECT serves: a path reused by a
|
|
147
|
-
* different folder (rename plus recreate) answers with a different value,
|
|
148
|
-
* and this is the only comparison the mutating connection cannot race.
|
|
149
|
-
*/
|
|
150
|
-
trashUidValidity: number;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export interface EmptyTrashEventV2 extends EmptyTrashEventFields {
|
|
154
|
-
schemaVersion: typeof MUTATION_EVENT_SCHEMA_VERSION;
|
|
155
|
-
trashMailboxPath: string;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export interface EmptyTrashEventV3 extends EmptyTrashEventFields {
|
|
159
|
-
schemaVersion: typeof PATHLESS_MUTATION_EVENT_SCHEMA_VERSION;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export type EmptyTrashEvent = EmptyTrashEventV2 | EmptyTrashEventV3;
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Event for copying a message to another mailbox.
|
|
166
|
-
*/
|
|
167
|
-
interface MessageCopyEventFields extends BaseEvent {
|
|
168
|
-
type: "MESSAGE_COPY";
|
|
169
|
-
sourceMessageId: string;
|
|
170
|
-
newMessageId: string;
|
|
171
|
-
sourceMailboxId: string;
|
|
172
|
-
destinationMailboxId: string;
|
|
173
|
-
uid: number;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export interface MessageCopyEventV1 extends MessageCopyEventFields {
|
|
177
|
-
schemaVersion?: undefined;
|
|
178
|
-
sourceMailboxPath: string;
|
|
179
|
-
destinationMailboxPath: string;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export interface MessageCopyEventV3 extends MessageCopyEventFields {
|
|
183
|
-
schemaVersion: typeof PATHLESS_MUTATION_EVENT_SCHEMA_VERSION;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export type MessageCopyEvent = MessageCopyEventV1 | MessageCopyEventV3;
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Event for appending a sent message to the Sent mailbox via IMAP APPEND.
|
|
190
|
-
*/
|
|
191
|
-
export interface AppendSentMessageEvent extends BaseEvent {
|
|
192
|
-
type: "APPEND_SENT_MESSAGE";
|
|
193
|
-
outboxMessageId: string;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Drains one pending placement-move marker (issue #1271, epic #1281) to
|
|
198
|
-
* IMAP. Deliberately carries no UID and no destination — both are resolved
|
|
199
|
-
* fresh from the Message row and the `MessagePlacementMove` marker at push
|
|
200
|
-
* time (epic invariant 1), so this event stays valid across any amount of
|
|
201
|
-
* queue delay or an unrelated UIDVALIDITY rebuild (#1272). If the marker is
|
|
202
|
-
* gone by the time this is processed (confirmed, superseded, or an external
|
|
203
|
-
* delete already reconciled it), the handler is a no-op.
|
|
204
|
-
*/
|
|
205
|
-
export interface PlacementMovePushEvent extends BaseEvent {
|
|
206
|
-
type: "PLACEMENT_MOVE_PUSH";
|
|
207
|
-
accountConfigId: string;
|
|
208
|
-
messageId: string;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Wake-up hint for one pending flag-push marker (issue #1273, epic #1281).
|
|
213
|
-
* Carries only our message id + the flag field — never a UID and never the
|
|
214
|
-
* desired add/remove transition (both live on the `MessageFlagPush` marker,
|
|
215
|
-
* resolved fresh at push time, epic invariant 1). Produced two ways: by
|
|
216
|
-
* `FlagPushService` (remit-mailbox-service) right after a user's flag flip
|
|
217
|
-
* commits locally, and by the periodic per-mailbox sync tick
|
|
218
|
-
* (`sync-messages.ts`) re-arming any marker it finds still stuck `pending`
|
|
219
|
-
* (a prior enqueue that never landed). If the marker is gone or has moved
|
|
220
|
-
* past `pending` by the time this is processed, the handler is a no-op.
|
|
221
|
-
*/
|
|
222
|
-
export interface FlagPushEvent extends BaseEvent {
|
|
223
|
-
type: "FLAG_PUSH";
|
|
224
|
-
accountConfigId: string;
|
|
225
|
-
messageId: string;
|
|
226
|
-
flagName: string;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export interface DeleteAccountObjectsEvent {
|
|
230
|
-
type: "DELETE_ACCOUNT_OBJECTS";
|
|
231
|
-
accountConfigId: string;
|
|
232
|
-
continuationToken?: string;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/** Union of all event types the worker can process (including non-IMAP ones). */
|
|
236
|
-
export type WorkerEvent = ImapEvent | DeleteAccountObjectsEvent;
|
|
237
|
-
|
|
238
|
-
export type MessageManagementEvent =
|
|
239
|
-
| MessageDeleteEvent
|
|
240
|
-
| MessageMoveEvent
|
|
241
|
-
| EmptyTrashEvent
|
|
242
|
-
| MessageCopyEvent;
|
|
243
|
-
|
|
244
|
-
export type ImapEvent =
|
|
245
|
-
| SyncMailboxesEvent
|
|
246
|
-
| SyncMessagesEvent
|
|
247
|
-
| SyncMessageBodyEvent
|
|
248
|
-
| MailboxManagementEvent
|
|
249
|
-
| MessageManagementEvent
|
|
250
|
-
| AppendSentMessageEvent
|
|
251
|
-
| PlacementMovePushEvent
|
|
252
|
-
| FlagPushEvent;
|
package/src/failure-kind.test.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { describe, it } from "node:test";
|
|
3
|
-
import { RefreshTokenError } from "@remit/mail-oauth-service";
|
|
4
|
-
import { MailConnectionError } from "@remit/mailbox-service";
|
|
5
|
-
import { imapFailureKind } from "./failure-kind.js";
|
|
6
|
-
|
|
7
|
-
describe("imapFailureKind", () => {
|
|
8
|
-
it("counts an IMAP authentication rejection as auth", () => {
|
|
9
|
-
assert.equal(
|
|
10
|
-
imapFailureKind(
|
|
11
|
-
new MailConnectionError("auth", "IMAP authentication failed"),
|
|
12
|
-
),
|
|
13
|
-
"auth",
|
|
14
|
-
);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("counts a token refresh that cannot mint credentials as auth", () => {
|
|
18
|
-
assert.equal(
|
|
19
|
-
imapFailureKind(
|
|
20
|
-
new RefreshTokenError({
|
|
21
|
-
kind: "reauth-required",
|
|
22
|
-
code: "invalid_grant",
|
|
23
|
-
}),
|
|
24
|
-
),
|
|
25
|
-
"auth",
|
|
26
|
-
);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("keeps a network failure apart from an auth failure", () => {
|
|
30
|
-
assert.equal(
|
|
31
|
-
imapFailureKind(
|
|
32
|
-
new MailConnectionError("network", "IMAP connection failed: ETIMEDOUT"),
|
|
33
|
-
),
|
|
34
|
-
"network",
|
|
35
|
-
);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("classifies anything else as other", () => {
|
|
39
|
-
assert.equal(imapFailureKind(new Error("bad cursor")), "other");
|
|
40
|
-
assert.equal(imapFailureKind(undefined), "other");
|
|
41
|
-
});
|
|
42
|
-
});
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { after, before, describe, it } from "node:test";
|
|
3
|
-
import type { OutboxMessageItem } from "@remit/data-ports";
|
|
4
|
-
import { MailboxSpecialUse, OutboxMessageStatus } from "@remit/domain-enums";
|
|
5
|
-
import type { AppendSentMessageEvent } from "../events.js";
|
|
6
|
-
import { openStore, type World } from "../test-helpers/test-world.js";
|
|
7
|
-
import { handleAppendSentMessage } from "./append-sent-message.js";
|
|
8
|
-
|
|
9
|
-
let store: Awaited<ReturnType<typeof openStore>>;
|
|
10
|
-
|
|
11
|
-
before(async () => {
|
|
12
|
-
store = await openStore();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
after(() => store.close());
|
|
16
|
-
|
|
17
|
-
interface Arranged {
|
|
18
|
-
w: World;
|
|
19
|
-
outbox: OutboxMessageItem;
|
|
20
|
-
event: AppendSentMessageEvent;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* A message composed and sent through the service the API calls, then marked
|
|
25
|
-
* sent the way the SMTP worker marks it once the relay accepted it.
|
|
26
|
-
*/
|
|
27
|
-
const arrange = async (
|
|
28
|
-
options: { sentFolder?: boolean; attachment?: Buffer } = {},
|
|
29
|
-
): Promise<Arranged> => {
|
|
30
|
-
const w = await store.world();
|
|
31
|
-
await w.folder("INBOX");
|
|
32
|
-
if (options.sentFolder !== false) {
|
|
33
|
-
await w.folder("Sent", { specialUse: MailboxSpecialUse.Sent });
|
|
34
|
-
}
|
|
35
|
-
const { outbox: draft } = await w.draft(options.attachment);
|
|
36
|
-
const outbox = await w.client.outboxQueue.send(
|
|
37
|
-
w.account.accountConfigId,
|
|
38
|
-
draft.outboxMessageId,
|
|
39
|
-
);
|
|
40
|
-
await w.client.outboxMessage.update(
|
|
41
|
-
w.account.accountConfigId,
|
|
42
|
-
outbox.outboxMessageId,
|
|
43
|
-
{ status: OutboxMessageStatus.sent, sentAt: Date.now() },
|
|
44
|
-
);
|
|
45
|
-
return {
|
|
46
|
-
w,
|
|
47
|
-
outbox,
|
|
48
|
-
event: {
|
|
49
|
-
type: "APPEND_SENT_MESSAGE",
|
|
50
|
-
eventId: `evt-${outbox.outboxMessageId}`,
|
|
51
|
-
timestamp: Date.now(),
|
|
52
|
-
accountId: w.account.accountId,
|
|
53
|
-
outboxMessageId: outbox.outboxMessageId,
|
|
54
|
-
} as AppendSentMessageEvent,
|
|
55
|
-
};
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const run = (a: Arranged, receiveCount = 1) =>
|
|
59
|
-
handleAppendSentMessage(a.event, a.w.log, receiveCount, a.w.scope);
|
|
60
|
-
|
|
61
|
-
const outboxRow = (a: Arranged) =>
|
|
62
|
-
a.w.client.outboxMessage
|
|
63
|
-
.get(a.w.account.accountConfigId, a.outbox.outboxMessageId)
|
|
64
|
-
.catch(() => undefined);
|
|
65
|
-
|
|
66
|
-
describe("filing a sent message", () => {
|
|
67
|
-
it("stores a seen copy under its own Message-ID in Sent and drops the outbox row", async () => {
|
|
68
|
-
const a = await arrange();
|
|
69
|
-
|
|
70
|
-
await run(a);
|
|
71
|
-
|
|
72
|
-
const [copy] = a.w.imap.messages("Sent");
|
|
73
|
-
assert.equal(copy?.messageIdHeader, `<${a.outbox.messageIdValue}>`);
|
|
74
|
-
assert.ok(copy?.flags.includes("\\Seen"));
|
|
75
|
-
assert.match(
|
|
76
|
-
copy?.raw.toString("utf8") ?? "",
|
|
77
|
-
/Subject: Quarterly numbers/,
|
|
78
|
-
);
|
|
79
|
-
assert.equal(await outboxRow(a), undefined);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
// #679: the Sent copy carries the files the wire copy carried, and the row
|
|
83
|
-
// was the only reference to them, so they go with it.
|
|
84
|
-
it("files the copy with its attachment and removes the stored file", async () => {
|
|
85
|
-
const a = await arrange({ attachment: Buffer.from("q1,q2\n1,2\n") });
|
|
86
|
-
|
|
87
|
-
await run(a);
|
|
88
|
-
|
|
89
|
-
const raw = a.w.imap.messages("Sent")[0]?.raw.toString("utf8") ?? "";
|
|
90
|
-
assert.match(raw, /^Content-Type: multipart\/mixed;/m);
|
|
91
|
-
assert.match(raw, /filename=numbers\.csv/);
|
|
92
|
-
assert.deepEqual(
|
|
93
|
-
await a.w.client.storage.listOutboxAttachments(
|
|
94
|
-
a.w.account.accountConfigId,
|
|
95
|
-
a.w.account.accountId,
|
|
96
|
-
a.outbox.outboxMessageId,
|
|
97
|
-
),
|
|
98
|
-
[],
|
|
99
|
-
);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("settles the row as unfiled when the account has no Sent folder", async () => {
|
|
103
|
-
const a = await arrange({ sentFolder: false });
|
|
104
|
-
|
|
105
|
-
await run(a);
|
|
106
|
-
|
|
107
|
-
assert.equal(a.w.imap.connections, 0);
|
|
108
|
-
const row = await outboxRow(a);
|
|
109
|
-
assert.equal(row?.status, OutboxMessageStatus.unfiled);
|
|
110
|
-
assert.match(
|
|
111
|
-
String(row?.lastError),
|
|
112
|
-
/no folder appointed to the Sent role/,
|
|
113
|
-
);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("files nothing while the message has not been sent yet", async () => {
|
|
117
|
-
const a = await arrange();
|
|
118
|
-
await a.w.client.outboxMessage.update(
|
|
119
|
-
a.w.account.accountConfigId,
|
|
120
|
-
a.outbox.outboxMessageId,
|
|
121
|
-
{ status: OutboxMessageStatus.queued },
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
await run(a);
|
|
125
|
-
|
|
126
|
-
assert.deepEqual(a.w.imap.commands("APPEND"), []);
|
|
127
|
-
assert.equal((await outboxRow(a))?.status, OutboxMessageStatus.queued);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it("keeps the row and redelivers a refused APPEND within the budget", async () => {
|
|
131
|
-
const a = await arrange();
|
|
132
|
-
a.w.imap.failNext.APPEND = new Error("Connection reset");
|
|
133
|
-
|
|
134
|
-
await assert.rejects(run(a), /Connection reset/);
|
|
135
|
-
|
|
136
|
-
assert.equal((await outboxRow(a))?.status, OutboxMessageStatus.sent);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("settles the row as unfiled on the last attempt instead of dead-lettering it", async () => {
|
|
140
|
-
const a = await arrange();
|
|
141
|
-
a.w.imap.failNext.APPEND = new Error("Mailbox is full");
|
|
142
|
-
|
|
143
|
-
await run(a, 3);
|
|
144
|
-
|
|
145
|
-
const row = await outboxRow(a);
|
|
146
|
-
assert.equal(row?.status, OutboxMessageStatus.unfiled);
|
|
147
|
-
assert.match(String(row?.lastError), /Mailbox is full/);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// #858: a redelivery after the APPEND landed must not file a second copy.
|
|
151
|
-
it("files nothing a second time when the copy was already recorded", async () => {
|
|
152
|
-
const a = await arrange();
|
|
153
|
-
await a.w.client.outboxMessage.update(
|
|
154
|
-
a.w.account.accountConfigId,
|
|
155
|
-
a.outbox.outboxMessageId,
|
|
156
|
-
{ appendedUid: 41 },
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
await run(a);
|
|
160
|
-
|
|
161
|
-
assert.deepEqual(a.w.imap.commands("APPEND"), []);
|
|
162
|
-
assert.equal(await outboxRow(a), undefined);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it("acks an event whose outbox row is already gone", async () => {
|
|
166
|
-
const a = await arrange();
|
|
167
|
-
await a.w.client.outboxMessage.delete(
|
|
168
|
-
a.w.account.accountConfigId,
|
|
169
|
-
a.outbox.outboxMessageId,
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
await run(a);
|
|
173
|
-
|
|
174
|
-
assert.equal(a.w.imap.connections, 0);
|
|
175
|
-
});
|
|
176
|
-
});
|