@remit/backend 0.0.109 → 0.0.110

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/backend",
3
- "version": "0.0.109",
3
+ "version": "0.0.110",
4
4
  "description": "Remit Mail Inspector API backend",
5
5
  "license": "MIT",
6
6
  "author": "",
@@ -9,7 +9,9 @@ import { describe, it } from "node:test";
9
9
  import {
10
10
  computeMessagesSynced,
11
11
  deriveMailboxPhase,
12
+ type MailboxProgressFields,
12
13
  type MailboxSyncFields,
14
+ toMailboxSyncProgress,
13
15
  } from "./sync-progress.js";
14
16
 
15
17
  // ─── deriveMailboxPhase tests ───────────────────────────────────────────────
@@ -156,3 +158,48 @@ describe("computeMessagesSynced", () => {
156
158
  assert.equal(computeMessagesSynced(mailbox), 50);
157
159
  });
158
160
  });
161
+
162
+ // ─── toMailboxSyncProgress tests ────────────────────────────────────────────
163
+
164
+ const mailboxRow = (
165
+ overrides: Partial<MailboxProgressFields> = {},
166
+ ): MailboxProgressFields => ({
167
+ mailboxId: "mb-1",
168
+ fullPath: "INBOX",
169
+ lastMessageSyncAt: 1_700_000_000_000,
170
+ initialSyncCompletedAt: 1_700_000_000_001,
171
+ messageCount: 10,
172
+ lastSyncUid: 1,
173
+ highWaterMarkUid: 100,
174
+ ...overrides,
175
+ });
176
+
177
+ describe("toMailboxSyncProgress", () => {
178
+ // Issue #771: the client cannot tell an arrival from a departure by count —
179
+ // one message in and one out nets to zero. The highest UID only ever moves
180
+ // up, so the projection has to carry it or the arrival stays invisible.
181
+ it("carries the mailbox's highest UID", () => {
182
+ assert.equal(
183
+ toMailboxSyncProgress(mailboxRow({ highWaterMarkUid: 4321 }))
184
+ .highWaterMarkUid,
185
+ 4321,
186
+ );
187
+ });
188
+
189
+ it("reports the sentinel 0 for a mailbox that has never taken a message", () => {
190
+ assert.equal(
191
+ toMailboxSyncProgress(mailboxRow({ highWaterMarkUid: 0 }))
192
+ .highWaterMarkUid,
193
+ 0,
194
+ );
195
+ });
196
+
197
+ it("projects identity, phase and totals alongside it", () => {
198
+ const progress = toMailboxSyncProgress(mailboxRow());
199
+ assert.equal(progress.mailboxId, "mb-1");
200
+ assert.equal(progress.fullPath, "INBOX");
201
+ assert.equal(progress.phase, "complete");
202
+ assert.equal(progress.messagesTotal, 10);
203
+ assert.equal(progress.lastSyncedAt, 1_700_000_000_000);
204
+ });
205
+ });
@@ -10,6 +10,21 @@ export type MailboxSyncFields = Pick<
10
10
  | "highWaterMarkUid"
11
11
  >;
12
12
 
13
+ /** A mailbox row, as far as the sync-status projection reads it. */
14
+ export type MailboxProgressFields = MailboxSyncFields &
15
+ Pick<MailboxItem, "mailboxId" | "fullPath">;
16
+
17
+ /** The per-mailbox entry `getSyncStatus` returns. */
18
+ export interface MailboxSyncProgress {
19
+ mailboxId: string;
20
+ fullPath: string;
21
+ phase: string;
22
+ messagesTotal: number;
23
+ messagesSynced: number;
24
+ highWaterMarkUid: number;
25
+ lastSyncedAt: number | undefined;
26
+ }
27
+
13
28
  /**
14
29
  * Derive a per-mailbox sync phase from worker-written sync state.
15
30
  *
@@ -62,3 +77,23 @@ export const computeMessagesSynced = (mailbox: MailboxSyncFields): number => {
62
77
 
63
78
  return Math.min(Math.max(synced, 0), messagesTotal);
64
79
  };
80
+
81
+ /**
82
+ * Project a mailbox row into its sync-status entry.
83
+ *
84
+ * `highWaterMarkUid` is carried through because a total cannot tell an arrival
85
+ * from a departure: a message landing in the same sync round that another one
86
+ * leaves nets to zero, and the client's freshness check saw nothing (#771). The
87
+ * highest UID only ever moves up, so it names the arrival on its own.
88
+ */
89
+ export const toMailboxSyncProgress = (
90
+ mailbox: MailboxProgressFields,
91
+ ): MailboxSyncProgress => ({
92
+ mailboxId: mailbox.mailboxId,
93
+ fullPath: mailbox.fullPath,
94
+ phase: deriveMailboxPhase(mailbox),
95
+ messagesTotal: mailbox.messageCount ?? 0,
96
+ messagesSynced: computeMessagesSynced(mailbox),
97
+ highWaterMarkUid: mailbox.highWaterMarkUid ?? 0,
98
+ lastSyncedAt: mailbox.lastMessageSyncAt || undefined,
99
+ });
@@ -12,7 +12,7 @@ import { sqsClient } from "../service/sqs.js";
12
12
  import { triggerAccountSync } from "../service/trigger-sync.js";
13
13
  import type { OperationHandler, SyncOperationIds } from "../types.js";
14
14
  import { assertAccountOwnership } from "./account-ownership.js";
15
- import { computeMessagesSynced, deriveMailboxPhase } from "./sync-progress.js";
15
+ import { toMailboxSyncProgress } from "./sync-progress.js";
16
16
 
17
17
  interface SyncTriggerDeps {
18
18
  sqsClient: SQSClient;
@@ -114,14 +114,7 @@ export const SyncOperations: Record<
114
114
  mailboxCountTotal: account.mailboxCountTotal,
115
115
  mailboxCountSynced: account.mailboxCountSynced,
116
116
  lastSyncAt: account.lastSyncAt,
117
- mailboxes: mailboxes.map((mailbox) => ({
118
- mailboxId: mailbox.mailboxId,
119
- fullPath: mailbox.fullPath,
120
- phase: deriveMailboxPhase(mailbox),
121
- messagesTotal: mailbox.messageCount ?? 0,
122
- messagesSynced: computeMessagesSynced(mailbox),
123
- lastSyncedAt: mailbox.lastMessageSyncAt || undefined,
124
- })),
117
+ mailboxes: mailboxes.map(toMailboxSyncProgress),
125
118
  };
126
119
  },
127
120
  };