@rapidmx/activesync-plugin 1.0.0-beta.2 → 1.0.0-beta.3
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/README.md +4 -4
- package/dist/lib/BaseDeviceSyncStateRoute.js +32 -0
- package/dist/lib/BaseDeviceSyncStateRoute.js.map +1 -1
- package/dist/lib/BaseEasRoute.js +91 -15
- package/dist/lib/BaseEasRoute.js.map +1 -1
- package/dist/lib/EasAuditLog.js +56 -0
- package/dist/lib/EasAuditLog.js.map +1 -0
- package/dist/lib/EasCollectionLease.js +199 -0
- package/dist/lib/EasCollectionLease.js.map +1 -0
- package/dist/lib/EasCollectionStore.js +102 -0
- package/dist/lib/EasCollectionStore.js.map +1 -0
- package/dist/lib/EasCollectionSync.js +294 -0
- package/dist/lib/EasCollectionSync.js.map +1 -0
- package/dist/lib/EasSyncKeyUtils.js +125 -62
- package/dist/lib/EasSyncKeyUtils.js.map +1 -1
- package/dist/lib/MessageMoveRules.js +100 -0
- package/dist/lib/MessageMoveRules.js.map +1 -0
- package/dist/lib/MimeHeaderUtils.js +395 -0
- package/dist/lib/MimeHeaderUtils.js.map +1 -0
- package/dist/lib/RegexPatternUtils.js +30 -0
- package/dist/lib/RegexPatternUtils.js.map +1 -0
- package/dist/lib/RestapiCompat.js +41 -0
- package/dist/lib/RestapiCompat.js.map +1 -0
- package/dist/lib/adapters/CalendarSyncAdapter.js +142 -33
- package/dist/lib/adapters/CalendarSyncAdapter.js.map +1 -1
- package/dist/lib/adapters/EmailSyncAdapter.js +114 -27
- package/dist/lib/adapters/EmailSyncAdapter.js.map +1 -1
- package/dist/lib/adapters/mongo/EmailSyncAdapterMongo.js +2 -1
- package/dist/lib/adapters/mongo/EmailSyncAdapterMongo.js.map +1 -1
- package/dist/lib/adapters/sql/EmailSyncAdapterSQL.js +2 -1
- package/dist/lib/adapters/sql/EmailSyncAdapterSQL.js.map +1 -1
- package/dist/lib/codec/WbxmlDecoder.js +86 -34
- package/dist/lib/codec/WbxmlDecoder.js.map +1 -1
- package/dist/lib/codec/WbxmlEncoder.js +103 -22
- package/dist/lib/codec/WbxmlEncoder.js.map +1 -1
- package/dist/lib/commands/ComposeMailCommand.js +105 -31
- package/dist/lib/commands/ComposeMailCommand.js.map +1 -1
- package/dist/lib/commands/FolderSyncCommand.js +67 -27
- package/dist/lib/commands/FolderSyncCommand.js.map +1 -1
- package/dist/lib/commands/GetItemEstimateCommand.js +80 -59
- package/dist/lib/commands/GetItemEstimateCommand.js.map +1 -1
- package/dist/lib/commands/ItemOperationsCommand.js +219 -56
- package/dist/lib/commands/ItemOperationsCommand.js.map +1 -1
- package/dist/lib/commands/MeetingResponseCommand.js +212 -53
- package/dist/lib/commands/MeetingResponseCommand.js.map +1 -1
- package/dist/lib/commands/MoveItemsCommand.js +54 -21
- package/dist/lib/commands/MoveItemsCommand.js.map +1 -1
- package/dist/lib/commands/PingCommand.js +288 -56
- package/dist/lib/commands/PingCommand.js.map +1 -1
- package/dist/lib/commands/ProvisionCommand.js +34 -17
- package/dist/lib/commands/ProvisionCommand.js.map +1 -1
- package/dist/lib/commands/ResolveRecipientsCommand.js +43 -5
- package/dist/lib/commands/ResolveRecipientsCommand.js.map +1 -1
- package/dist/lib/commands/SearchCommand.js +92 -17
- package/dist/lib/commands/SearchCommand.js.map +1 -1
- package/dist/lib/commands/SettingsCommand.js +2 -1
- package/dist/lib/commands/SettingsCommand.js.map +1 -1
- package/dist/lib/commands/SmartForwardCommand.js +2 -1
- package/dist/lib/commands/SmartForwardCommand.js.map +1 -1
- package/dist/lib/commands/SmartReplyCommand.js +2 -1
- package/dist/lib/commands/SmartReplyCommand.js.map +1 -1
- package/dist/lib/commands/SyncCommand.js +481 -244
- package/dist/lib/commands/SyncCommand.js.map +1 -1
- package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js +6 -1
- package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js.map +1 -1
- package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js +3 -1
- package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js.map +1 -1
- package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js +2 -1
- package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js.map +1 -1
- package/dist/lib/commands/mongo/PingCommandMongo.js +23 -0
- package/dist/lib/commands/mongo/PingCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/SearchCommandMongo.js +3 -1
- package/dist/lib/commands/mongo/SearchCommandMongo.js.map +1 -1
- package/dist/lib/commands/mongo/SendMailCommandMongo.js +2 -1
- package/dist/lib/commands/mongo/SendMailCommandMongo.js.map +1 -1
- package/dist/lib/commands/mongo/SmartForwardCommandMongo.js +2 -1
- package/dist/lib/commands/mongo/SmartForwardCommandMongo.js.map +1 -1
- package/dist/lib/commands/mongo/SmartReplyCommandMongo.js +2 -1
- package/dist/lib/commands/mongo/SmartReplyCommandMongo.js.map +1 -1
- package/dist/lib/commands/mongo/SyncCommandMongo.js +7 -1
- package/dist/lib/commands/mongo/SyncCommandMongo.js.map +1 -1
- package/dist/lib/commands/mongo/index.js +1 -0
- package/dist/lib/commands/mongo/index.js.map +1 -1
- package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js +6 -1
- package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js.map +1 -1
- package/dist/lib/commands/sql/ItemOperationsCommandSQL.js +3 -1
- package/dist/lib/commands/sql/ItemOperationsCommandSQL.js.map +1 -1
- package/dist/lib/commands/sql/MeetingResponseCommandSQL.js +2 -1
- package/dist/lib/commands/sql/MeetingResponseCommandSQL.js.map +1 -1
- package/dist/lib/commands/sql/PingCommandSQL.js +23 -0
- package/dist/lib/commands/sql/PingCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/SearchCommandSQL.js +3 -1
- package/dist/lib/commands/sql/SearchCommandSQL.js.map +1 -1
- package/dist/lib/commands/sql/SendMailCommandSQL.js +2 -1
- package/dist/lib/commands/sql/SendMailCommandSQL.js.map +1 -1
- package/dist/lib/commands/sql/SmartForwardCommandSQL.js +2 -1
- package/dist/lib/commands/sql/SmartForwardCommandSQL.js.map +1 -1
- package/dist/lib/commands/sql/SmartReplyCommandSQL.js +2 -1
- package/dist/lib/commands/sql/SmartReplyCommandSQL.js.map +1 -1
- package/dist/lib/commands/sql/SyncCommandSQL.js +7 -1
- package/dist/lib/commands/sql/SyncCommandSQL.js.map +1 -1
- package/dist/lib/commands/sql/index.js +1 -0
- package/dist/lib/commands/sql/index.js.map +1 -1
- package/dist/lib/index.js +5 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/jobs/EasDeviceStateCleanupJob.js +44 -2
- package/dist/lib/jobs/EasDeviceStateCleanupJob.js.map +1 -1
- package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js +4 -0
- package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js.map +1 -1
- package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js +9 -0
- package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js.map +1 -1
- package/dist/lib/models/EasCollectionChunk.js +2 -0
- package/dist/lib/models/EasCollectionChunk.js.map +1 -0
- package/dist/lib/models/EasCollectionState.js +2 -0
- package/dist/lib/models/EasCollectionState.js.map +1 -0
- package/dist/lib/models/mongo/DeviceSyncStateMongo.js +7 -0
- package/dist/lib/models/mongo/DeviceSyncStateMongo.js.map +1 -1
- package/dist/lib/models/mongo/EasCollectionChunkMongo.js +83 -0
- package/dist/lib/models/mongo/EasCollectionChunkMongo.js.map +1 -0
- package/dist/lib/models/mongo/EasCollectionStateMongo.js +162 -0
- package/dist/lib/models/mongo/EasCollectionStateMongo.js.map +1 -0
- package/dist/lib/models/sql/DeviceSyncStateSQL.js +7 -0
- package/dist/lib/models/sql/DeviceSyncStateSQL.js.map +1 -1
- package/dist/lib/models/sql/EasCollectionChunkSQL.js +83 -0
- package/dist/lib/models/sql/EasCollectionChunkSQL.js.map +1 -0
- package/dist/lib/models/sql/EasCollectionStateSQL.js +162 -0
- package/dist/lib/models/sql/EasCollectionStateSQL.js.map +1 -0
- package/dist/lib/mongo/EasRouteMongo.js +2 -2
- package/dist/lib/mongo/EasRouteMongo.js.map +1 -1
- package/dist/lib/mongo.js +2 -0
- package/dist/lib/mongo.js.map +1 -1
- package/dist/lib/sql/EasRouteSQL.js +2 -2
- package/dist/lib/sql/EasRouteSQL.js.map +1 -1
- package/dist/lib/sql.js +2 -0
- package/dist/lib/sql.js.map +1 -1
- package/dist/types/BaseDeviceSyncStateRoute.d.ts +10 -0
- package/dist/types/BaseEasRoute.d.ts +15 -4
- package/dist/types/EasAuditLog.d.ts +47 -0
- package/dist/types/EasCollectionLease.d.ts +60 -0
- package/dist/types/EasCollectionStore.d.ts +51 -0
- package/dist/types/EasCollectionSync.d.ts +96 -0
- package/dist/types/EasCommandHandler.d.ts +7 -4
- package/dist/types/EasSyncKeyUtils.d.ts +73 -42
- package/dist/types/MessageMoveRules.d.ts +45 -0
- package/dist/types/MimeHeaderUtils.d.ts +73 -0
- package/dist/types/RegexPatternUtils.d.ts +14 -0
- package/dist/types/RestapiCompat.d.ts +24 -0
- package/dist/types/adapters/CalendarSyncAdapter.d.ts +47 -18
- package/dist/types/adapters/EasCollectionSyncAdapter.d.ts +18 -2
- package/dist/types/adapters/EmailSyncAdapter.d.ts +24 -7
- package/dist/types/adapters/mongo/EmailSyncAdapterMongo.d.ts +1 -0
- package/dist/types/adapters/sql/EmailSyncAdapterSQL.d.ts +1 -0
- package/dist/types/codec/WbxmlDecoder.d.ts +39 -0
- package/dist/types/codec/WbxmlEncoder.d.ts +36 -1
- package/dist/types/commands/ComposeMailCommand.d.ts +33 -15
- package/dist/types/commands/FolderSyncCommand.d.ts +4 -1
- package/dist/types/commands/GetItemEstimateCommand.d.ts +20 -15
- package/dist/types/commands/ItemOperationsCommand.d.ts +29 -4
- package/dist/types/commands/MeetingResponseCommand.d.ts +52 -13
- package/dist/types/commands/MoveItemsCommand.d.ts +13 -3
- package/dist/types/commands/PingCommand.d.ts +72 -14
- package/dist/types/commands/ProvisionCommand.d.ts +16 -11
- package/dist/types/commands/ResolveRecipientsCommand.d.ts +4 -0
- package/dist/types/commands/SearchCommand.d.ts +13 -1
- package/dist/types/commands/SyncCommand.d.ts +102 -62
- package/dist/types/commands/mongo/GetItemEstimateCommandMongo.d.ts +3 -0
- package/dist/types/commands/mongo/ItemOperationsCommandMongo.d.ts +2 -0
- package/dist/types/commands/mongo/MeetingResponseCommandMongo.d.ts +1 -0
- package/dist/types/commands/mongo/PingCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/SearchCommandMongo.d.ts +2 -0
- package/dist/types/commands/mongo/SendMailCommandMongo.d.ts +1 -0
- package/dist/types/commands/mongo/SmartForwardCommandMongo.d.ts +1 -0
- package/dist/types/commands/mongo/SmartReplyCommandMongo.d.ts +1 -0
- package/dist/types/commands/mongo/SyncCommandMongo.d.ts +4 -0
- package/dist/types/commands/mongo/index.d.ts +1 -0
- package/dist/types/commands/sql/GetItemEstimateCommandSQL.d.ts +3 -0
- package/dist/types/commands/sql/ItemOperationsCommandSQL.d.ts +2 -0
- package/dist/types/commands/sql/MeetingResponseCommandSQL.d.ts +1 -0
- package/dist/types/commands/sql/PingCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/SearchCommandSQL.d.ts +2 -0
- package/dist/types/commands/sql/SendMailCommandSQL.d.ts +1 -0
- package/dist/types/commands/sql/SmartForwardCommandSQL.d.ts +1 -0
- package/dist/types/commands/sql/SmartReplyCommandSQL.d.ts +1 -0
- package/dist/types/commands/sql/SyncCommandSQL.d.ts +4 -0
- package/dist/types/commands/sql/index.d.ts +1 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/jobs/EasDeviceStateCleanupJob.d.ts +15 -0
- package/dist/types/jobs/mongo/EasDeviceStateCleanupJobMongo.d.ts +2 -0
- package/dist/types/jobs/sql/EasDeviceStateCleanupJobSQL.d.ts +4 -0
- package/dist/types/models/DeviceSyncState.d.ts +13 -2
- package/dist/types/models/EasCollectionChunk.d.ts +20 -0
- package/dist/types/models/EasCollectionState.d.ts +79 -0
- package/dist/types/models/mongo/DeviceSyncStateMongo.d.ts +1 -0
- package/dist/types/models/mongo/EasCollectionChunkMongo.d.ts +16 -0
- package/dist/types/models/mongo/EasCollectionStateMongo.d.ts +27 -0
- package/dist/types/models/sql/DeviceSyncStateSQL.d.ts +1 -0
- package/dist/types/models/sql/EasCollectionChunkSQL.d.ts +16 -0
- package/dist/types/models/sql/EasCollectionStateSQL.d.ts +27 -0
- package/dist/types/mongo.d.ts +2 -0
- package/dist/types/sql.d.ts +2 -0
- package/package.json +267 -238
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { asEntity } from "./RestapiCompat.js";
|
|
2
|
+
/** Largest held set kept inline on the `EasCollectionState` row (`serverIds`) - past it the set moves to chunk rows. */
|
|
3
|
+
export const INLINE_HELD_LIMIT = 2000;
|
|
4
|
+
/** Most `ServerId`s one `EasCollectionChunk` row holds. */
|
|
5
|
+
export const HELD_CHUNK_SIZE = 2000;
|
|
6
|
+
/** Chunk rows read per query while loading a held set. */
|
|
7
|
+
const CHUNK_PAGE_SIZE = 100;
|
|
8
|
+
/**
|
|
9
|
+
* Loads the `ServerId`s a device holds for a collection: `state.serverIds` while the set is inline, otherwise every
|
|
10
|
+
* `EasCollectionChunk` row of the collection (read in pages, ordered by `chunkIndex`).
|
|
11
|
+
*/
|
|
12
|
+
export async function loadHeldSet(state, store) {
|
|
13
|
+
if (!state?.chunked) {
|
|
14
|
+
return { ids: new Set(state?.serverIds ?? []), chunks: [] };
|
|
15
|
+
}
|
|
16
|
+
const chunks = [];
|
|
17
|
+
let after = -1;
|
|
18
|
+
for (;;) {
|
|
19
|
+
const page = await store.repo.find({
|
|
20
|
+
mailboxUid: state.mailboxUid,
|
|
21
|
+
deviceId: state.deviceId,
|
|
22
|
+
folderUid: state.folderUid,
|
|
23
|
+
chunkIndex: `gt(${after})`,
|
|
24
|
+
sort: JSON.stringify({ chunkIndex: "ASC" }),
|
|
25
|
+
limit: CHUNK_PAGE_SIZE,
|
|
26
|
+
}, { ignoreACL: true, limit: CHUNK_PAGE_SIZE });
|
|
27
|
+
chunks.push(...page);
|
|
28
|
+
if (page.length < CHUNK_PAGE_SIZE) {
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
after = page[page.length - 1].chunkIndex;
|
|
32
|
+
}
|
|
33
|
+
const ids = new Set();
|
|
34
|
+
for (const chunk of chunks) {
|
|
35
|
+
for (const id of chunk.ids) {
|
|
36
|
+
ids.add(id);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { ids, chunks };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Persists `ids` as the collection's new held set, given the `loaded` set the round started from, and returns the
|
|
43
|
+
* `serverIds`/`chunked` values the state row must be saved with.
|
|
44
|
+
*
|
|
45
|
+
* A set within `INLINE_HELD_LIMIT` that was never chunked stays inline (no chunk rows touched). Otherwise only the
|
|
46
|
+
* difference is written: each existing chunk drops the ids no longer held, new ids fill the free space of existing
|
|
47
|
+
* chunks before new chunks are appended, and only chunks whose content changed are updated (an emptied chunk is
|
|
48
|
+
* deleted) - a round that adds a window of items rewrites one or two chunks, never the whole set.
|
|
49
|
+
*
|
|
50
|
+
* Writes are not atomic with the state row: the caller must treat a failure here (or of the state row after this)
|
|
51
|
+
* as a lost round and make the device restart the collection.
|
|
52
|
+
*/
|
|
53
|
+
export async function saveHeldSet(key, loaded, ids, wasChunked, store) {
|
|
54
|
+
if (!wasChunked && ids.size <= INLINE_HELD_LIMIT) {
|
|
55
|
+
return { serverIds: [...ids], chunked: false };
|
|
56
|
+
}
|
|
57
|
+
const placed = new Set();
|
|
58
|
+
const kept = loaded.chunks.map((chunk) => chunk.ids.filter((id) => {
|
|
59
|
+
if (!ids.has(id) || placed.has(id)) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
placed.add(id);
|
|
63
|
+
return true;
|
|
64
|
+
}));
|
|
65
|
+
const pending = [...ids].filter((id) => !placed.has(id));
|
|
66
|
+
let next = 0;
|
|
67
|
+
for (const chunkIds of kept) {
|
|
68
|
+
while (chunkIds.length < HELD_CHUNK_SIZE && next < pending.length) {
|
|
69
|
+
chunkIds.push(pending[next++]);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
for (let i = 0; i < loaded.chunks.length; i++) {
|
|
73
|
+
const chunk = loaded.chunks[i];
|
|
74
|
+
const chunkIds = kept[i];
|
|
75
|
+
const unchanged = chunkIds.length === chunk.ids.length && chunkIds.every((id, j) => id === chunk.ids[j]);
|
|
76
|
+
if (unchanged) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (chunkIds.length === 0) {
|
|
80
|
+
await store.repo.delete(chunk.uid, { ignoreACL: true, purge: true, skipPush: true });
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
await store.repo.update({ uid: chunk.uid, version: chunk.version, ids: chunkIds }, asEntity(store.repo, chunk), {
|
|
84
|
+
ignoreACL: true,
|
|
85
|
+
skipPush: true,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
let chunkIndex = loaded.chunks.reduce((max, chunk) => Math.max(max, chunk.chunkIndex), -1);
|
|
90
|
+
while (next < pending.length) {
|
|
91
|
+
const chunkIds = pending.slice(next, next + HELD_CHUNK_SIZE);
|
|
92
|
+
next += chunkIds.length;
|
|
93
|
+
chunkIndex++;
|
|
94
|
+
await store.repo.create(new store.chunkClass({ mailboxUid: key.mailboxUid, deviceId: key.deviceId, folderUid: key.folderUid, chunkIndex, ids: chunkIds }), { ignoreACL: true, skipPush: true });
|
|
95
|
+
}
|
|
96
|
+
return { serverIds: [], chunked: true };
|
|
97
|
+
}
|
|
98
|
+
/** Removes every chunk row of a collection (a restart with `SyncKey 0`, or a forgotten device). */
|
|
99
|
+
export async function clearHeldSet(key, store) {
|
|
100
|
+
await store.repo.truncate({ mailboxUid: key.mailboxUid, deviceId: key.deviceId, folderUid: key.folderUid }, { ignoreACL: true });
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=EasCollectionStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EasCollectionStore.js","sourceRoot":"","sources":["../../src/EasCollectionStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { FolderType } from "@rapidmx/restapi";
|
|
2
|
+
import { compareCursor, cursorOf, isListableUid, parseSyncKey, scanAfter, scanOverlap } from "./EasSyncKeyUtils.js";
|
|
3
|
+
/** How far before a round's start the out-of-folder cursor is fast-forwarded while the device holds nothing -
|
|
4
|
+
* generous slack for clock differences between the servers stamping `dateModified`. */
|
|
5
|
+
const MOVE_CURSOR_SLACK_MS = 60000;
|
|
6
|
+
/** How far behind each cursor a round re-reads the stream for rows committed out of order by another replica. */
|
|
7
|
+
export const DEFAULT_OVERLAP_MS = 5000;
|
|
8
|
+
/** Most rows one overlap re-read returns, and most entries `recent` keeps. */
|
|
9
|
+
export const DEFAULT_OVERLAP_LIMIT = 1000;
|
|
10
|
+
function mapOf(record) {
|
|
11
|
+
return record ? new Map(Object.entries(record)) : undefined;
|
|
12
|
+
}
|
|
13
|
+
/** Loads the working state for the round a client's current `SyncKey` (`state.syncKey`) continues. `held` is the
|
|
14
|
+
* collection's held set (see `EasCollectionStore.loadHeldSet`); defaults to the inline `serverIds`. */
|
|
15
|
+
export function workingStateFromRow(state, held = new Set(state.serverIds)) {
|
|
16
|
+
return {
|
|
17
|
+
generation: parseSyncKey(state.syncKey)?.generation ?? 0,
|
|
18
|
+
cursor: { date: new Date(state.cursorDate), uid: state.cursorUid },
|
|
19
|
+
moveCursor: { date: new Date(state.moveCursorDate), uid: state.moveCursorUid },
|
|
20
|
+
serverIds: new Set(held),
|
|
21
|
+
echoes: new Map(Object.entries(state.echoes ?? {})),
|
|
22
|
+
recent: mapOf(state.recent),
|
|
23
|
+
reconcileCursor: state.reconcileCursor ?? "",
|
|
24
|
+
filterType: state.filterType ?? undefined,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Rebuilds the working state as it stood before the most recent round, for a client retrying `round.syncKey`. */
|
|
28
|
+
export function workingStateFromRound(state, round, held = new Set(state.serverIds)) {
|
|
29
|
+
const serverIds = new Set(held);
|
|
30
|
+
for (const uid of round.addedIds) {
|
|
31
|
+
serverIds.delete(uid);
|
|
32
|
+
}
|
|
33
|
+
for (const uid of round.removedIds) {
|
|
34
|
+
serverIds.add(uid);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
generation: parseSyncKey(round.syncKey)?.generation ?? 0,
|
|
38
|
+
cursor: { date: new Date(round.cursorDate), uid: round.cursorUid },
|
|
39
|
+
moveCursor: { date: new Date(round.moveCursorDate), uid: round.moveCursorUid },
|
|
40
|
+
serverIds,
|
|
41
|
+
echoes: new Map(Object.entries(round.echoes)),
|
|
42
|
+
recent: mapOf(round.recent),
|
|
43
|
+
reconcileCursor: round.reconcileCursor ?? "",
|
|
44
|
+
filterType: state.filterType ?? undefined,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** Deep-copies a working state so a dry run (`GetItemEstimate`) can't disturb the original. */
|
|
48
|
+
export function cloneWorkingState(state) {
|
|
49
|
+
return {
|
|
50
|
+
...state,
|
|
51
|
+
cursor: { ...state.cursor },
|
|
52
|
+
moveCursor: { ...state.moveCursor },
|
|
53
|
+
serverIds: new Set(state.serverIds),
|
|
54
|
+
echoes: new Map(state.echoes),
|
|
55
|
+
recent: state.recent ? new Map(state.recent) : undefined,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** Records the round that turned `base` into `result` (see `EasCollectionRound`). */
|
|
59
|
+
export function roundRecord(syncKey, base, result, clientIds) {
|
|
60
|
+
return {
|
|
61
|
+
syncKey,
|
|
62
|
+
cursorDate: base.cursor.date.toISOString(),
|
|
63
|
+
cursorUid: base.cursor.uid,
|
|
64
|
+
moveCursorDate: base.moveCursor.date.toISOString(),
|
|
65
|
+
moveCursorUid: base.moveCursor.uid,
|
|
66
|
+
addedIds: [...result.serverIds].filter((uid) => !base.serverIds.has(uid)),
|
|
67
|
+
removedIds: [...base.serverIds].filter((uid) => !result.serverIds.has(uid)),
|
|
68
|
+
echoes: Object.fromEntries(base.echoes),
|
|
69
|
+
...(base.recent ? { recent: Object.fromEntries(base.recent) } : {}),
|
|
70
|
+
reconcileCursor: base.reconcileCursor,
|
|
71
|
+
clientIds: [...clientIds].map(([clientId, serverId]) => ({ clientId, serverId })),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Computes one round of server-side changes for a `Sync` collection and advances `state` past them:
|
|
76
|
+
*
|
|
77
|
+
* - **Out-of-folder stream** (rows of the same mailbox in any other folder after `state.moveCursor`, live or
|
|
78
|
+
* deleted): an item the device holds has been moved out of this folder and is a `Delete`. Read before the folder
|
|
79
|
+
* rows are processed, so when an item moves between the two reads the out-of-folder row (the newer one) wins.
|
|
80
|
+
* Skipped while the device holds nothing; the cursor is then fast-forwarded to just before the round started
|
|
81
|
+
* instead, so the first real scan doesn't have to crawl the whole mailbox history.
|
|
82
|
+
* - **Folder stream** (rows of this folder after `state.cursor`): a live item the device holds is a `Change`, one
|
|
83
|
+
* it doesn't hold is an `Add` (subject to `include`), a soft-deleted item it holds is a `Delete`; a row whose
|
|
84
|
+
* `dateModified` still equals the device's own recorded write (`state.echoes`) is skipped.
|
|
85
|
+
* - **Overlap**: `dateModified` is stamped before a write commits, so with several replicas a row can become visible
|
|
86
|
+
* after a cursor has already passed its timestamp. Both streams therefore also re-read the last `overlapMs` behind
|
|
87
|
+
* their cursor. Out-of-folder rows are idempotent (an item is only deleted while held), so they're simply
|
|
88
|
+
* re-applied; folder rows are deduplicated against `state.recent` (uid -> the `dateModified` it was processed at),
|
|
89
|
+
* and only a row not recorded there at that timestamp - one that became visible late - is processed.
|
|
90
|
+
* - **Reconcile**: a hard-purged row leaves no trace in either stream. Once a round has caught up (nothing more
|
|
91
|
+
* available), up to `reconcileLimit` held ids after `state.reconcileCursor` are looked up in the folder, and each
|
|
92
|
+
* one that no longer exists there is a `Delete`; the cursor wraps around after the last held id.
|
|
93
|
+
*
|
|
94
|
+
* Both cursors only ever advance past rows actually processed - never to "now" - so nothing is skipped when the
|
|
95
|
+
* window fills up (`moreAvailable`).
|
|
96
|
+
*/
|
|
97
|
+
export async function enumerateCollection(state, options) {
|
|
98
|
+
const startedAt = (options.now ?? new Date()).getTime();
|
|
99
|
+
const overlapMs = options.overlapMs ?? DEFAULT_OVERLAP_MS;
|
|
100
|
+
const overlapLimit = options.overlapLimit ?? DEFAULT_OVERLAP_LIMIT;
|
|
101
|
+
const folderCriteria = { folderUid: options.folderUid };
|
|
102
|
+
const moveCriteria = { mailboxUid: options.folderMailboxUid, folderUid: `ne(${options.folderUid})` };
|
|
103
|
+
const folderLate = await scanOverlap(options.repo, folderCriteria, state.cursor, overlapMs, overlapLimit);
|
|
104
|
+
const folderPage = await scanAfter(options.repo, folderCriteria, state.cursor, options.windowSize);
|
|
105
|
+
const holdsItems = state.serverIds.size > 0;
|
|
106
|
+
const moveLate = holdsItems ? await scanOverlap(options.repo, moveCriteria, state.moveCursor, overlapMs, overlapLimit) : [];
|
|
107
|
+
const movePage = holdsItems ? await scanAfter(options.repo, moveCriteria, state.moveCursor, options.moveScanLimit) : undefined;
|
|
108
|
+
const commands = [];
|
|
109
|
+
let budget = options.windowSize;
|
|
110
|
+
let moreAvailable = folderPage.more || !!movePage?.more;
|
|
111
|
+
// A row saved before overlap tracking existed has no record of what its window already reported: everything
|
|
112
|
+
// currently visible there is taken as reported, rather than re-sent.
|
|
113
|
+
const recent = state.recent ?? new Map(folderLate.map((row) => [row.uid, new Date(row.dateModified).toISOString()]));
|
|
114
|
+
const movedOut = new Set();
|
|
115
|
+
/** Processes one out-of-folder row; `false` when the window is full and the row was left for a later round. */
|
|
116
|
+
const processMoved = (row) => {
|
|
117
|
+
if (state.serverIds.has(row.uid)) {
|
|
118
|
+
if (budget === 0) {
|
|
119
|
+
moreAvailable = true;
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
commands.push({ kind: "Delete", uid: row.uid });
|
|
123
|
+
state.serverIds.delete(row.uid);
|
|
124
|
+
budget--;
|
|
125
|
+
}
|
|
126
|
+
movedOut.add(row.uid);
|
|
127
|
+
return true;
|
|
128
|
+
};
|
|
129
|
+
if (movePage) {
|
|
130
|
+
for (const row of moveLate) {
|
|
131
|
+
if (!processMoved(row)) {
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
for (const row of movePage.rows) {
|
|
136
|
+
if (!processMoved(row)) {
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
state.moveCursor = cursorOf(row);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
const floor = { date: new Date(startedAt - MOVE_CURSOR_SLACK_MS), uid: "" };
|
|
144
|
+
if (compareCursor(floor, state.moveCursor) > 0) {
|
|
145
|
+
state.moveCursor = floor;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/** Processes one folder-stream row; `false` when the window is full and the row was left for a later round. */
|
|
149
|
+
const processFolderRow = (row, iso) => {
|
|
150
|
+
const uid = row.uid;
|
|
151
|
+
const echo = state.echoes.get(uid);
|
|
152
|
+
let command;
|
|
153
|
+
if (movedOut.has(uid) || (echo !== undefined && echo === iso)) {
|
|
154
|
+
command = undefined;
|
|
155
|
+
}
|
|
156
|
+
else if (row.deleted === true) {
|
|
157
|
+
command = state.serverIds.has(uid) ? { kind: "Delete", uid } : undefined;
|
|
158
|
+
}
|
|
159
|
+
else if (state.serverIds.has(uid)) {
|
|
160
|
+
command = { kind: "Change", item: row };
|
|
161
|
+
}
|
|
162
|
+
else if (!options.include || options.include(row)) {
|
|
163
|
+
command = { kind: "Add", item: row };
|
|
164
|
+
}
|
|
165
|
+
if (command) {
|
|
166
|
+
if (budget === 0) {
|
|
167
|
+
moreAvailable = true;
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
commands.push(command);
|
|
171
|
+
if (command.kind === "Delete") {
|
|
172
|
+
state.serverIds.delete(uid);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
state.serverIds.add(uid);
|
|
176
|
+
}
|
|
177
|
+
budget--;
|
|
178
|
+
}
|
|
179
|
+
recent.set(uid, iso);
|
|
180
|
+
return true;
|
|
181
|
+
};
|
|
182
|
+
for (const row of folderLate) {
|
|
183
|
+
const iso = new Date(row.dateModified).toISOString();
|
|
184
|
+
if (recent.get(row.uid) !== iso && !processFolderRow(row, iso)) {
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
for (const row of folderPage.rows) {
|
|
189
|
+
if (!processFolderRow(row, new Date(row.dateModified).toISOString())) {
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
state.cursor = cursorOf(row);
|
|
193
|
+
}
|
|
194
|
+
for (const [uid, iso] of state.echoes) {
|
|
195
|
+
if (compareCursor({ date: new Date(iso), uid }, state.cursor) <= 0) {
|
|
196
|
+
state.echoes.delete(uid);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
state.recent = pruneRecent(recent, state.cursor, overlapMs, overlapLimit);
|
|
200
|
+
if (!moreAvailable && budget > 0 && (options.reconcileLimit ?? 0) > 0 && state.serverIds.size > 0) {
|
|
201
|
+
moreAvailable = await reconcile(state, options, commands, budget);
|
|
202
|
+
}
|
|
203
|
+
return { commands, moreAvailable };
|
|
204
|
+
}
|
|
205
|
+
/** Keeps only the `recent` entries still inside the overlap window behind `cursor`, at most `limit` (the newest). */
|
|
206
|
+
function pruneRecent(recent, cursor, overlapMs, limit) {
|
|
207
|
+
const floor = cursor.date.getTime() - overlapMs;
|
|
208
|
+
const kept = [...recent].filter(([, iso]) => new Date(iso).getTime() >= floor);
|
|
209
|
+
kept.sort((a, b) => (a[1] < b[1] ? 1 : a[1] > b[1] ? -1 : 0));
|
|
210
|
+
return new Map(kept.slice(0, limit));
|
|
211
|
+
}
|
|
212
|
+
/** One reconcile slice (see `enumerateCollection`). Returns whether commands were left for a later round. */
|
|
213
|
+
async function reconcile(state, options, commands, budget) {
|
|
214
|
+
const limit = options.reconcileLimit;
|
|
215
|
+
const slice = [...state.serverIds]
|
|
216
|
+
.filter((uid) => uid > state.reconcileCursor)
|
|
217
|
+
.sort()
|
|
218
|
+
.slice(0, limit);
|
|
219
|
+
if (slice.length === 0) {
|
|
220
|
+
state.reconcileCursor = "";
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
// Only plain uids go into the `in(...)` operand, which the query parser splits on commas and coerces per value (`me`
|
|
224
|
+
// is the caller, `null` is null); any other held id is checked on its own by exact uid.
|
|
225
|
+
const listable = slice.filter(isListableUid);
|
|
226
|
+
const found = listable.length === 0
|
|
227
|
+
? []
|
|
228
|
+
: await options.repo.find({ folderUid: options.folderUid, uid: `in(${listable.join(",")})`, limit: listable.length }, {
|
|
229
|
+
ignoreACL: true,
|
|
230
|
+
limit: listable.length,
|
|
231
|
+
});
|
|
232
|
+
const present = new Set(found.filter((row) => row.folderUid === options.folderUid).map((row) => row.uid));
|
|
233
|
+
for (const uid of slice.filter((id) => !isListableUid(id))) {
|
|
234
|
+
const row = await options.repo.findOne(uid, { ignoreACL: true });
|
|
235
|
+
if (row && row.uid === uid && row.folderUid === options.folderUid) {
|
|
236
|
+
present.add(uid);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
for (const uid of slice) {
|
|
240
|
+
if (!present.has(uid)) {
|
|
241
|
+
if (budget === 0) {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
commands.push({ kind: "Delete", uid });
|
|
245
|
+
state.serverIds.delete(uid);
|
|
246
|
+
state.echoes.delete(uid);
|
|
247
|
+
budget--;
|
|
248
|
+
}
|
|
249
|
+
state.reconcileCursor = uid;
|
|
250
|
+
}
|
|
251
|
+
if (slice.length < limit) {
|
|
252
|
+
state.reconcileCursor = "";
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
/** The `Class` a folder of `type` holds - for a `Sync`/`GetItemEstimate` request that omits `Class` for a collection
|
|
257
|
+
* with no remembered class. */
|
|
258
|
+
export function classForFolderType(type) {
|
|
259
|
+
switch (type) {
|
|
260
|
+
case FolderType.CALENDAR:
|
|
261
|
+
return "Calendar";
|
|
262
|
+
case FolderType.CONTACTS:
|
|
263
|
+
return "Contacts";
|
|
264
|
+
case FolderType.TASKS:
|
|
265
|
+
return "Tasks";
|
|
266
|
+
default:
|
|
267
|
+
return "Email";
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
271
|
+
/** [MS-ASCMD] `FilterType` age windows, in days (`1`..`7`). */
|
|
272
|
+
const FILTER_DAYS = { "1": 1, "2": 3, "3": 7, "4": 14, "5": 30, "6": 90, "7": 180 };
|
|
273
|
+
/**
|
|
274
|
+
* Builds the `include` predicate for a collection's `FilterType`, or `undefined` for no filtering: `Email` keeps
|
|
275
|
+
* messages received within the window; `Calendar` keeps recurring events and events ending within it; `Tasks`
|
|
276
|
+
* keeps incomplete tasks for `8`. Values a class doesn't define (and no `FilterType` at all) are treated as "no
|
|
277
|
+
* filter".
|
|
278
|
+
*/
|
|
279
|
+
export function filterPredicate(collectionClass, filterType, now = new Date()) {
|
|
280
|
+
const days = filterType !== undefined ? FILTER_DAYS[filterType] : undefined;
|
|
281
|
+
if (collectionClass === "Email" && days !== undefined && days <= 30) {
|
|
282
|
+
const cutoff = now.getTime() - days * DAY_MS;
|
|
283
|
+
return (item) => new Date(item.receivedDate).getTime() >= cutoff;
|
|
284
|
+
}
|
|
285
|
+
if (collectionClass === "Calendar" && days !== undefined && days >= 14) {
|
|
286
|
+
const cutoff = now.getTime() - days * DAY_MS;
|
|
287
|
+
return (item) => !!item.recurrenceRule || new Date(item.endDate).getTime() >= cutoff;
|
|
288
|
+
}
|
|
289
|
+
if (collectionClass === "Tasks" && filterType === "8") {
|
|
290
|
+
return (item) => !item.completed;
|
|
291
|
+
}
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
294
|
+
//# sourceMappingURL=EasCollectionSync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EasCollectionSync.js","sourceRoot":"","sources":["../../src/EasCollectionSync.ts"],"names":[],"mappings":"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"}
|