@remit/data-ports 0.0.59 → 0.0.60
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 +5 -1
- package/src/index.ts +2 -0
- package/src/message-settlement.test.ts +72 -0
- package/src/message-settlement.ts +48 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remit/data-ports",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.60",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -52,6 +52,10 @@
|
|
|
52
52
|
"./update-manifest": {
|
|
53
53
|
"types": "./src/update-manifest.ts",
|
|
54
54
|
"default": "./src/update-manifest.ts"
|
|
55
|
+
},
|
|
56
|
+
"./message-settlement": {
|
|
57
|
+
"types": "./src/message-settlement.ts",
|
|
58
|
+
"default": "./src/message-settlement.ts"
|
|
55
59
|
}
|
|
56
60
|
},
|
|
57
61
|
"scripts": {
|
package/src/index.ts
CHANGED
|
@@ -51,6 +51,8 @@ export type {
|
|
|
51
51
|
IUnitOfWork,
|
|
52
52
|
UnitOfWorkRepositories,
|
|
53
53
|
} from "./interfaces/unit-of-work.js";
|
|
54
|
+
export type { MessageSettlementFields } from "./message-settlement.js";
|
|
55
|
+
export { hasAbandonedDelete } from "./message-settlement.js";
|
|
54
56
|
export {
|
|
55
57
|
FILTER_NO_ACTION,
|
|
56
58
|
isSenderMuted,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { describe, test } from "node:test";
|
|
3
|
+
import { MessageStatus, MessageSyncStatus } from "@remit/domain-enums";
|
|
4
|
+
import { hasAbandonedDelete } from "./message-settlement.js";
|
|
5
|
+
|
|
6
|
+
describe("hasAbandonedDelete", () => {
|
|
7
|
+
test("the pair abandonDelete writes, its only writer", () => {
|
|
8
|
+
assert.equal(
|
|
9
|
+
hasAbandonedDelete({
|
|
10
|
+
status: MessageStatus.active,
|
|
11
|
+
syncStatus: MessageSyncStatus.failed,
|
|
12
|
+
}),
|
|
13
|
+
true,
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("a move mid-retry is not a give-up, whatever `failed` suggests", () => {
|
|
18
|
+
assert.equal(
|
|
19
|
+
hasAbandonedDelete({
|
|
20
|
+
status: MessageStatus.moving,
|
|
21
|
+
syncStatus: MessageSyncStatus.failed,
|
|
22
|
+
}),
|
|
23
|
+
false,
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("a delete mid-retry keeps `deleting`, so it is not a give-up", () => {
|
|
28
|
+
assert.equal(
|
|
29
|
+
hasAbandonedDelete({
|
|
30
|
+
status: MessageStatus.deleting,
|
|
31
|
+
syncStatus: MessageSyncStatus.failed,
|
|
32
|
+
}),
|
|
33
|
+
false,
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("a copy that gave up hides its row as `deleted` rather than claiming this", () => {
|
|
38
|
+
assert.equal(
|
|
39
|
+
hasAbandonedDelete({
|
|
40
|
+
status: MessageStatus.deleted,
|
|
41
|
+
syncStatus: MessageSyncStatus.failed,
|
|
42
|
+
}),
|
|
43
|
+
false,
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Both a settled move (`updateUid`) and a delete that exhausted its retries
|
|
49
|
+
* (#1143 repairs the row to where the message actually is) land on this pair.
|
|
50
|
+
* The second is a give-up this predicate deliberately cannot see — pinned so
|
|
51
|
+
* the gap is a decision on record, not an oversight.
|
|
52
|
+
*/
|
|
53
|
+
test("`active` + `synced` is never a give-up, however it was reached", () => {
|
|
54
|
+
assert.equal(
|
|
55
|
+
hasAbandonedDelete({
|
|
56
|
+
status: MessageStatus.active,
|
|
57
|
+
syncStatus: MessageSyncStatus.synced,
|
|
58
|
+
}),
|
|
59
|
+
false,
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("an ordinary inbound row, `pending` forever, says nothing", () => {
|
|
64
|
+
assert.equal(
|
|
65
|
+
hasAbandonedDelete({
|
|
66
|
+
status: MessageStatus.active,
|
|
67
|
+
syncStatus: MessageSyncStatus.pending,
|
|
68
|
+
}),
|
|
69
|
+
false,
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { MessageStatus, MessageSyncStatus } from "@remit/domain-enums";
|
|
2
|
+
import type { MessageItem } from "./types.js";
|
|
3
|
+
|
|
4
|
+
export type MessageSettlementFields = Pick<
|
|
5
|
+
MessageItem,
|
|
6
|
+
"status" | "syncStatus"
|
|
7
|
+
>;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A delete Remit refused to run, having already removed the message locally.
|
|
11
|
+
* `abandonDelete` reaches this from four checks, all of them made before any
|
|
12
|
+
* expunge: the Trash folder the event names is not on the server (TRYCREATE),
|
|
13
|
+
* the event carries no destination, it names an operation this build does not
|
|
14
|
+
* recognise, or it was minted under an unknown contract. The row is handed back
|
|
15
|
+
* to the folder the server still holds the message in.
|
|
16
|
+
*
|
|
17
|
+
* `status: active` alongside `syncStatus: failed` is the whole signal, and
|
|
18
|
+
* `abandonDelete` (`imap-worker/src/handlers/message-delete.ts`) is its ONLY
|
|
19
|
+
* writer. Every other writer of either value writes the other along with it:
|
|
20
|
+
*
|
|
21
|
+
* - `updateUid` (`drizzle-service/src/repos/message.ts`) settles a confirmed
|
|
22
|
+
* move by writing `active` and `synced` in the same statement, so a move that
|
|
23
|
+
* worked after a failed attempt cannot leave `failed` behind.
|
|
24
|
+
* - `empty-trash` and `message-copy`'s `settleCopied` both hand a row back as
|
|
25
|
+
* `active` + `synced`.
|
|
26
|
+
* - `upsertWithStatus` leaves an existing row alone, so no inbound sync writes
|
|
27
|
+
* either value onto a row that already has them.
|
|
28
|
+
*
|
|
29
|
+
* `syncStatus: failed` ON ITS OWN is NOT a give-up marker, whatever
|
|
30
|
+
* `placement-settled.ts`'s docstring says. `message-move.ts`, `message-delete.ts`
|
|
31
|
+
* and `message-copy.ts` each write it on an ORDINARY TRANSIENT attempt and then
|
|
32
|
+
* re-throw for queue redelivery — the row is mid-retry and about to succeed. In
|
|
33
|
+
* those handlers `status` stays at its in-flight value (`moving`, `deleting`),
|
|
34
|
+
* which is what separates them from the pair above.
|
|
35
|
+
*
|
|
36
|
+
* Three give-ups this cannot see, and must not pretend to:
|
|
37
|
+
*
|
|
38
|
+
* - A MOVE that gave up leaves `moving` + `failed`, the same pair a first
|
|
39
|
+
* dropped connection writes. Nothing persisted tells the two apart.
|
|
40
|
+
* - A DELETE that exhausted its retries settles `active` + `synced` (#1143's
|
|
41
|
+
* `resolveExhaustedMessageDeleteFailure` repairs the row to where the message
|
|
42
|
+
* actually is), so it reads as fully settled here.
|
|
43
|
+
* - `flag-push` and `placement-move-push` never write either field at all.
|
|
44
|
+
* Their give-up state lives on their own marker rows.
|
|
45
|
+
*/
|
|
46
|
+
export const hasAbandonedDelete = (message: MessageSettlementFields): boolean =>
|
|
47
|
+
message.status === MessageStatus.active &&
|
|
48
|
+
message.syncStatus === MessageSyncStatus.failed;
|