@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
|
@@ -1,33 +1,91 @@
|
|
|
1
1
|
import { type WbxmlElement } from "../codec/WbxmlElement.js";
|
|
2
2
|
import type { EasCommandContext, EasCommandHandler } from "../EasCommandHandler.js";
|
|
3
|
+
/** Binds one MS-ASCMD `Class` value to the entity class whose rows a `Ping` checks for pending changes. */
|
|
4
|
+
export interface PingCollectionBinding {
|
|
5
|
+
entityClass: any;
|
|
6
|
+
}
|
|
3
7
|
/**
|
|
4
8
|
* Handles EAS `Ping`: a long-poll HTTP request that blocks for up to `HeartbeatInterval` seconds waiting for a
|
|
5
|
-
* change in any of the client-specified folders, then reports which (if any) actually changed.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* change in any of the client-specified folders, then reports which (if any) actually changed. Every
|
|
10
|
+
* `Message`/`CalendarEvent`/etc. mutation already publishes to the `folderUid` channels via
|
|
11
|
+
* `BaseScopedChildRoute.notify()`/`RepoUtils`'s own push - `Ping` only needs a subscriber.
|
|
12
|
+
*
|
|
13
|
+
* Resource bounds:
|
|
14
|
+
* - One shared Redis subscriber client per process (per Redis URL), lazily connected once. Each `Ping` attaches
|
|
15
|
+
* its own listener to its channels and removes exactly that listener when it settles. A connect/subscribe
|
|
16
|
+
* failure fails open (the `Ping` just waits out its heartbeat and reports no changes) and a failed connect is
|
|
17
|
+
* forgotten so a later `Ping` retries it.
|
|
18
|
+
* - At most one active `Ping` per (mailbox, device) per process: a newer one supersedes (answers Status 1) the
|
|
19
|
+
* older one.
|
|
20
|
+
* - The wait ends as soon as the HTTP response finishes (including a client abort).
|
|
21
|
+
* - More than `mail:eas:ping_max_folders` folders is rejected with Status 6 before any ACL work, and the ACL
|
|
22
|
+
* checks themselves run in bounded concurrent chunks.
|
|
23
|
+
* - Without a `datastores:events` config the `Ping` still waits the full (clamped) heartbeat before answering
|
|
24
|
+
* Status 1, so a device can't hot-loop against it.
|
|
25
|
+
*
|
|
26
|
+
* **Changes made before the `Ping` started**: a publish only reaches a subscriber that exists at that moment, so a
|
|
27
|
+
* change landing between the device's last `Sync` and this `Ping`'s subscribe would otherwise go unnoticed until
|
|
28
|
+
* the next change. Once subscribed (or right away, without Redis), each folder's stream is checked for a row after
|
|
29
|
+
* the cursor its `EasCollectionState` recorded, and any folder with one is answered with Status 2 immediately. This
|
|
30
|
+
* needs `collectionStateClass`/`collectionBindings` (set by `PingCommandMongo`/`PingCommandSQL`); a folder the
|
|
31
|
+
* device never synced has no cursor and isn't checked.
|
|
11
32
|
*
|
|
12
33
|
* Requested folder uids are filtered down to only those the caller currently has `READ` on before subscribing -
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* to and no longer does) and learn purely from this channel when that other mailbox's data changes.
|
|
34
|
+
* without it a device could long-poll on any folder uid it happens to know (including one whose share was since
|
|
35
|
+
* revoked) and learn from this channel when that other mailbox's data changes.
|
|
16
36
|
*
|
|
17
37
|
* @author Jean-Philippe Steinmetz
|
|
18
38
|
*/
|
|
19
39
|
export declare class PingCommand implements EasCommandHandler {
|
|
40
|
+
/** Shared subscriber connections, keyed by Redis URL - each value resolves once that client is connected. */
|
|
41
|
+
private static readonly subscribers;
|
|
42
|
+
/** The cancel function of the currently active `Ping` for each (mailbox, device) pair. */
|
|
43
|
+
private static readonly activePings;
|
|
20
44
|
readonly command = "Ping";
|
|
21
45
|
private redisConfig;
|
|
22
46
|
private minHeartbeatSeconds;
|
|
23
47
|
private maxHeartbeatSeconds;
|
|
48
|
+
private maxFolders;
|
|
24
49
|
private aclUtils?;
|
|
50
|
+
/** Supplied by the Mongo/SQL subclasses; without them the pending-change check is skipped. */
|
|
51
|
+
protected collectionStateClass?: any;
|
|
52
|
+
protected collectionBindings: Record<string, PingCollectionBinding>;
|
|
53
|
+
private _objectFactory?;
|
|
54
|
+
private collectionStateRepo?;
|
|
55
|
+
private repos;
|
|
56
|
+
init(): Promise<void>;
|
|
57
|
+
/** Forgets all process-wide shared state (subscriber clients and active pings). Intended for tests. */
|
|
58
|
+
static resetSharedState(): void;
|
|
25
59
|
handle(ctx: EasCommandContext): Promise<WbxmlElement | undefined>;
|
|
26
60
|
private statusResponse;
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
*
|
|
31
|
-
*
|
|
61
|
+
/** Returns the subset of `folderUids` the caller has `READ` on, checking at most `ACL_CHECK_CHUNK_SIZE` at once. */
|
|
62
|
+
private filterPermitted;
|
|
63
|
+
/**
|
|
64
|
+
* The subset of `folderUids` whose change stream has a row the device hasn't synced yet (after its collection's
|
|
65
|
+
* recorded cursor, other than the device's own writes). A failed lookup counts as "no pending change".
|
|
66
|
+
*
|
|
67
|
+
* Batched, so a `Ping` watching hundreds of folders costs a handful of queries rather than three per folder: every
|
|
68
|
+
* folder's `EasCollectionState` is read in one `in(...)` query per `STATE_LOOKUP_CHUNK`, and each collection class's
|
|
69
|
+
* folders are checked together (`pendingInClass()`); only a folder that batch can't decide is scanned on its own.
|
|
70
|
+
*/
|
|
71
|
+
private pendingChanges;
|
|
72
|
+
/** Every requested folder's collection state for this device, keyed by folder uid - one query per chunk of plain
|
|
73
|
+
* folder uids (exact-matched in memory), and one query per folder uid that can't be listed in `in(...)`. */
|
|
74
|
+
private loadStates;
|
|
75
|
+
/**
|
|
76
|
+
* The folders among `states` (all of one collection class, stored in `repo`) with a pending change. One pair of
|
|
77
|
+
* queries (live and soft-deleted rows) reads, across all of them, the rows at or after the earliest cursor, in
|
|
78
|
+
* stream order, up to `PENDING_BATCH_ROW_LIMIT`. When neither page is full, that is every row that could be pending
|
|
79
|
+
* for any of the folders, so each folder is decided exactly in memory - the same rule as a per-folder `scanAfter()`.
|
|
80
|
+
* When a page is full, a folder with a pending row among the rows read is still pending; any other folder is scanned
|
|
81
|
+
* on its own, as before. A folder uid that can't be listed in `in(...)` is always scanned on its own.
|
|
82
|
+
*/
|
|
83
|
+
private pendingInClass;
|
|
84
|
+
/** Returns the process-wide subscriber client for `url`, connecting it on first use. A failed connect is
|
|
85
|
+
* evicted from the cache (and the client destroyed) so a later call retries. */
|
|
86
|
+
private static getSubscriber;
|
|
87
|
+
/** Waits up to `timeoutSeconds` for a publish on any of `folderUids`, resolving with the channel that changed,
|
|
88
|
+
* or `[]` on timeout, supersede by a newer `Ping` from the same device, request close, or Redis being
|
|
89
|
+
* unavailable (in which case the full heartbeat is still waited). */
|
|
32
90
|
private waitForChange;
|
|
33
91
|
}
|
|
@@ -15,19 +15,25 @@ import type { EasCommandContext, EasCommandHandler } from "../EasCommandHandler.
|
|
|
15
15
|
* no way for the server to inspect device state directly either).
|
|
16
16
|
*
|
|
17
17
|
* - **Request 1** (no `PolicyKey` in the body): mint a new policy key, store it on `DeviceSyncState` (not yet
|
|
18
|
-
* provisioned), and send back the policy document under that key.
|
|
19
|
-
* device (`DeviceSyncState.remoteWipeRequested`, set by an admin - see the remote-wipe route), skip normal
|
|
20
|
-
* policy issuance entirely and send the `RemoteWipe` directive instead.
|
|
18
|
+
* provisioned), and send back the policy document under that key.
|
|
21
19
|
* - **Request 2** (client echoes the `PolicyKey` back, acknowledging the policy): if the key matches what was
|
|
22
20
|
* minted in request 1 *and* the client's own `Status` is `1`, mark the device provisioned and re-confirm the
|
|
23
21
|
* same key; anything else (a stale/replayed key, a device that never actually saw request 1's response, or a
|
|
24
22
|
* device reporting it could not comply) is rejected without provisioning.
|
|
23
|
+
* - **While a wipe is pending**, every other Provision request (a policy request *or* an acknowledgement of an
|
|
24
|
+
* older key) gets the `RemoteWipe` directive and never provisions. Requesting the wipe also clears the stored
|
|
25
|
+
* policy key (`BaseDeviceSyncStateRoute.remoteWipe`), and `BaseEasRoute` refuses any command whose presented
|
|
26
|
+
* `X-MS-PolicyKey` doesn't match the stored key, so a device can't keep syncing on its old key.
|
|
25
27
|
* - **RemoteWipe acknowledgement**: after wiping itself, a device sends a bare `<Provision><RemoteWipe>
|
|
26
28
|
* <Status>1</Status></RemoteWipe></Provision>` (no `Policies`). Detected first, ahead of the normal
|
|
27
|
-
* issue/acknowledge branching
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
29
|
+
* issue/acknowledge branching, and ignored (Status 2) unless a wipe is actually pending. Clears `remoteWipeRequested`,
|
|
30
|
+
* stamps `remoteWipeAcknowledgedAt` for audit and sets `blocked`, leaving `provisioned` `false`.
|
|
31
|
+
* - **A blocked device** (one that acknowledged a wipe) is refused every Provision request with Status 129
|
|
32
|
+
* (DeviceIsBlockedForThisUser) - and `BaseEasRoute` refuses its other commands - until an administrator clears the
|
|
33
|
+
* flag (`BaseDeviceSyncStateRoute.unblock`). Otherwise a device (or whoever holds it) could acknowledge the wipe
|
|
34
|
+
* without wiping anything and simply provision again. The block (like the wipe itself) is keyed on the client-supplied
|
|
35
|
+
* `DeviceId`: a client that ignores the directive can pair again under another `DeviceId` with the same credentials -
|
|
36
|
+
* see `DeviceSyncState.blocked`; revoking the account's credentials is what stops that. `remoteWipeAccountOnly` is recorded for admin audit only; the wire directive sent to the
|
|
31
37
|
* device is identical either way (a real "wipe just this account's data" vs. "wipe the whole device"
|
|
32
38
|
* distinction would require an MDM-capable client extension this library doesn't implement).
|
|
33
39
|
*
|
|
@@ -41,14 +47,13 @@ export declare class ProvisionCommand implements EasCommandHandler {
|
|
|
41
47
|
private requireDeviceEncryption;
|
|
42
48
|
private allowSimplePassword;
|
|
43
49
|
handle(ctx: EasCommandContext): Promise<WbxmlElement | undefined>;
|
|
44
|
-
/** Request 1: mint and store a new policy key
|
|
45
|
-
* pending for this device, the `RemoteWipe` directive instead. */
|
|
50
|
+
/** Request 1: mint and store a new policy key and send the policy document. */
|
|
46
51
|
private issuePolicy;
|
|
47
52
|
/** Request 2: the client acknowledges the policy key it was handed in request 1, self-reporting whether
|
|
48
53
|
* it actually applied the policy via its own `Status`. */
|
|
49
54
|
private acknowledgePolicy;
|
|
50
|
-
/** The device has wiped itself and is acknowledging - clear the pending flag
|
|
51
|
-
*
|
|
55
|
+
/** The device has wiped itself and is acknowledging - clear the pending flag and block the device until an
|
|
56
|
+
* administrator unblocks it; `provisioned` stays `false` (from when the wipe was requested). */
|
|
52
57
|
private acknowledgeRemoteWipe;
|
|
53
58
|
private persist;
|
|
54
59
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type WbxmlElement } from "../codec/WbxmlElement.js";
|
|
2
2
|
import type { EasCommandContext, EasCommandHandler } from "../EasCommandHandler.js";
|
|
3
|
+
/** [MS-ASCMD]: a `ResolveRecipients` request MUST NOT contain more than 100 `To` elements. */
|
|
4
|
+
export declare const MAX_RESOLVE_RECIPIENTS_TO = 100;
|
|
3
5
|
/**
|
|
4
6
|
* Handles EAS `ResolveRecipients`: resolves each `<To>` value (a display name, partial name, or address) the
|
|
5
7
|
* client is unsure how to reach against the mailbox's own `Contact` (GAL) store - the same substring-match
|
|
@@ -28,8 +30,10 @@ export declare abstract class ResolveRecipientsCommand implements EasCommandHand
|
|
|
28
30
|
private maxMatches;
|
|
29
31
|
private _objectFactory?;
|
|
30
32
|
private contactRepo?;
|
|
33
|
+
private logger;
|
|
31
34
|
init(): Promise<void>;
|
|
32
35
|
handle(ctx: EasCommandContext): Promise<WbxmlElement | undefined>;
|
|
36
|
+
private statusOnly;
|
|
33
37
|
private resolveOne;
|
|
34
38
|
private recipientElement;
|
|
35
39
|
private responseElement;
|
|
@@ -30,7 +30,12 @@ import type { EasCommandContext, EasCommandHandler } from "../EasCommandHandler.
|
|
|
30
30
|
* request's own capped fetch actually returned" - a client requesting a `Range` past that cap sees fewer
|
|
31
31
|
* results than may really exist, a documented approximation rather than exact server-side paging.
|
|
32
32
|
*
|
|
33
|
-
*
|
|
33
|
+
* **Audit**: the index is the caller's own mailbox's, but a hit is rendered from the stored message, wherever it is
|
|
34
|
+
* filed now. Results from a mailbox the caller doesn't own (restapi's `isNonOwnerAccess()`) are recorded as one
|
|
35
|
+
* `MESSAGE_CONTENT_ACCESSED` entry per such mailbox per request, listing the returned message uids (`EasAuditLog`).
|
|
36
|
+
*
|
|
37
|
+
* `contactClass`/`messageClass`/`emailAdapterClass`/`mailboxClass`/`auditLogClass` are supplied by the Mongo/SQL
|
|
38
|
+
* concrete subclasses.
|
|
34
39
|
*
|
|
35
40
|
* @author Jean-Philippe Steinmetz
|
|
36
41
|
*/
|
|
@@ -39,7 +44,12 @@ export declare abstract class SearchCommand implements EasCommandHandler {
|
|
|
39
44
|
protected abstract contactClass: any;
|
|
40
45
|
protected abstract messageClass: any;
|
|
41
46
|
protected abstract emailAdapterClass: any;
|
|
47
|
+
protected abstract mailboxClass: any;
|
|
48
|
+
protected abstract auditLogClass: any;
|
|
42
49
|
private _objectFactory?;
|
|
50
|
+
private config?;
|
|
51
|
+
private logger;
|
|
52
|
+
private mailboxRepo?;
|
|
43
53
|
private contactRepo?;
|
|
44
54
|
private messageRepo?;
|
|
45
55
|
private emailAdapter?;
|
|
@@ -51,6 +61,8 @@ export declare abstract class SearchCommand implements EasCommandHandler {
|
|
|
51
61
|
handle(ctx: EasCommandContext): Promise<WbxmlElement | undefined>;
|
|
52
62
|
private handleGal;
|
|
53
63
|
private handleMailbox;
|
|
64
|
+
/** One `MESSAGE_CONTENT_ACCESSED` entry per non-owner mailbox among `page`'s messages (see this class's doc comment). */
|
|
65
|
+
private auditResults;
|
|
54
66
|
private contactToResult;
|
|
55
67
|
/** Reuses `EmailSyncAdapter.toApplicationData()`'s own field mapping (Subject/From/To/Cc/Bcc/DateReceived/
|
|
56
68
|
* Importance/Read/Flag/Body/ConversationId) for a Mailbox-store search hit's `Properties` - the same
|
|
@@ -1,100 +1,140 @@
|
|
|
1
1
|
import { type RecoverableBaseEntity } from "@rapidrest/service-core";
|
|
2
2
|
import { type WbxmlElement } from "../codec/WbxmlElement.js";
|
|
3
3
|
import type { EasCommandContext, EasCommandHandler } from "../EasCommandHandler.js";
|
|
4
|
+
/** Most `<Collection>`s one `Sync` request may carry - more is answered with a top-level Status 4. */
|
|
5
|
+
export declare const MAX_SYNC_COLLECTIONS = 300;
|
|
6
|
+
/** Most client `Add`/`Change`/`Delete` commands one collection of a `Sync` request may carry - more is answered
|
|
7
|
+
* with that collection's Status 4, without applying any of them. */
|
|
8
|
+
export declare const MAX_SYNC_COMMANDS_PER_COLLECTION = 512;
|
|
4
9
|
/** Binds one MS-ASCMD `Class` value (`"Email"`, `"Contacts"`, ...) to the concrete entity class `SyncCommand`
|
|
5
10
|
* should build a `RepoUtils` for, and the adapter class that maps that entity to/from `ApplicationData`.
|
|
6
|
-
* Supplied by the Mongo/SQL concrete subclasses, one map entry per supported collection type.
|
|
7
|
-
*
|
|
8
|
-
* `adapterClass`, not a pre-built `adapter` instance: `SyncCommand.init()` instantiates each one itself via
|
|
9
|
-
* `ObjectFactory`, so an adapter can `@Inject` its own dependencies (`EmailSyncAdapter` needs `BlobStore` for a
|
|
10
|
-
* Draft's body) exactly like any other DI-managed class in this library - a bare `new EmailSyncAdapter()` has
|
|
11
|
-
* no way to satisfy that. */
|
|
11
|
+
* Supplied by the Mongo/SQL concrete subclasses, one map entry per supported collection type. */
|
|
12
12
|
export interface SyncCollectionBinding<T extends RecoverableBaseEntity> {
|
|
13
13
|
entityClass: any;
|
|
14
14
|
adapterClass: any;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
* Handles EAS `Sync
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
17
|
+
* Handles EAS `Sync` for `Email`/`Contacts`/`Calendar`/`Tasks` folders.
|
|
18
|
+
*
|
|
19
|
+
* **Per-collection state** lives in its own `EasCollectionState` row per (mailbox, device, folder) - see that
|
|
20
|
+
* model - rather than in `DeviceSyncState`, so concurrent `Sync`s of different folders never contend for one row.
|
|
21
|
+
* Besides the issued `SyncKey`, the row records exactly which items the device holds (inline in `serverIds` while
|
|
22
|
+
* small, in `EasCollectionChunk` rows once large - see `EasCollectionStore`). That is what makes the reported
|
|
23
|
+
* commands correct rather than guessed: an item the device doesn't hold is always an `Add` (including on the first
|
|
24
|
+
* round after `SyncKey 0`), an item it holds is a `Change`, and an item it holds that has been deleted *or moved to
|
|
25
|
+
* another folder* is a `Delete` - see `EasCollectionSync.enumerateCollection`.
|
|
26
|
+
*
|
|
27
|
+
* **One round per collection at a time**: each collection's round runs under a lease on (mailbox, device, folder)
|
|
28
|
+
* (`EasCollectionLease`: in-process, plus Redis `datastores:cache` across server copies when configured), and its
|
|
29
|
+
* state is read only once the lease is held - a second `Sync` of the same collection waits for the first to finish
|
|
30
|
+
* and then sees its result (typically answering the now-previous `SyncKey` as a retry), instead of both computing a
|
|
31
|
+
* round from the same state. A lease not acquired within `LEASE_WAIT_MS` is answered with Status 16 (retry).
|
|
32
|
+
*
|
|
33
|
+
* **Round order**: the client's own `Commands` are applied first, then server changes are enumerated. The device's
|
|
34
|
+
* own writes are not echoed back: each successful `Add`/`Change` records the resulting `dateModified` in the row's
|
|
35
|
+
* `echoes`, and a changed row still carrying exactly that timestamp is skipped. The cursor itself only ever advances
|
|
36
|
+
* past rows actually enumerated, never past a pending server change.
|
|
37
|
+
*
|
|
38
|
+
* **Lost state**: a round's state that can't be saved is answered with the collection's Status 3 (and a failed
|
|
39
|
+
* `SyncKey 0` restart with Status 5), never with a `SyncKey` the server doesn't have - the device re-syncs from
|
|
40
|
+
* scratch instead of continuing from a key whose held set was never recorded.
|
|
41
|
+
*
|
|
42
|
+
* **Retries**: a client that never received a response re-sends the `SyncKey` it still holds. The row keeps the
|
|
43
|
+
* previous round's key and delta (`previous`), so that key is accepted and the round is recomputed from the state
|
|
44
|
+
* before it; an `Add` re-sent with the same `ClientId` is answered with the item created the first time, and a
|
|
45
|
+
* `Delete` of an item that round already removed succeeds silently.
|
|
46
|
+
*
|
|
47
|
+
* **Options honoured**: `WindowSize` (capped by `mail:eas:sync_window_size` and 512), `FilterType` (age window for
|
|
48
|
+
* `Email`/`Calendar`, incomplete-only for `Tasks`; applied to items the device doesn't hold yet). A collection
|
|
49
|
+
* started without `Options` has no filter recorded, and the first `FilterType` sent while the device still holds
|
|
50
|
+
* nothing is adopted (clients commonly send `Options` only from the second request on); a `FilterType` differing
|
|
51
|
+
* from the recorded one otherwise gets Status 3 so the client re-syncs from 0. `DeletesAsMoves` (default `true`: an
|
|
52
|
+
* `Email` delete moves the message to Deleted Items; a delete inside Deleted Items, or with `DeletesAsMoves` `0`,
|
|
53
|
+
* deletes it) and `GetChanges` `0` (no server changes this round).
|
|
22
54
|
*
|
|
23
|
-
* **
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* gets one response covering all of them. All per-collection `SyncKey`/remembered-`Class` writes for the whole
|
|
27
|
-
* request are batched into a single `persistDeviceSyncState` call after every collection has been processed
|
|
28
|
-
* (never one call per collection) - see `EasSyncKeyUtils.persistDeviceSyncState`'s own doc comment for why a
|
|
29
|
-
* second write to the same `DeviceSyncState` within one request must never be built off a stale copy.
|
|
55
|
+
* **Meetings**: a device deleting or editing an attendee's copy of someone else's meeting never makes restapi's
|
|
56
|
+
* `MeetingSchedulingJob` send cancellations/invitations as the organizer - see `CalendarSyncAdapter.beforeDelete`
|
|
57
|
+
* and its `fromApplicationData`.
|
|
30
58
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
59
|
+
* **Access**: every `CollectionId` needs `READ` on the folder (otherwise Status 4, indistinguishable from an unknown
|
|
60
|
+
* collection); `Add`/`Change`/`Delete` additionally need `CREATE`/`UPDATE`/`DELETE`, and a `ServerId` that resolves
|
|
61
|
+
* to an item outside the synced folder is reported as not found (Status 8). An `Email` `Add` is only accepted in
|
|
62
|
+
* a Drafts folder ([MS-ASCMD]: no non-draft email may be added by a client), and a new item's `mailboxUid` is the
|
|
63
|
+
* folder's own mailbox, so an item added to a shared folder belongs to that folder's mailbox. An `Email` body can only
|
|
64
|
+
* be changed on a genuine draft (`EmailSyncAdapter`), and a delete-as-move out of Outbox cancels the scheduled send
|
|
65
|
+
* (`MessageMoveRules.planMessageMove`; Status 6 once the message was relayed), and no delete at all happens while a send of
|
|
66
|
+
* the message is in flight (`hasLiveSendLease`, Status 6). Every update is version-checked on both
|
|
67
|
+
* backends (`asEntity`).
|
|
34
68
|
*
|
|
35
|
-
* **
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* and `applyChange`/`applyDelete` re-verify the resolved item's own `folderUid` actually matches the collection
|
|
40
|
-
* being synced (treating a mismatch identically to "not found" - Status `8` - never revealing that the
|
|
41
|
-
* `ServerId` resolves to something real elsewhere). Without this, a client could supply any other mailbox's
|
|
42
|
-
* folder/item uid as its own `CollectionId`/`ServerId` and read or mutate that mailbox's data directly - the
|
|
43
|
-
* same ownership-verification-after-an-`ignoreACL`-lookup pattern `ItemOperationsCommand`/`MoveItemsCommand`
|
|
44
|
-
* already use, applied consistently here too.
|
|
69
|
+
* **Audit** (`EasAuditLog`), only in a mailbox the caller doesn't own (restapi's `isNonOwnerAccess()` - an administrator
|
|
70
|
+
* or a delegate syncing a shared folder), and only for `Email`: a round that sends `Add`/`Change` items records one
|
|
71
|
+
* `MESSAGE_CONTENT_ACCESSED` entry for the collection listing the sent message uids (at most a window's worth), rather
|
|
72
|
+
* than one per row; each successful client `Delete` records one `MESSAGE_DELETE` entry, as restapi's REST delete does.
|
|
45
73
|
*
|
|
46
|
-
*
|
|
47
|
-
* - **Client-originated `Add`/`Change`/`Delete` commands are accepted for every collection type**, including
|
|
48
|
-
* `Email` (a device creating/editing a Draft, or deleting a message locally - see `applyAdd`/`applyChange`/
|
|
49
|
-
* `applyDelete`). `[MS-ASCMD]` itself disallows `Add`/`Change` for any *non-draft* `Email` item - this library
|
|
50
|
-
* doesn't verify a Sync `Email` Add/Change actually targets the caller's own Drafts folder *specifically*
|
|
51
|
-
* (only that it's a folder the caller actually owns/can write to - see above), matching how Contacts/Calendar/
|
|
52
|
-
* Tasks folder targeting is equally unchecked beyond ownership elsewhere. A collection whose adapter has no
|
|
53
|
-
* `fromApplicationData` at all would get Status `6` for `Add`/`Change` instead, but every adapter today
|
|
54
|
-
* implements it. Per `[MS-ASCMD]`'s own "Add (Sync)"/"Status (Sync)" pages: `Add` always gets a `Responses`
|
|
55
|
-
* entry (it must report the assigned `ServerId`); `Change`/`Delete` only get one on **failure** - a silent
|
|
56
|
-
* success means "assume it worked."
|
|
57
|
-
* - Only a body preview is returned per item (see `EmailSyncAdapter`'s own doc comment) - full body content is
|
|
58
|
-
* fetched separately via `ItemOperationsCommand`. A Draft's `Email` Add/Change is plain-text-only, with no
|
|
59
|
-
* attachment support (mirrors `ComposeMailCommand`'s own already-documented attachment gap).
|
|
74
|
+
* Per `[MS-ASCMD]`, `Add` always gets a `Responses` entry; `Change`/`Delete` only on failure.
|
|
60
75
|
*
|
|
61
76
|
* @author Jean-Philippe Steinmetz
|
|
62
77
|
*/
|
|
63
78
|
export declare abstract class SyncCommand implements EasCommandHandler {
|
|
64
79
|
readonly command = "Sync";
|
|
65
80
|
protected abstract collectionBindings: Record<string, SyncCollectionBinding<any>>;
|
|
66
|
-
/** Supplied by the Mongo/SQL concrete subclasses
|
|
67
|
-
* be given the caller's own `Mailbox` (`EmailSyncAdapter` needs it for a new Draft's `from`) - same
|
|
68
|
-
* one-line-per-backend pattern `MeetingResponseCommand`/`SettingsCommand` already use. */
|
|
81
|
+
/** Supplied by the Mongo/SQL concrete subclasses. */
|
|
69
82
|
protected abstract mailboxClass: any;
|
|
83
|
+
protected abstract folderClass: any;
|
|
84
|
+
protected abstract collectionStateClass: any;
|
|
85
|
+
protected abstract collectionChunkClass: any;
|
|
86
|
+
protected abstract auditLogClass: any;
|
|
70
87
|
private windowSize;
|
|
88
|
+
private cacheConfig;
|
|
89
|
+
private config?;
|
|
90
|
+
protected moveScanLimit: number;
|
|
91
|
+
protected reconcileLimit: number;
|
|
92
|
+
protected leaseWaitMs: number;
|
|
71
93
|
private _objectFactory?;
|
|
72
94
|
private aclUtils?;
|
|
95
|
+
private logger;
|
|
73
96
|
private repos;
|
|
74
97
|
private adapters;
|
|
75
98
|
private mailboxRepo?;
|
|
99
|
+
private folderRepo?;
|
|
100
|
+
private collectionStateRepo?;
|
|
101
|
+
private collectionChunkRepo?;
|
|
76
102
|
init(): Promise<void>;
|
|
77
|
-
|
|
78
|
-
|
|
103
|
+
private get chunkStore();
|
|
104
|
+
/** Resolves a mailbox at most once per request, and only if actually needed. */
|
|
79
105
|
private mailboxLoader;
|
|
80
106
|
handle(ctx: EasCommandContext): Promise<WbxmlElement | undefined>;
|
|
81
|
-
|
|
82
|
-
* this round advanced anything) the `folderUid`/new `SyncKey`/remembered `Class` for `handle()` to fold
|
|
83
|
-
* into its single end-of-request `persistDeviceSyncState` call - this method itself never persists
|
|
84
|
-
* anything, so it's safe to call once per collection in a request without the write-batching hazard
|
|
85
|
-
* `EasSyncKeyUtils.persistDeviceSyncState`'s doc comment describes. */
|
|
107
|
+
private effectiveWindowSize;
|
|
86
108
|
private processCollection;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
*
|
|
91
|
-
|
|
109
|
+
/** The rest of `processCollection`, run while holding the collection's lease. */
|
|
110
|
+
private processLocked;
|
|
111
|
+
/** `SyncKey 0`: (re)starts the collection with an empty item set. Per [MS-ASCMD] the response carries only the
|
|
112
|
+
* new key; the next round reports every item as an `Add`. */
|
|
113
|
+
private startCollection;
|
|
114
|
+
/**
|
|
115
|
+
* Writes the held set (`held.ids`, given the set `held.loaded` the round started from) and then creates or
|
|
116
|
+
* updates the collection's state row. Returns `false` - after logging - when anything failed (including losing a
|
|
117
|
+
* race for the row): the caller must then not hand out the new key. A `"restart"` empties the held set.
|
|
118
|
+
*
|
|
119
|
+
* **Chunk writes can't be atomic with the state row**, so before any chunk row is touched the state row is first
|
|
120
|
+
* marked `chunked` with both its `SyncKey` and its previous round's key blanked (`invalidateBeforeChunkWrite()`).
|
|
121
|
+
* If anything after that fails - including the final state write, and even when the device never sees this round's
|
|
122
|
+
* Status 3 - no key matches the half-written chunks any more: the device's next `Sync` gets Status 3 and restarts
|
|
123
|
+
* with `SyncKey 0`, which always removes every chunk row of the collection (whether or not the row says `chunked`,
|
|
124
|
+
* so rows orphaned before this rule existed are cleared too). A collection converting from inline to chunked also
|
|
125
|
+
* clears leftover chunk rows first, so an orphan can never collide with the unique `chunkIndex`.
|
|
126
|
+
*/
|
|
127
|
+
private saveState;
|
|
128
|
+
/** Marks `stored` chunked with no acceptable `SyncKey` (current or previous) before its chunk rows are written - see
|
|
129
|
+
* `saveState()`. Returns the updated row, whose version the final state write must carry. */
|
|
130
|
+
private invalidateBeforeChunkWrite;
|
|
92
131
|
private addResponseElement;
|
|
93
132
|
private statusResponseElement;
|
|
133
|
+
/** Remembers the `dateModified` the device's own write left on `item`, so the write isn't echoed back. */
|
|
134
|
+
private noteWrite;
|
|
94
135
|
private applyAdd;
|
|
95
136
|
private applyChange;
|
|
96
137
|
private applyDelete;
|
|
97
|
-
/** Builds one `<Collection>` response element
|
|
98
|
-
* and wraps the whole set in a single `<Sync><Collections>`. */
|
|
138
|
+
/** Builds one `<Collection>` response element. */
|
|
99
139
|
private collectionResponse;
|
|
100
140
|
}
|
|
@@ -3,5 +3,8 @@ import { GetItemEstimateCommand, type EstimateCollectionBinding } from "../GetIt
|
|
|
3
3
|
* @author Jean-Philippe Steinmetz
|
|
4
4
|
*/
|
|
5
5
|
export declare class GetItemEstimateCommandMongo extends GetItemEstimateCommand {
|
|
6
|
+
protected folderClass: any;
|
|
7
|
+
protected collectionStateClass: any;
|
|
8
|
+
protected collectionChunkClass: any;
|
|
6
9
|
protected collectionBindings: Record<string, EstimateCollectionBinding>;
|
|
7
10
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { PingCommand, type PingCollectionBinding } from "../PingCommand.js";
|
|
2
|
+
/**
|
|
3
|
+
* @author Jean-Philippe Steinmetz
|
|
4
|
+
*/
|
|
5
|
+
export declare class PingCommandMongo extends PingCommand {
|
|
6
|
+
protected collectionStateClass: any;
|
|
7
|
+
protected collectionBindings: Record<string, PingCollectionBinding>;
|
|
8
|
+
}
|
|
@@ -4,5 +4,9 @@ import { SyncCommand, type SyncCollectionBinding } from "../SyncCommand.js";
|
|
|
4
4
|
*/
|
|
5
5
|
export declare class SyncCommandMongo extends SyncCommand {
|
|
6
6
|
protected mailboxClass: any;
|
|
7
|
+
protected auditLogClass: any;
|
|
8
|
+
protected folderClass: any;
|
|
9
|
+
protected collectionStateClass: any;
|
|
10
|
+
protected collectionChunkClass: any;
|
|
7
11
|
protected collectionBindings: Record<string, SyncCollectionBinding<any>>;
|
|
8
12
|
}
|
|
@@ -3,5 +3,8 @@ import { GetItemEstimateCommand, type EstimateCollectionBinding } from "../GetIt
|
|
|
3
3
|
* @author Jean-Philippe Steinmetz
|
|
4
4
|
*/
|
|
5
5
|
export declare class GetItemEstimateCommandSQL extends GetItemEstimateCommand {
|
|
6
|
+
protected folderClass: any;
|
|
7
|
+
protected collectionStateClass: any;
|
|
8
|
+
protected collectionChunkClass: any;
|
|
6
9
|
protected collectionBindings: Record<string, EstimateCollectionBinding>;
|
|
7
10
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { PingCommand, type PingCollectionBinding } from "../PingCommand.js";
|
|
2
|
+
/**
|
|
3
|
+
* @author Jean-Philippe Steinmetz
|
|
4
|
+
*/
|
|
5
|
+
export declare class PingCommandSQL extends PingCommand {
|
|
6
|
+
protected collectionStateClass: any;
|
|
7
|
+
protected collectionBindings: Record<string, PingCollectionBinding>;
|
|
8
|
+
}
|
|
@@ -4,5 +4,9 @@ import { SyncCommand, type SyncCollectionBinding } from "../SyncCommand.js";
|
|
|
4
4
|
*/
|
|
5
5
|
export declare class SyncCommandSQL extends SyncCommand {
|
|
6
6
|
protected mailboxClass: any;
|
|
7
|
+
protected auditLogClass: any;
|
|
8
|
+
protected folderClass: any;
|
|
9
|
+
protected collectionStateClass: any;
|
|
10
|
+
protected collectionChunkClass: any;
|
|
7
11
|
protected collectionBindings: Record<string, SyncCollectionBinding<any>>;
|
|
8
12
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -30,9 +30,14 @@ export * from "./codec/WbxmlDecoder.js";
|
|
|
30
30
|
export * from "./BaseEasRoute.js";
|
|
31
31
|
export * from "./BaseDeviceSyncStateRoute.js";
|
|
32
32
|
export * from "./models/DeviceSyncState.js";
|
|
33
|
+
export * from "./models/EasCollectionState.js";
|
|
34
|
+
export * from "./models/EasCollectionChunk.js";
|
|
33
35
|
export * from "./jobs/EasDeviceStateCleanupJob.js";
|
|
34
36
|
export * from "./EasCommandHandler.js";
|
|
35
37
|
export * from "./EasSyncKeyUtils.js";
|
|
38
|
+
export * from "./EasCollectionSync.js";
|
|
39
|
+
export * from "./EasCollectionStore.js";
|
|
40
|
+
export * from "./EasCollectionLease.js";
|
|
36
41
|
export * from "./CompactDateTime.js";
|
|
37
42
|
export * from "./commands/ProvisionCommand.js";
|
|
38
43
|
export * from "./commands/FolderSyncCommand.js";
|