document-drive 1.17.2 → 1.19.0
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 +83 -0
- package/package.json +11 -9
- package/src/server/index.ts +341 -232
- package/src/server/listener/manager.ts +265 -144
- package/src/server/listener/transmitter/pull-responder.ts +64 -11
- package/src/server/listener/transmitter/switchboard-push.ts +56 -13
- package/src/server/types.ts +114 -144
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ListenerFilter, Trigger } from "document-model-libs/document-drive";
|
|
2
|
-
import { Operation
|
|
2
|
+
import { Operation } from "document-model/document";
|
|
3
3
|
import { PULL_DRIVE_INTERVAL } from "../..";
|
|
4
4
|
import { generateUUID } from "../../../utils";
|
|
5
5
|
import { gql, requestGraphql } from "../../../utils/graphql";
|
|
@@ -7,7 +7,7 @@ import { logger as defaultLogger } from "../../../utils/logger";
|
|
|
7
7
|
import { OperationError } from "../../error";
|
|
8
8
|
import {
|
|
9
9
|
GetStrandsOptions,
|
|
10
|
-
|
|
10
|
+
IListenerManager,
|
|
11
11
|
IOperationResult,
|
|
12
12
|
Listener,
|
|
13
13
|
ListenerRevision,
|
|
@@ -16,13 +16,14 @@ import {
|
|
|
16
16
|
RemoteDriveOptions,
|
|
17
17
|
StrandUpdate,
|
|
18
18
|
} from "../../types";
|
|
19
|
-
import { ListenerManager } from "../manager";
|
|
20
19
|
import {
|
|
21
20
|
ITransmitter,
|
|
22
21
|
PullResponderTrigger,
|
|
23
22
|
StrandUpdateSource,
|
|
24
23
|
} from "./types";
|
|
25
24
|
|
|
25
|
+
const ENABLE_SYNC_DEBUG = false;
|
|
26
|
+
|
|
26
27
|
export type OperationUpdateGraphQL = Omit<OperationUpdate, "input"> & {
|
|
27
28
|
input: string;
|
|
28
29
|
};
|
|
@@ -45,22 +46,52 @@ export interface IPullResponderTransmitter extends ITransmitter {
|
|
|
45
46
|
getStrands(options?: GetStrandsOptions): Promise<StrandUpdate[]>;
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
const STATIC_DEBUG_ID = `[PRT #static]`;
|
|
50
|
+
|
|
51
|
+
function staticDebugLog(...data: any[]) {
|
|
52
|
+
if (!ENABLE_SYNC_DEBUG) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (data.length > 0 && typeof data[0] === "string") {
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
58
|
+
console.log(`${STATIC_DEBUG_ID} ${data[0]}`, ...data.slice(1));
|
|
59
|
+
} else {
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
61
|
+
console.log(STATIC_DEBUG_ID, ...data);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
48
65
|
export class PullResponderTransmitter implements IPullResponderTransmitter {
|
|
49
|
-
private
|
|
66
|
+
private debugID = `[PRT #${Math.floor(Math.random() * 999)}]`;
|
|
50
67
|
private listener: Listener;
|
|
51
|
-
private manager:
|
|
68
|
+
private manager: IListenerManager;
|
|
52
69
|
|
|
53
|
-
constructor(
|
|
54
|
-
listener: Listener,
|
|
55
|
-
drive: IBaseDocumentDriveServer,
|
|
56
|
-
manager: ListenerManager,
|
|
57
|
-
) {
|
|
70
|
+
constructor(listener: Listener, manager: IListenerManager) {
|
|
58
71
|
this.listener = listener;
|
|
59
|
-
this.drive = drive;
|
|
60
72
|
this.manager = manager;
|
|
73
|
+
this.debugLog(`constructor(listener: ${listener.listenerId})`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private debugLog(...data: any[]) {
|
|
77
|
+
if (!ENABLE_SYNC_DEBUG) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (data.length > 0 && typeof data[0] === "string") {
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
83
|
+
console.log(`${this.debugID} ${data[0]}`, ...data.slice(1));
|
|
84
|
+
} else {
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
86
|
+
console.log(this.debugID, ...data);
|
|
87
|
+
}
|
|
61
88
|
}
|
|
62
89
|
|
|
63
90
|
getStrands(options?: GetStrandsOptions): Promise<StrandUpdate[]> {
|
|
91
|
+
this.debugLog(
|
|
92
|
+
`getStrands(drive: ${this.listener.driveId}, listener: ${this.listener.listenerId})`,
|
|
93
|
+
);
|
|
94
|
+
|
|
64
95
|
return this.manager.getStrands(
|
|
65
96
|
this.listener.driveId,
|
|
66
97
|
this.listener.listenerId,
|
|
@@ -78,6 +109,11 @@ export class PullResponderTransmitter implements IPullResponderTransmitter {
|
|
|
78
109
|
listenerId: string,
|
|
79
110
|
revisions: ListenerRevision[],
|
|
80
111
|
): Promise<boolean> {
|
|
112
|
+
this.debugLog(
|
|
113
|
+
`processAcknowledge(drive: ${driveId}, listener: ${listenerId})`,
|
|
114
|
+
revisions,
|
|
115
|
+
);
|
|
116
|
+
|
|
81
117
|
const syncUnits = await this.manager.getListenerSyncUnitIds(
|
|
82
118
|
driveId,
|
|
83
119
|
listenerId,
|
|
@@ -113,6 +149,7 @@ export class PullResponderTransmitter implements IPullResponderTransmitter {
|
|
|
113
149
|
url: string,
|
|
114
150
|
filter: ListenerFilter,
|
|
115
151
|
): Promise<Listener["listenerId"]> {
|
|
152
|
+
staticDebugLog(`registerPullResponder(url: ${url})`, filter);
|
|
116
153
|
// graphql request to switchboard
|
|
117
154
|
const result = await requestGraphql<{
|
|
118
155
|
registerPullResponderListener: {
|
|
@@ -148,6 +185,7 @@ export class PullResponderTransmitter implements IPullResponderTransmitter {
|
|
|
148
185
|
listenerId: string,
|
|
149
186
|
options?: GetStrandsOptions, // TODO add support for since
|
|
150
187
|
): Promise<StrandUpdate[]> {
|
|
188
|
+
staticDebugLog(`pullStrands(url: ${url}, listener: ${listenerId})`);
|
|
151
189
|
const result = await requestGraphql<PullStrandsGraphQL>(
|
|
152
190
|
url,
|
|
153
191
|
gql`
|
|
@@ -214,6 +252,11 @@ export class PullResponderTransmitter implements IPullResponderTransmitter {
|
|
|
214
252
|
listenerId: string,
|
|
215
253
|
revisions: ListenerRevision[],
|
|
216
254
|
): Promise<boolean> {
|
|
255
|
+
staticDebugLog(
|
|
256
|
+
`acknowledgeStrands(url: ${url}, listener: ${listenerId})`,
|
|
257
|
+
revisions,
|
|
258
|
+
);
|
|
259
|
+
|
|
217
260
|
const result = await requestGraphql<{ acknowledge: boolean }>(
|
|
218
261
|
url,
|
|
219
262
|
gql`
|
|
@@ -248,6 +291,8 @@ export class PullResponderTransmitter implements IPullResponderTransmitter {
|
|
|
248
291
|
onRevisions?: (revisions: ListenerRevisionWithError[]) => void,
|
|
249
292
|
onAcknowledge?: (success: boolean) => void,
|
|
250
293
|
) {
|
|
294
|
+
staticDebugLog(`executePull(driveId: ${driveId}), trigger:`, trigger);
|
|
295
|
+
|
|
251
296
|
try {
|
|
252
297
|
const { url, listenerId } = trigger.data;
|
|
253
298
|
const strands = await PullResponderTransmitter.pullStrands(
|
|
@@ -330,6 +375,8 @@ export class PullResponderTransmitter implements IPullResponderTransmitter {
|
|
|
330
375
|
onRevisions?: (revisions: ListenerRevisionWithError[]) => void,
|
|
331
376
|
onAcknowledge?: (success: boolean) => void,
|
|
332
377
|
): CancelPullLoop {
|
|
378
|
+
staticDebugLog(`setupPull(drive: ${driveId}), trigger:`, trigger);
|
|
379
|
+
|
|
333
380
|
const { interval } = trigger.data;
|
|
334
381
|
let loopInterval = PULL_DRIVE_INTERVAL;
|
|
335
382
|
if (interval) {
|
|
@@ -348,6 +395,7 @@ export class PullResponderTransmitter implements IPullResponderTransmitter {
|
|
|
348
395
|
|
|
349
396
|
const executeLoop = async () => {
|
|
350
397
|
while (!isCancelled) {
|
|
398
|
+
staticDebugLog("Execute loop...");
|
|
351
399
|
await this.executePull(
|
|
352
400
|
driveId,
|
|
353
401
|
trigger,
|
|
@@ -357,6 +405,7 @@ export class PullResponderTransmitter implements IPullResponderTransmitter {
|
|
|
357
405
|
onAcknowledge,
|
|
358
406
|
);
|
|
359
407
|
await new Promise((resolve) => {
|
|
408
|
+
staticDebugLog(`Scheduling next pull in ${loopInterval} ms`);
|
|
360
409
|
timeout = setTimeout(resolve, loopInterval) as unknown as number;
|
|
361
410
|
});
|
|
362
411
|
}
|
|
@@ -377,6 +426,10 @@ export class PullResponderTransmitter implements IPullResponderTransmitter {
|
|
|
377
426
|
url: string,
|
|
378
427
|
options: Pick<RemoteDriveOptions, "pullInterval" | "pullFilter">,
|
|
379
428
|
): Promise<PullResponderTrigger> {
|
|
429
|
+
staticDebugLog(
|
|
430
|
+
`createPullResponderTrigger(drive: ${driveId}, url: ${url})`,
|
|
431
|
+
);
|
|
432
|
+
|
|
380
433
|
const { pullFilter, pullInterval } = options;
|
|
381
434
|
const listenerId = await PullResponderTransmitter.registerPullResponder(
|
|
382
435
|
driveId,
|
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
import stringify from "json-stringify-deterministic";
|
|
2
2
|
import { gql, requestGraphql } from "../../../utils/graphql";
|
|
3
3
|
import { logger } from "../../../utils/logger";
|
|
4
|
-
import {
|
|
5
|
-
IBaseDocumentDriveServer,
|
|
6
|
-
Listener,
|
|
7
|
-
ListenerRevision,
|
|
8
|
-
StrandUpdate,
|
|
9
|
-
} from "../../types";
|
|
4
|
+
import { ListenerRevision, StrandUpdate } from "../../types";
|
|
10
5
|
import { ITransmitter, StrandUpdateSource } from "./types";
|
|
11
6
|
|
|
7
|
+
const ENABLE_SYNC_DEBUG = false;
|
|
8
|
+
const SYNC_OPS_BATCH_LIMIT = 10;
|
|
9
|
+
|
|
12
10
|
export class SwitchboardPushTransmitter implements ITransmitter {
|
|
13
|
-
private drive: IBaseDocumentDriveServer;
|
|
14
|
-
private listener: Listener;
|
|
15
11
|
private targetURL: string;
|
|
12
|
+
private debugID = `[SPT #${Math.floor(Math.random() * 999)}]`;
|
|
13
|
+
|
|
14
|
+
constructor(targetURL: string) {
|
|
15
|
+
this.targetURL = targetURL;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private debugLog(...data: any[]) {
|
|
19
|
+
if (!ENABLE_SYNC_DEBUG) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
if (data.length > 0 && typeof data[0] === "string") {
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
25
|
+
console.log(`${this.debugID} ${data[0]}`, ...data.slice(1));
|
|
26
|
+
} else {
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
28
|
+
console.log(this.debugID, ...data);
|
|
29
|
+
}
|
|
21
30
|
}
|
|
22
31
|
|
|
23
32
|
async transmit(
|
|
@@ -28,6 +37,7 @@ export class SwitchboardPushTransmitter implements ITransmitter {
|
|
|
28
37
|
source.type === "trigger" &&
|
|
29
38
|
source.trigger.data?.url === this.targetURL
|
|
30
39
|
) {
|
|
40
|
+
this.debugLog(`Cutting trigger loop from ${this.targetURL}.`);
|
|
31
41
|
return strands.map((strand) => ({
|
|
32
42
|
driveId: strand.driveId,
|
|
33
43
|
documentId: strand.documentId,
|
|
@@ -38,6 +48,39 @@ export class SwitchboardPushTransmitter implements ITransmitter {
|
|
|
38
48
|
}));
|
|
39
49
|
}
|
|
40
50
|
|
|
51
|
+
const culledStrands: StrandUpdate[] = [];
|
|
52
|
+
let opsCounter = 0;
|
|
53
|
+
|
|
54
|
+
for (
|
|
55
|
+
let s = 0;
|
|
56
|
+
opsCounter <= SYNC_OPS_BATCH_LIMIT && s < strands.length;
|
|
57
|
+
s++
|
|
58
|
+
) {
|
|
59
|
+
const currentStrand = strands.at(s);
|
|
60
|
+
if (!currentStrand) {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
const newOps = Math.min(
|
|
64
|
+
SYNC_OPS_BATCH_LIMIT - opsCounter,
|
|
65
|
+
currentStrand.operations.length,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
culledStrands.push({
|
|
69
|
+
...currentStrand,
|
|
70
|
+
operations: currentStrand.operations.slice(0, newOps),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
opsCounter += newOps;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this.debugLog(
|
|
77
|
+
` Total update: [${strands.map((s) => s.operations.length).join(", ")}] operations`,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
this.debugLog(
|
|
81
|
+
`Culled update: [${culledStrands.map((s) => s.operations.length).join(", ")}] operations`,
|
|
82
|
+
);
|
|
83
|
+
|
|
41
84
|
// Send Graphql mutation to switchboard
|
|
42
85
|
try {
|
|
43
86
|
const { pushUpdates } = await requestGraphql<{
|
|
@@ -58,7 +101,7 @@ export class SwitchboardPushTransmitter implements ITransmitter {
|
|
|
58
101
|
}
|
|
59
102
|
`,
|
|
60
103
|
{
|
|
61
|
-
strands:
|
|
104
|
+
strands: culledStrands.map((strand) => ({
|
|
62
105
|
...strand,
|
|
63
106
|
operations: strand.operations.map((op) => ({
|
|
64
107
|
...op,
|
package/src/server/types.ts
CHANGED
|
@@ -22,15 +22,15 @@ import type {
|
|
|
22
22
|
} from "document-model/document";
|
|
23
23
|
import { Unsubscribe } from "nanoevents";
|
|
24
24
|
import { BaseDocumentDriveServer } from ".";
|
|
25
|
-
import {
|
|
26
|
-
IReceiver as IInternalListener,
|
|
27
|
-
IInternalTransmitter,
|
|
28
|
-
} from "./listener/transmitter/internal";
|
|
29
25
|
import { IReadModeDriveServer } from "../read-mode/types";
|
|
30
26
|
import { RunAsap } from "../utils";
|
|
31
27
|
import { IDefaultDrivesManager } from "../utils/default-drives-manager";
|
|
32
28
|
import { DriveInfo } from "../utils/graphql";
|
|
33
29
|
import { OperationError, SynchronizationUnitNotFoundError } from "./error";
|
|
30
|
+
import {
|
|
31
|
+
IReceiver as IInternalListener,
|
|
32
|
+
IInternalTransmitter,
|
|
33
|
+
} from "./listener/transmitter/internal";
|
|
34
34
|
import {
|
|
35
35
|
ITransmitter,
|
|
36
36
|
PullResponderTrigger,
|
|
@@ -105,6 +105,7 @@ export type Listener = {
|
|
|
105
105
|
system: boolean;
|
|
106
106
|
filter: ListenerFilter;
|
|
107
107
|
callInfo?: ListenerCallInfo;
|
|
108
|
+
transmitter?: ITransmitter;
|
|
108
109
|
};
|
|
109
110
|
|
|
110
111
|
export type CreateListenerInput = {
|
|
@@ -317,137 +318,150 @@ export type GetStrandsOptions = {
|
|
|
317
318
|
fromRevision?: number;
|
|
318
319
|
};
|
|
319
320
|
|
|
320
|
-
export
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
321
|
+
export type ListenerManagerOptions = {
|
|
322
|
+
sequentialUpdates?: boolean;
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
export const DefaultListenerManagerOptions = {
|
|
326
|
+
sequentialUpdates: true,
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
type PublicKeys<T> = {
|
|
330
|
+
[K in keyof T]: T extends { [P in K]: T[K] } ? K : never;
|
|
331
|
+
}[keyof T];
|
|
332
|
+
|
|
333
|
+
type PublicPart<T> = Pick<T, PublicKeys<T>>;
|
|
334
|
+
|
|
335
|
+
export interface IBaseDocumentDriveServer {
|
|
336
|
+
initialize(): Promise<Error[] | null>;
|
|
337
|
+
setDocumentModels(models: DocumentModel[]): void;
|
|
338
|
+
getDrives(): Promise<string[]>;
|
|
339
|
+
addDrive(input: DriveInput): Promise<DocumentDriveDocument>;
|
|
340
|
+
addRemoteDrive(
|
|
327
341
|
url: string,
|
|
328
342
|
options: RemoteDriveOptions,
|
|
329
343
|
): Promise<DocumentDriveDocument>;
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
344
|
+
deleteDrive(driveId: string): Promise<void>;
|
|
345
|
+
getDrive(
|
|
346
|
+
driveId: string,
|
|
333
347
|
options?: GetDocumentOptions,
|
|
334
348
|
): Promise<DocumentDriveDocument>;
|
|
335
349
|
|
|
336
|
-
|
|
350
|
+
getDriveBySlug(slug: string): Promise<DocumentDriveDocument>;
|
|
337
351
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
352
|
+
getDocuments(driveId: string): Promise<string[]>;
|
|
353
|
+
getDocument(
|
|
354
|
+
driveId: string,
|
|
355
|
+
documentId: string,
|
|
342
356
|
options?: GetDocumentOptions,
|
|
343
357
|
): Promise<Document>;
|
|
344
358
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
359
|
+
addOperation(
|
|
360
|
+
driveId: string,
|
|
361
|
+
documentId: string,
|
|
348
362
|
operation: Operation,
|
|
349
363
|
options?: AddOperationOptions,
|
|
350
364
|
): Promise<IOperationResult>;
|
|
351
365
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
366
|
+
addOperations(
|
|
367
|
+
driveId: string,
|
|
368
|
+
documentId: string,
|
|
355
369
|
operations: Operation[],
|
|
356
370
|
options?: AddOperationOptions,
|
|
357
371
|
): Promise<IOperationResult>;
|
|
358
372
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
373
|
+
queueOperation(
|
|
374
|
+
driveId: string,
|
|
375
|
+
documentId: string,
|
|
362
376
|
operation: Operation,
|
|
363
377
|
options?: AddOperationOptions,
|
|
364
378
|
): Promise<IOperationResult>;
|
|
365
379
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
380
|
+
queueOperations(
|
|
381
|
+
driveId: string,
|
|
382
|
+
documentId: string,
|
|
369
383
|
operations: Operation[],
|
|
370
384
|
options?: AddOperationOptions,
|
|
371
385
|
): Promise<IOperationResult>;
|
|
372
386
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
387
|
+
queueAction(
|
|
388
|
+
driveId: string,
|
|
389
|
+
documentId: string,
|
|
376
390
|
action: Action,
|
|
377
391
|
options?: AddOperationOptions,
|
|
378
392
|
): Promise<IOperationResult>;
|
|
379
393
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
394
|
+
queueActions(
|
|
395
|
+
driveId: string,
|
|
396
|
+
documentId: string,
|
|
383
397
|
actions: Action[],
|
|
384
398
|
options?: AddOperationOptions,
|
|
385
399
|
): Promise<IOperationResult>;
|
|
386
400
|
|
|
387
|
-
|
|
388
|
-
|
|
401
|
+
addDriveOperation(
|
|
402
|
+
driveId: string,
|
|
389
403
|
operation: Operation<DocumentDriveAction | BaseAction>,
|
|
390
404
|
options?: AddOperationOptions,
|
|
391
405
|
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
392
|
-
|
|
393
|
-
|
|
406
|
+
addDriveOperations(
|
|
407
|
+
driveId: string,
|
|
394
408
|
operations: Operation<DocumentDriveAction | BaseAction>[],
|
|
395
409
|
options?: AddOperationOptions,
|
|
396
410
|
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
397
411
|
|
|
398
|
-
|
|
399
|
-
|
|
412
|
+
queueDriveOperation(
|
|
413
|
+
driveId: string,
|
|
400
414
|
operation: Operation<DocumentDriveAction | BaseAction>,
|
|
401
415
|
options?: AddOperationOptions,
|
|
402
416
|
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
403
417
|
|
|
404
|
-
|
|
405
|
-
|
|
418
|
+
queueDriveOperations(
|
|
419
|
+
driveId: string,
|
|
406
420
|
operations: Operation<DocumentDriveAction | BaseAction>[],
|
|
407
421
|
options?: AddOperationOptions,
|
|
408
422
|
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
409
423
|
|
|
410
|
-
|
|
411
|
-
|
|
424
|
+
queueDriveAction(
|
|
425
|
+
driveId: string,
|
|
412
426
|
action: DocumentDriveAction | BaseAction,
|
|
413
427
|
options?: AddOperationOptions,
|
|
414
428
|
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
415
429
|
|
|
416
|
-
|
|
417
|
-
|
|
430
|
+
queueDriveActions(
|
|
431
|
+
driveId: string,
|
|
418
432
|
actions: Array<DocumentDriveAction | BaseAction>,
|
|
419
433
|
options?: AddOperationOptions,
|
|
420
434
|
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
421
435
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
436
|
+
addAction(
|
|
437
|
+
driveId: string,
|
|
438
|
+
documentId: string,
|
|
425
439
|
action: Action,
|
|
426
440
|
options?: AddOperationOptions,
|
|
427
441
|
): Promise<IOperationResult>;
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
442
|
+
addActions(
|
|
443
|
+
driveId: string,
|
|
444
|
+
documentId: string,
|
|
431
445
|
actions: Action[],
|
|
432
446
|
options?: AddOperationOptions,
|
|
433
447
|
): Promise<IOperationResult>;
|
|
434
448
|
|
|
435
|
-
|
|
436
|
-
|
|
449
|
+
addDriveAction(
|
|
450
|
+
driveId: string,
|
|
437
451
|
action: DocumentDriveAction | BaseAction,
|
|
438
452
|
options?: AddOperationOptions,
|
|
439
453
|
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
440
|
-
|
|
441
|
-
|
|
454
|
+
addDriveActions(
|
|
455
|
+
driveId: string,
|
|
442
456
|
actions: (DocumentDriveAction | BaseAction)[],
|
|
443
457
|
options?: AddOperationOptions,
|
|
444
458
|
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
445
459
|
|
|
446
|
-
|
|
460
|
+
getSyncStatus(
|
|
447
461
|
syncUnitId: string,
|
|
448
462
|
): SyncStatus | SynchronizationUnitNotFoundError;
|
|
449
463
|
|
|
450
|
-
|
|
464
|
+
addInternalListener(
|
|
451
465
|
driveId: string,
|
|
452
466
|
receiver: IInternalListener,
|
|
453
467
|
options: {
|
|
@@ -459,7 +473,7 @@ export abstract class AbstractDocumentDriveServer {
|
|
|
459
473
|
): Promise<IInternalTransmitter>;
|
|
460
474
|
|
|
461
475
|
/** Synchronization methods */
|
|
462
|
-
|
|
476
|
+
getSynchronizationUnits(
|
|
463
477
|
driveId: string,
|
|
464
478
|
documentId?: string[],
|
|
465
479
|
scope?: string[],
|
|
@@ -468,13 +482,13 @@ export abstract class AbstractDocumentDriveServer {
|
|
|
468
482
|
loadedDrive?: DocumentDriveDocument,
|
|
469
483
|
): Promise<SynchronizationUnit[]>;
|
|
470
484
|
|
|
471
|
-
|
|
485
|
+
getSynchronizationUnit(
|
|
472
486
|
driveId: string,
|
|
473
487
|
syncId: string,
|
|
474
488
|
loadedDrive?: DocumentDriveDocument,
|
|
475
489
|
): Promise<SynchronizationUnit | undefined>;
|
|
476
490
|
|
|
477
|
-
|
|
491
|
+
getSynchronizationUnitsIds(
|
|
478
492
|
driveId: string,
|
|
479
493
|
documentId?: string[],
|
|
480
494
|
scope?: string[],
|
|
@@ -482,7 +496,7 @@ export abstract class AbstractDocumentDriveServer {
|
|
|
482
496
|
documentType?: string[],
|
|
483
497
|
): Promise<SynchronizationUnitQuery[]>;
|
|
484
498
|
|
|
485
|
-
|
|
499
|
+
getOperationData(
|
|
486
500
|
driveId: string,
|
|
487
501
|
syncId: string,
|
|
488
502
|
filter: GetStrandsOptions,
|
|
@@ -490,117 +504,73 @@ export abstract class AbstractDocumentDriveServer {
|
|
|
490
504
|
): Promise<OperationUpdate[]>;
|
|
491
505
|
|
|
492
506
|
/** Internal methods **/
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
): Promise<Document>;
|
|
497
|
-
protected abstract deleteDocument(drive: string, id: string): Promise<void>;
|
|
498
|
-
|
|
499
|
-
protected abstract getDocumentModel(documentType: string): DocumentModel;
|
|
500
|
-
abstract getDocumentModels(): DocumentModel[];
|
|
501
|
-
|
|
502
|
-
/** Event methods **/
|
|
503
|
-
protected abstract emit<K extends keyof DriveEvents>(
|
|
504
|
-
event: K,
|
|
505
|
-
...args: Parameters<DriveEvents[K]>
|
|
506
|
-
): void;
|
|
507
|
-
abstract on<K extends keyof DriveEvents>(
|
|
508
|
-
event: K,
|
|
509
|
-
cb: DriveEvents[K],
|
|
510
|
-
): Unsubscribe;
|
|
511
|
-
|
|
512
|
-
abstract getTransmitter(
|
|
507
|
+
getDocumentModels(): DocumentModel[];
|
|
508
|
+
|
|
509
|
+
getTransmitter(
|
|
513
510
|
driveId: string,
|
|
514
511
|
listenerId: string,
|
|
515
512
|
): Promise<ITransmitter | undefined>;
|
|
516
513
|
|
|
517
|
-
|
|
514
|
+
clearStorage(): Promise<void>;
|
|
518
515
|
|
|
519
|
-
|
|
516
|
+
registerPullResponderTrigger(
|
|
520
517
|
id: string,
|
|
521
518
|
url: string,
|
|
522
519
|
options: Pick<RemoteDriveOptions, "pullFilter" | "pullInterval">,
|
|
523
520
|
): Promise<PullResponderTrigger>;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
export type ListenerManagerOptions = {
|
|
527
|
-
sequentialUpdates?: boolean;
|
|
528
|
-
};
|
|
529
|
-
|
|
530
|
-
export const DefaultListenerManagerOptions = {
|
|
531
|
-
sequentialUpdates: true,
|
|
532
|
-
};
|
|
533
|
-
|
|
534
|
-
type PublicKeys<T> = {
|
|
535
|
-
[K in keyof T]: T extends { [P in K]: T[K] } ? K : never;
|
|
536
|
-
}[keyof T];
|
|
537
|
-
|
|
538
|
-
type PublicPart<T> = Pick<T, PublicKeys<T>>;
|
|
539
521
|
|
|
540
|
-
|
|
522
|
+
on<K extends keyof DriveEvents>(event: K, cb: DriveEvents[K]): Unsubscribe;
|
|
523
|
+
}
|
|
541
524
|
|
|
542
525
|
export type IDocumentDriveServer = IBaseDocumentDriveServer &
|
|
543
526
|
IDefaultDrivesManager &
|
|
544
527
|
IReadModeDriveServer;
|
|
545
528
|
|
|
546
|
-
export
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
DocumentDriveState["id"],
|
|
552
|
-
Record<Listener["listenerId"], ITransmitter>
|
|
553
|
-
> = {};
|
|
554
|
-
|
|
555
|
-
constructor(
|
|
556
|
-
drive: IBaseDocumentDriveServer,
|
|
557
|
-
listenerState = new Map<string, Map<string, ListenerState>>(),
|
|
558
|
-
options: ListenerManagerOptions = DefaultListenerManagerOptions,
|
|
559
|
-
) {
|
|
560
|
-
this.drive = drive;
|
|
561
|
-
this.listenerState = listenerState;
|
|
562
|
-
this.options = { ...DefaultListenerManagerOptions, ...options };
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
abstract initDrive(drive: DocumentDriveDocument): Promise<void>;
|
|
566
|
-
abstract removeDrive(driveId: DocumentDriveState["id"]): Promise<void>;
|
|
567
|
-
|
|
568
|
-
abstract driveHasListeners(driveId: string): boolean;
|
|
569
|
-
abstract addListener(listener: Listener): Promise<ITransmitter>;
|
|
570
|
-
abstract removeListener(
|
|
571
|
-
driveId: string,
|
|
572
|
-
listenerId: string,
|
|
573
|
-
): Promise<boolean>;
|
|
574
|
-
abstract getListener(
|
|
575
|
-
driveId: string,
|
|
576
|
-
listenerId: string,
|
|
577
|
-
): Promise<ListenerState | undefined>;
|
|
529
|
+
export type DriveUpdateErrorHandler = (
|
|
530
|
+
error: Error,
|
|
531
|
+
driveId: string,
|
|
532
|
+
listener: ListenerState,
|
|
533
|
+
) => void;
|
|
578
534
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
listenerId: string,
|
|
582
|
-
): Promise<ITransmitter | undefined>;
|
|
535
|
+
export interface IListenerManager {
|
|
536
|
+
initialize(handler: DriveUpdateErrorHandler): Promise<void>;
|
|
583
537
|
|
|
584
|
-
|
|
538
|
+
removeDrive(driveId: DocumentDriveState["id"]): Promise<void>;
|
|
539
|
+
driveHasListeners(driveId: string): boolean;
|
|
540
|
+
|
|
541
|
+
setListener(driveId: string, listener: Listener): Promise<void>;
|
|
542
|
+
removeListener(driveId: string, listenerId: string): Promise<boolean>;
|
|
543
|
+
getListenerState(driveId: string, listenerId: string): ListenerState;
|
|
544
|
+
|
|
545
|
+
getStrands(
|
|
585
546
|
driveId: string,
|
|
586
547
|
listenerId: string,
|
|
587
548
|
options?: GetStrandsOptions,
|
|
588
549
|
): Promise<StrandUpdate[]>;
|
|
589
|
-
|
|
590
|
-
abstract updateSynchronizationRevisions(
|
|
550
|
+
updateSynchronizationRevisions(
|
|
591
551
|
driveId: string,
|
|
592
552
|
syncUnits: SynchronizationUnit[],
|
|
593
553
|
source: StrandUpdateSource,
|
|
594
554
|
willUpdate?: (listeners: Listener[]) => void,
|
|
595
555
|
onError?: (error: Error, driveId: string, listener: ListenerState) => void,
|
|
556
|
+
forceSync?: boolean,
|
|
596
557
|
): Promise<ListenerUpdate[]>;
|
|
597
|
-
|
|
598
|
-
abstract updateListenerRevision(
|
|
558
|
+
updateListenerRevision(
|
|
599
559
|
listenerId: string,
|
|
600
560
|
driveId: string,
|
|
601
561
|
syncId: string,
|
|
602
562
|
listenerRev: number,
|
|
603
563
|
): Promise<void>;
|
|
564
|
+
|
|
565
|
+
// todo: re-evaluate
|
|
566
|
+
getListenerSyncUnitIds(
|
|
567
|
+
driveId: string,
|
|
568
|
+
listenerId: string,
|
|
569
|
+
): Promise<SynchronizationUnitQuery[]>;
|
|
570
|
+
removeSyncUnits(
|
|
571
|
+
driveId: string,
|
|
572
|
+
syncUnits: Pick<SynchronizationUnit, "syncId">[],
|
|
573
|
+
): Promise<void>;
|
|
604
574
|
}
|
|
605
575
|
|
|
606
576
|
export type ListenerStatus =
|