@remit/data-ports 0.0.10 → 0.0.11
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 +1 -1
- package/src/id.test.ts +38 -1
- package/src/id.ts +23 -0
package/package.json
CHANGED
package/src/id.test.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { describe, it } from "node:test";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
deriveCopyMessageId,
|
|
5
|
+
deriveMessageId,
|
|
6
|
+
deriveQuarantineId,
|
|
7
|
+
quarantineMessageIdHash,
|
|
8
|
+
} from "./id.js";
|
|
4
9
|
|
|
5
10
|
const ACCOUNT = "acct-1";
|
|
6
11
|
const MAILBOX = "mbx-1";
|
|
@@ -30,6 +35,38 @@ describe("deriveQuarantineId", () => {
|
|
|
30
35
|
});
|
|
31
36
|
});
|
|
32
37
|
|
|
38
|
+
describe("deriveCopyMessageId", () => {
|
|
39
|
+
const SOURCE = deriveMessageId(ACCOUNT, "<abc@example.com>");
|
|
40
|
+
const DEST = "mbx-archive";
|
|
41
|
+
|
|
42
|
+
it("is stable for the same source and destination (idempotent copy)", () => {
|
|
43
|
+
assert.equal(
|
|
44
|
+
deriveCopyMessageId(SOURCE, DEST),
|
|
45
|
+
deriveCopyMessageId(SOURCE, DEST),
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("separates a copy of one mail into two folders", () => {
|
|
50
|
+
assert.notEqual(
|
|
51
|
+
deriveCopyMessageId(SOURCE, DEST),
|
|
52
|
+
deriveCopyMessageId(SOURCE, "mbx-other"),
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("separates copies of two different mails into one folder", () => {
|
|
57
|
+
assert.notEqual(
|
|
58
|
+
deriveCopyMessageId(SOURCE, DEST),
|
|
59
|
+
deriveCopyMessageId(deriveMessageId(ACCOUNT, "<xyz@example.com>"), DEST),
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("never equals the folder-independent id sync derives", () => {
|
|
64
|
+
// Sync keys a message on deriveMessageId; the copy id folds in the
|
|
65
|
+
// destination mailbox, so the two can never collide.
|
|
66
|
+
assert.notEqual(deriveCopyMessageId(SOURCE, DEST), SOURCE);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
33
70
|
describe("quarantineMessageIdHash", () => {
|
|
34
71
|
it("hashes a real Message-ID to a pinned sha256 value", () => {
|
|
35
72
|
const hash = quarantineMessageIdHash("<abc@example.com>");
|
package/src/id.ts
CHANGED
|
@@ -73,6 +73,29 @@ export const deriveMessageIdFromSource = (
|
|
|
73
73
|
source: MessageIdSource,
|
|
74
74
|
): string => deriveMessageId(accountId, normalizeMessageIdHeader(source));
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Identity of a message copied into another folder (issue #75). Derived from
|
|
78
|
+
* the source message and the destination mailbox so a copy is deterministic:
|
|
79
|
+
* copying the same mail into the same folder twice — a replayed COPY event or a
|
|
80
|
+
* repeated user copy — resolves to one row instead of a fresh unreachable
|
|
81
|
+
* duplicate each time. A random id, which is what the copy path used before,
|
|
82
|
+
* gave every attempt a new identity that no later sync or delete could reach.
|
|
83
|
+
*
|
|
84
|
+
* The destination mailbox is part of the name so a copy of one mail into two
|
|
85
|
+
* folders is two rows, one per folder. It also keeps this id out of the space
|
|
86
|
+
* sync assigns: sync keys a message on `deriveMessageId(accountId,
|
|
87
|
+
* messageIdHeader)`, which is folder-independent, so the copy row can never
|
|
88
|
+
* collide with, or be overwritten by, the canonical synced row.
|
|
89
|
+
*/
|
|
90
|
+
export const deriveCopyMessageId = (
|
|
91
|
+
sourceMessageId: string,
|
|
92
|
+
destinationMailboxId: string,
|
|
93
|
+
): string =>
|
|
94
|
+
base36uuidv5(
|
|
95
|
+
`messagecopy:${sourceMessageId}:${destinationMailboxId}`,
|
|
96
|
+
REMIT_NAMESPACE,
|
|
97
|
+
);
|
|
98
|
+
|
|
76
99
|
export const deriveThreadId = (
|
|
77
100
|
accountId: string,
|
|
78
101
|
rootMessageIdHeader: string,
|