@remit/mailbox-service 0.0.112 → 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} +16 -72
- 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} +38 -64
- 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} +13 -68
- 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 -97
- 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 -88
- package/src/account-credentials.ts +0 -210
- 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 -410
- package/src/body-sync-queue.test.ts +0 -64
- package/src/body-sync-queue.ts +0 -131
- package/src/body-sync-terminal.test.ts +0 -148
- package/src/body-sync-terminal.ts +0 -174
- package/src/body-sync.test.ts +0 -925
- package/src/body-sync.ts +0 -1891
- package/src/calendar-parts.ts +0 -41
- package/src/classification-backfill.test.ts +0 -285
- package/src/classification-backfill.ts +0 -363
- package/src/classify-message.test.ts +0 -142
- 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 -262
- package/src/external-move.ts +0 -285
- package/src/filter-operations.test.ts +0 -319
- package/src/filter-operations.ts +0 -377
- package/src/filter-update.test.ts +0 -313
- package/src/filters/filter-config.test.ts +0 -87
- package/src/filters/filter-config.ts +0 -34
- package/src/filters/list-id.test.ts +0 -50
- package/src/filters/match.test.ts +0 -392
- package/src/filters/match.ts +0 -225
- package/src/filters/pipeline.test.ts +0 -199
- package/src/filters/pipeline.ts +0 -170
- package/src/flag-push.test.ts +0 -112
- package/src/flag-push.ts +0 -198
- package/src/flag-queue.test.ts +0 -197
- package/src/flag-queue.ts +0 -509
- package/src/folder-operations.test.ts +0 -273
- package/src/folder-operations.ts +0 -217
- package/src/folder-placement.ts +0 -56
- package/src/forwarded-message.test.ts +0 -122
- 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 -97
- package/src/imapflow-connection.test.ts +0 -503
- package/src/imapflow-connection.ts +0 -1476
- package/src/index.ts +0 -397
- package/src/list-id-backfill.test.ts +0 -207
- 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 -141
- package/src/mailbox-delete-intent.test.ts +0 -119
- package/src/mailbox-management.test.ts +0 -243
- package/src/mailbox-management.ts +0 -838
- package/src/mailbox-presence.test.ts +0 -74
- package/src/mailbox-queue.ts +0 -397
- package/src/mailbox-rename-intent.test.ts +0 -220
- package/src/mailbox-rename-subtree.test.ts +0 -287
- package/src/mailbox-sync.test.ts +0 -286
- package/src/mailbox-sync.ts +0 -724
- package/src/message-move.test.ts +0 -726
- package/src/message-move.ts +0 -1518
- package/src/message-parser.ts +0 -26
- package/src/message-presence.test.ts +0 -50
- package/src/message-read.ts +0 -87
- package/src/message-save-retry.test.ts +0 -90
- package/src/message-sync-flags.test.ts +0 -167
- package/src/message-sync.test.ts +0 -762
- package/src/message-sync.ts +0 -2133
- package/src/mime-walker.test.ts +0 -311
- package/src/mime-walker.ts +0 -306
- package/src/organize-job.ts +0 -93
- 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 -626
- package/src/outbox-attachment.ts +0 -536
- package/src/outbox-failed-recovery.test.ts +0 -299
- package/src/outbox-queue.test.ts +0 -315
- package/src/outbox-queue.ts +0 -525
- package/src/ownership.ts +0 -140
- package/src/placement-move-terminal.ts +0 -145
- package/src/placement-move.ts +0 -363
- package/src/placement-settled.test.ts +0 -147
- package/src/placement.test.ts +0 -404
- package/src/quarantine.test.ts +0 -133
- package/src/quarantine.ts +0 -229
- package/src/refusal.ts +0 -29
- package/src/snippet.test.ts +0 -213
- package/src/snippet.ts +0 -152
- package/src/spam-report.test.ts +0 -329
- package/src/spam-report.ts +0 -357
- package/src/sync-warning.test.ts +0 -213
- package/src/sync-warning.ts +0 -56
- 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/fake-imap.ts +0 -690
- package/src/test-helpers/folder-roles.ts +0 -66
- package/src/test-helpers/mime-tree-generator.ts +0 -461
- package/src/test-helpers/test-queue.ts +0 -56
- package/src/test-helpers/test-store.ts +0 -299
- 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/thread-root.test.ts +0 -29
- package/src/thread-root.ts +0 -51
- package/src/types/mailparser-augment.d.ts +0 -23
- 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/tsconfig.json +0 -8
package/src/flag-push.ts
DELETED
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { SendMessageCommand, type SQSClient } from "@aws-sdk/client-sqs";
|
|
3
|
-
import type { IMessageFlagPushRepository } from "@remit/data-ports";
|
|
4
|
-
import { createQueueProducer } from "@remit/sqs-client/producer";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Event the reconciler (imap-worker `handleFlagPush`) drains. Carries ONLY
|
|
8
|
-
* our own message id + the flag field — never a UID and never the desired
|
|
9
|
-
* operation (that lives on the marker, resolved fresh at push time, epic
|
|
10
|
-
* #1281 invariant 1). The event stays valid across any amount of queue delay
|
|
11
|
-
* or a later local flip of the SAME field (which replaces the marker before
|
|
12
|
-
* this event is ever processed).
|
|
13
|
-
*/
|
|
14
|
-
export interface FlagPushEvent {
|
|
15
|
-
type: "FLAG_PUSH";
|
|
16
|
-
eventId: string;
|
|
17
|
-
timestamp: number;
|
|
18
|
-
accountId: string;
|
|
19
|
-
accountConfigId: string;
|
|
20
|
-
messageId: string;
|
|
21
|
-
flagName: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface FlagPushLogger {
|
|
25
|
-
info(obj: Record<string, unknown>, msg: string): void;
|
|
26
|
-
error(obj: Record<string, unknown>, msg: string): void;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const noopLogger: FlagPushLogger = {
|
|
30
|
-
info: () => {},
|
|
31
|
-
error: () => {},
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export interface FlagPushConfig {
|
|
35
|
-
markerService: IMessageFlagPushRepository;
|
|
36
|
-
sqsQueueUrl: string;
|
|
37
|
-
sqsEndpoint?: string;
|
|
38
|
-
logger?: FlagPushLogger;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export type FlagPushOperationValue = "add" | "remove";
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Writes a pending flag-push marker and best-effort enqueues a wake-up hint
|
|
45
|
-
* (issue #1273, epic #1281). Called AFTER the caller (`FlagQueueService`) has
|
|
46
|
-
* already durably written the local `MessageFlag`/`ThreadMessage` state in
|
|
47
|
-
* the SAME request — the user's intent is durable the moment `flip` writes
|
|
48
|
-
* the marker, before the enqueue even runs.
|
|
49
|
-
*
|
|
50
|
-
* Mirrors `PlacementMoveService` (#1289)'s marker lifecycle, scoped per
|
|
51
|
-
* (`messageId`, `flagName`) instead of per `messageId` — a message can carry
|
|
52
|
-
* an independent pending read-state marker and pending star marker at once
|
|
53
|
-
* (epic FAQ: "a flag flip replaces a pending flag flip", scoped per field).
|
|
54
|
-
* `put` (not `create`) always resets the marker to `pending`, so a later flip
|
|
55
|
-
* of the SAME field simply replaces the earlier marker — "later intent wins
|
|
56
|
-
* locally".
|
|
57
|
-
*
|
|
58
|
-
* Unlike #1289's placement move (where an enqueue failure must propagate,
|
|
59
|
-
* because nothing besides the marker+enqueue sequence has committed the
|
|
60
|
-
* user's intent), the flag flip here is durable the instant the marker write
|
|
61
|
-
* lands — the enqueue is purely a latency optimization so the push does not
|
|
62
|
-
* have to wait for the next periodic sync tick. An enqueue failure is
|
|
63
|
-
* therefore swallowed on purpose: it must never fail the caller's request.
|
|
64
|
-
* It must also never be silent — logged with an `alert`-shaped entry so a
|
|
65
|
-
* string of failures (SQS itself unhealthy) is operationally visible, same
|
|
66
|
-
* convention as the enqueue this replaces (`flag-queue.ts`'s prior
|
|
67
|
-
* `flag_sync_enqueue_failed`).
|
|
68
|
-
*/
|
|
69
|
-
export class FlagPushService {
|
|
70
|
-
private markerService: IMessageFlagPushRepository;
|
|
71
|
-
private sqs: SQSClient;
|
|
72
|
-
private queueUrl: string;
|
|
73
|
-
private log: FlagPushLogger;
|
|
74
|
-
|
|
75
|
-
constructor(config: FlagPushConfig) {
|
|
76
|
-
this.markerService = config.markerService;
|
|
77
|
-
this.queueUrl = config.sqsQueueUrl;
|
|
78
|
-
this.log = config.logger ?? noopLogger;
|
|
79
|
-
this.sqs = createQueueProducer({
|
|
80
|
-
queueUrl: config.sqsQueueUrl,
|
|
81
|
-
endpoint: config.sqsEndpoint,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
flip = async (params: {
|
|
86
|
-
accountId: string;
|
|
87
|
-
accountConfigId: string;
|
|
88
|
-
messageId: string;
|
|
89
|
-
mailboxId: string;
|
|
90
|
-
flagName: string;
|
|
91
|
-
operation: FlagPushOperationValue;
|
|
92
|
-
}): Promise<void> => {
|
|
93
|
-
const {
|
|
94
|
-
accountId,
|
|
95
|
-
accountConfigId,
|
|
96
|
-
messageId,
|
|
97
|
-
mailboxId,
|
|
98
|
-
flagName,
|
|
99
|
-
operation,
|
|
100
|
-
} = params;
|
|
101
|
-
|
|
102
|
-
await this.markerService.put({
|
|
103
|
-
messageId,
|
|
104
|
-
flagName,
|
|
105
|
-
accountId,
|
|
106
|
-
accountConfigId,
|
|
107
|
-
mailboxId,
|
|
108
|
-
operation,
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
await this.enqueueHint({ accountId, accountConfigId, messageId, flagName });
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
private enqueueHint = async (params: {
|
|
115
|
-
accountId: string;
|
|
116
|
-
accountConfigId: string;
|
|
117
|
-
messageId: string;
|
|
118
|
-
flagName: string;
|
|
119
|
-
}): Promise<void> => {
|
|
120
|
-
const event: FlagPushEvent = {
|
|
121
|
-
type: "FLAG_PUSH",
|
|
122
|
-
eventId: randomUUID(),
|
|
123
|
-
timestamp: Date.now(),
|
|
124
|
-
...params,
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const useFifo = this.queueUrl.endsWith(".fifo");
|
|
128
|
-
|
|
129
|
-
// The marker persisted in `flip` is the durable record — the queue,
|
|
130
|
-
// per the issue's own words. This send is only a wake-up hint: a
|
|
131
|
-
// queue-down failure must NOT fail the caller's flip (intent is
|
|
132
|
-
// already durable) and must NOT be silent either (standing rule: our
|
|
133
|
-
// infra failing is never a quiet blip). The periodic per-mailbox
|
|
134
|
-
// drain (issue #1273) still finds and pushes the marker regardless.
|
|
135
|
-
await this.sqs
|
|
136
|
-
.send(
|
|
137
|
-
new SendMessageCommand({
|
|
138
|
-
QueueUrl: this.queueUrl,
|
|
139
|
-
MessageBody: JSON.stringify(event),
|
|
140
|
-
...(useFifo && {
|
|
141
|
-
MessageGroupId: params.accountId,
|
|
142
|
-
MessageDeduplicationId: event.eventId,
|
|
143
|
-
}),
|
|
144
|
-
}),
|
|
145
|
-
)
|
|
146
|
-
.then(async () => {
|
|
147
|
-
// Only advance past `pending` once the hint is CONFIRMED sent — a
|
|
148
|
-
// marker still `pending` is exactly the periodic drain's query
|
|
149
|
-
// target (`sync-messages.ts`'s `drainPendingFlagPushes`), so a
|
|
150
|
-
// failed send below must leave it there, never here.
|
|
151
|
-
await this.markerService
|
|
152
|
-
.updateState(params.messageId, params.flagName, "queued")
|
|
153
|
-
.catch(async (error: unknown) => {
|
|
154
|
-
// Fast-path race (review finding on #1292): the worker can
|
|
155
|
-
// drain AND delete the marker before this transition runs —
|
|
156
|
-
// SQS delivery + processing can outrun this .then(). The
|
|
157
|
-
// enqueue itself already succeeded, so re-check whether the
|
|
158
|
-
// marker still exists before treating this as an infra
|
|
159
|
-
// failure: if it's already gone, the push already completed
|
|
160
|
-
// and this is expected, routine — never the
|
|
161
|
-
// flag_push_hint_enqueue_failed alarm shape. Any OTHER
|
|
162
|
-
// failure (e.g. the re-check itself fails because the
|
|
163
|
-
// backend is genuinely down) still propagates to the outer
|
|
164
|
-
// catch below.
|
|
165
|
-
const stillPending = await this.markerService.find(
|
|
166
|
-
params.messageId,
|
|
167
|
-
params.flagName,
|
|
168
|
-
);
|
|
169
|
-
if (!stillPending) {
|
|
170
|
-
this.log.info(
|
|
171
|
-
{ eventId: event.eventId, ...params },
|
|
172
|
-
"Marker already drained before the queued-state transition (fast-path race, harmless)",
|
|
173
|
-
);
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
throw error;
|
|
177
|
-
});
|
|
178
|
-
this.log.info(
|
|
179
|
-
{ eventId: event.eventId, ...params },
|
|
180
|
-
"Enqueued FLAG_PUSH wake-up hint",
|
|
181
|
-
);
|
|
182
|
-
})
|
|
183
|
-
.catch((error: unknown) => {
|
|
184
|
-
this.log.error(
|
|
185
|
-
{
|
|
186
|
-
alert: "flag_push_hint_enqueue_failed",
|
|
187
|
-
eventId: event.eventId,
|
|
188
|
-
...params,
|
|
189
|
-
errorName: (error as { name?: string })?.name,
|
|
190
|
-
errorCode:
|
|
191
|
-
(error as { Code?: string })?.Code ??
|
|
192
|
-
(error as { code?: string })?.code,
|
|
193
|
-
},
|
|
194
|
-
"Failed to enqueue FLAG_PUSH wake-up hint (marker persisted; the periodic sync tick will still drain it)",
|
|
195
|
-
);
|
|
196
|
-
});
|
|
197
|
-
};
|
|
198
|
-
}
|
package/src/flag-queue.test.ts
DELETED
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
3
|
-
import type { AccountItem, MailboxItem } from "@remit/data-ports";
|
|
4
|
-
import { MessageSystemFlag } from "@remit/domain-enums";
|
|
5
|
-
import { FlagPushService } from "./flag-push.js";
|
|
6
|
-
import { resolveExhaustedFlagPushFailure } from "./flag-push-terminal.js";
|
|
7
|
-
import { FlagQueueService, type UpdateFlagsInput } from "./flag-queue.js";
|
|
8
|
-
import {
|
|
9
|
-
eml,
|
|
10
|
-
type FakeImapConnection,
|
|
11
|
-
FakeImapServer,
|
|
12
|
-
} from "./test-helpers/fake-imap.js";
|
|
13
|
-
import {
|
|
14
|
-
captureQueue,
|
|
15
|
-
openStore,
|
|
16
|
-
type QueueCapture,
|
|
17
|
-
type Store,
|
|
18
|
-
seedAccount,
|
|
19
|
-
seedMailbox,
|
|
20
|
-
seedMessage,
|
|
21
|
-
silentLog,
|
|
22
|
-
} from "./test-helpers/test-store.js";
|
|
23
|
-
|
|
24
|
-
let store: Store;
|
|
25
|
-
let capture: QueueCapture;
|
|
26
|
-
let account: AccountItem;
|
|
27
|
-
let inbox: MailboxItem;
|
|
28
|
-
let messageId: string;
|
|
29
|
-
|
|
30
|
-
beforeEach(async () => {
|
|
31
|
-
capture = captureQueue();
|
|
32
|
-
store = await openStore();
|
|
33
|
-
account = await seedAccount(store);
|
|
34
|
-
inbox = await seedMailbox(store, account, "INBOX");
|
|
35
|
-
messageId = (await seedMessage(store, account, inbox, 1)).message.messageId;
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
afterEach(async () => {
|
|
39
|
-
capture.restore();
|
|
40
|
-
await store.close();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
const flags = () =>
|
|
44
|
-
new FlagQueueService({
|
|
45
|
-
messageFlagService: store.messageFlag,
|
|
46
|
-
messageService: store.message,
|
|
47
|
-
threadMessageService: store.threadMessage,
|
|
48
|
-
flagPushService: new FlagPushService({
|
|
49
|
-
markerService: store.flagPush,
|
|
50
|
-
sqsQueueUrl: "https://sqs.test/flags",
|
|
51
|
-
}),
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
const update = (input: UpdateFlagsInput) =>
|
|
55
|
-
flags().updateFlags(
|
|
56
|
-
account.accountConfigId,
|
|
57
|
-
messageId,
|
|
58
|
-
account.accountId,
|
|
59
|
-
input,
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
const listing = async () =>
|
|
63
|
-
(
|
|
64
|
-
await store.threadMessage.findAllByMessageId(
|
|
65
|
-
account.accountConfigId,
|
|
66
|
-
messageId,
|
|
67
|
-
)
|
|
68
|
-
)[0];
|
|
69
|
-
|
|
70
|
-
const owedFlagged = async () =>
|
|
71
|
-
(await store.flagPush.find(messageId, MessageSystemFlag.Flagged))?.operation;
|
|
72
|
-
|
|
73
|
-
describe("updateFlags keeps the star and its colour in step", () => {
|
|
74
|
-
for (const [input, hasStars, star, owed] of [
|
|
75
|
-
[{ isStarred: true }, true, "yellow", "add"],
|
|
76
|
-
[{ isStarred: true, starColor: "red" }, true, "red", "add"],
|
|
77
|
-
[{ starColor: "blue" }, true, "blue", "add"],
|
|
78
|
-
[{ isStarred: true, starColor: "none" }, true, "none", "add"],
|
|
79
|
-
] as const) {
|
|
80
|
-
it(`${JSON.stringify(input)} stars as ${star} and owes the server \\Flagged`, async () => {
|
|
81
|
-
await update(input);
|
|
82
|
-
|
|
83
|
-
const row = await listing();
|
|
84
|
-
assert.equal(row.hasStars, hasStars);
|
|
85
|
-
assert.equal(row.star, star);
|
|
86
|
-
assert.equal(await owedFlagged(), owed);
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
for (const input of [{ isStarred: false }, { starColor: "none" }] as const) {
|
|
91
|
-
it(`${JSON.stringify(input)} unstars a starred message`, async () => {
|
|
92
|
-
await update({ isStarred: true });
|
|
93
|
-
|
|
94
|
-
await update(input);
|
|
95
|
-
|
|
96
|
-
const row = await listing();
|
|
97
|
-
assert.equal(row.hasStars, false);
|
|
98
|
-
assert.equal(row.star, "none");
|
|
99
|
-
assert.equal(await owedFlagged(), "remove");
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
it("marks read once and treats a repeat as no change", async () => {
|
|
104
|
-
await update({ isRead: true });
|
|
105
|
-
const first = await store.flagPush.find(messageId, MessageSystemFlag.Seen);
|
|
106
|
-
|
|
107
|
-
const result = await update({ isRead: true });
|
|
108
|
-
|
|
109
|
-
assert.equal(result.isRead, true);
|
|
110
|
-
assert.equal((await listing()).isRead, true);
|
|
111
|
-
assert.deepEqual(
|
|
112
|
-
await store.flagPush.find(messageId, MessageSystemFlag.Seen),
|
|
113
|
-
first,
|
|
114
|
-
);
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
describe("a flag push that exhausted its retries", () => {
|
|
119
|
-
const resolve = async (
|
|
120
|
-
connect: () => FakeImapConnection,
|
|
121
|
-
afterRead: () => Promise<unknown> = async () => {},
|
|
122
|
-
) => {
|
|
123
|
-
await update({ isRead: true });
|
|
124
|
-
const read = await store.message.get(messageId);
|
|
125
|
-
await afterRead();
|
|
126
|
-
return resolveExhaustedFlagPushFailure(
|
|
127
|
-
{
|
|
128
|
-
messageService: store.message,
|
|
129
|
-
markerService: store.flagPush,
|
|
130
|
-
log: silentLog,
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
placement: {
|
|
134
|
-
status: read.status,
|
|
135
|
-
mailboxId: read.mailboxId,
|
|
136
|
-
uid: read.uid,
|
|
137
|
-
},
|
|
138
|
-
accountId: account.accountId,
|
|
139
|
-
accountConfigId: account.accountConfigId,
|
|
140
|
-
messageId,
|
|
141
|
-
flagName: MessageSystemFlag.Seen,
|
|
142
|
-
uid: 1,
|
|
143
|
-
mailboxPath: "INBOX",
|
|
144
|
-
getConnection: async () => connect(),
|
|
145
|
-
},
|
|
146
|
-
);
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
it("drops the push and the stale rows of a message the server no longer holds", async () => {
|
|
150
|
-
const imap = new FakeImapServer();
|
|
151
|
-
imap.addFolder("INBOX");
|
|
152
|
-
|
|
153
|
-
const { outcome } = await resolve(() => imap.connection());
|
|
154
|
-
|
|
155
|
-
assert.equal(outcome, "reconciled");
|
|
156
|
-
assert.equal(
|
|
157
|
-
await store.flagPush.find(messageId, MessageSystemFlag.Seen),
|
|
158
|
-
null,
|
|
159
|
-
);
|
|
160
|
-
await assert.rejects(store.message.get(messageId));
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it("leaves a row the user moved after the push read it", async () => {
|
|
164
|
-
const imap = new FakeImapServer();
|
|
165
|
-
imap.addFolder("INBOX");
|
|
166
|
-
const archive = await seedMailbox(store, account, "Archive");
|
|
167
|
-
|
|
168
|
-
const { outcome } = await resolve(
|
|
169
|
-
() => imap.connection(),
|
|
170
|
-
() =>
|
|
171
|
-
store.message.transitionPlacement(
|
|
172
|
-
messageId,
|
|
173
|
-
{ mailboxId: inbox.mailboxId },
|
|
174
|
-
{ mailboxId: archive.mailboxId, uid: 7 },
|
|
175
|
-
),
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
assert.equal(outcome, "reconciled");
|
|
179
|
-
const moved = await store.message.get(messageId);
|
|
180
|
-
assert.equal(moved.mailboxId, archive.mailboxId);
|
|
181
|
-
assert.equal(moved.uid, 7);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it("keeps the push and the rows of a message still there, dropped FETCH row or not", async () => {
|
|
185
|
-
const imap = new FakeImapServer();
|
|
186
|
-
imap.addFolder("INBOX");
|
|
187
|
-
imap.deliver("INBOX", eml({}));
|
|
188
|
-
const connection = imap.connection();
|
|
189
|
-
connection.fetchMessages = async () => [];
|
|
190
|
-
|
|
191
|
-
const { outcome } = await resolve(() => connection);
|
|
192
|
-
|
|
193
|
-
assert.equal(outcome, "broken");
|
|
194
|
-
assert.ok(await store.flagPush.find(messageId, MessageSystemFlag.Seen));
|
|
195
|
-
assert.ok(await store.message.get(messageId));
|
|
196
|
-
});
|
|
197
|
-
});
|