document-drive 1.0.0-websockets → 1.0.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 +1 -0
- package/package.json +74 -88
- package/src/cache/index.ts +2 -2
- package/src/cache/memory.ts +22 -13
- package/src/cache/redis.ts +43 -16
- package/src/cache/types.ts +4 -4
- package/src/index.ts +6 -3
- package/src/queue/base.ts +276 -214
- package/src/queue/index.ts +2 -2
- package/src/queue/redis.ts +138 -127
- package/src/queue/types.ts +44 -38
- package/src/read-mode/errors.ts +19 -0
- package/src/read-mode/index.ts +125 -0
- package/src/read-mode/service.ts +207 -0
- package/src/read-mode/types.ts +108 -0
- package/src/server/error.ts +61 -26
- package/src/server/index.ts +2160 -1785
- package/src/server/listener/index.ts +2 -2
- package/src/server/listener/manager.ts +475 -437
- package/src/server/listener/transmitter/index.ts +4 -5
- package/src/server/listener/transmitter/internal.ts +77 -79
- package/src/server/listener/transmitter/pull-responder.ts +363 -329
- package/src/server/listener/transmitter/switchboard-push.ts +72 -55
- package/src/server/listener/transmitter/types.ts +19 -25
- package/src/server/types.ts +536 -349
- package/src/server/utils.ts +26 -27
- package/src/storage/base.ts +81 -0
- package/src/storage/browser.ts +233 -216
- package/src/storage/filesystem.ts +257 -256
- package/src/storage/index.ts +2 -1
- package/src/storage/memory.ts +206 -214
- package/src/storage/prisma.ts +575 -568
- package/src/storage/sequelize.ts +460 -471
- package/src/storage/types.ts +83 -67
- package/src/utils/default-drives-manager.ts +341 -0
- package/src/utils/document-helpers.ts +19 -18
- package/src/utils/graphql.ts +288 -34
- package/src/utils/index.ts +61 -59
- package/src/utils/logger.ts +39 -37
- package/src/utils/migrations.ts +58 -0
- package/src/utils/run-asap.ts +156 -0
- package/CHANGELOG.md +0 -818
- package/src/server/listener/transmitter/subscription.ts +0 -364
package/src/server/types.ts
CHANGED
|
@@ -1,420 +1,607 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
DocumentDriveAction,
|
|
3
|
+
DocumentDriveDocument,
|
|
4
|
+
DocumentDriveLocalState,
|
|
5
|
+
DocumentDriveState,
|
|
6
|
+
ListenerCallInfo,
|
|
7
|
+
ListenerFilter,
|
|
8
|
+
Trigger,
|
|
9
|
+
} from "document-model-libs/document-drive";
|
|
9
10
|
import type {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
11
|
+
Action,
|
|
12
|
+
ActionContext,
|
|
13
|
+
BaseAction,
|
|
14
|
+
CreateChildDocumentInput,
|
|
15
|
+
Document,
|
|
16
|
+
DocumentModel,
|
|
17
|
+
Operation,
|
|
18
|
+
OperationScope,
|
|
19
|
+
ReducerOptions,
|
|
20
|
+
Signal,
|
|
21
|
+
State,
|
|
22
|
+
} from "document-model/document";
|
|
23
|
+
import { Unsubscribe } from "nanoevents";
|
|
24
|
+
import { BaseDocumentDriveServer } from ".";
|
|
25
|
+
import { IReadModeDriveServer } from "../read-mode/types";
|
|
26
|
+
import { RunAsap } from "../utils";
|
|
27
|
+
import { IDefaultDrivesManager } from "../utils/default-drives-manager";
|
|
28
|
+
import { DriveInfo } from "../utils/graphql";
|
|
29
|
+
import { OperationError, SynchronizationUnitNotFoundError } from "./error";
|
|
30
|
+
import {
|
|
31
|
+
ITransmitter,
|
|
32
|
+
PullResponderTrigger,
|
|
33
|
+
StrandUpdateSource,
|
|
34
|
+
} from "./listener/transmitter/types";
|
|
35
|
+
|
|
36
|
+
export type Constructor<T = object> = new (...args: any[]) => T;
|
|
37
|
+
|
|
38
|
+
export type DocumentDriveServerConstructor =
|
|
39
|
+
Constructor<BaseDocumentDriveServer>;
|
|
40
|
+
|
|
41
|
+
// Mixin type that returns a type extending both the base class and the interface
|
|
42
|
+
export type Mixin<T extends Constructor, I> = T &
|
|
43
|
+
Constructor<InstanceType<T> & I>;
|
|
44
|
+
|
|
45
|
+
export type DocumentDriveServerMixin<I> = Mixin<
|
|
46
|
+
typeof BaseDocumentDriveServer,
|
|
47
|
+
I
|
|
48
|
+
>;
|
|
24
49
|
|
|
25
50
|
export type DriveInput = State<
|
|
26
|
-
|
|
27
|
-
|
|
51
|
+
Omit<DocumentDriveState, "__typename" | "id" | "nodes"> & { id?: string },
|
|
52
|
+
DocumentDriveLocalState
|
|
28
53
|
>;
|
|
29
54
|
|
|
55
|
+
export type RemoteDriveAccessLevel = "READ" | "WRITE";
|
|
56
|
+
|
|
30
57
|
export type RemoteDriveOptions = DocumentDriveLocalState & {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
58
|
+
// TODO make local state optional
|
|
59
|
+
pullFilter?: ListenerFilter;
|
|
60
|
+
pullInterval?: number;
|
|
61
|
+
expectedDriveInfo?: DriveInfo;
|
|
62
|
+
accessLevel?: RemoteDriveAccessLevel;
|
|
34
63
|
};
|
|
35
64
|
|
|
36
65
|
export type CreateDocumentInput = CreateChildDocumentInput;
|
|
37
66
|
|
|
38
67
|
export type SignalResult = {
|
|
39
|
-
|
|
40
|
-
|
|
68
|
+
signal: Signal;
|
|
69
|
+
result: unknown; // infer from return types on document-model
|
|
41
70
|
};
|
|
42
71
|
|
|
43
72
|
export type IOperationResult<T extends Document = Document> = {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
73
|
+
status: UpdateStatus;
|
|
74
|
+
error?: OperationError;
|
|
75
|
+
operations: Operation[];
|
|
76
|
+
document: T | undefined;
|
|
77
|
+
signals: SignalResult[];
|
|
49
78
|
};
|
|
50
79
|
|
|
51
80
|
export type SynchronizationUnit = {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
81
|
+
syncId: string;
|
|
82
|
+
driveId: string;
|
|
83
|
+
documentId: string;
|
|
84
|
+
documentType: string;
|
|
85
|
+
scope: string;
|
|
86
|
+
branch: string;
|
|
87
|
+
lastUpdated: string;
|
|
88
|
+
revision: number;
|
|
60
89
|
};
|
|
61
90
|
|
|
62
|
-
export type SynchronizationUnitQuery = Omit<
|
|
91
|
+
export type SynchronizationUnitQuery = Omit<
|
|
92
|
+
SynchronizationUnit,
|
|
93
|
+
"revision" | "lastUpdated"
|
|
94
|
+
>;
|
|
63
95
|
|
|
64
96
|
export type Listener = {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
97
|
+
driveId: string;
|
|
98
|
+
listenerId: string;
|
|
99
|
+
label?: string;
|
|
100
|
+
block: boolean;
|
|
101
|
+
system: boolean;
|
|
102
|
+
filter: ListenerFilter;
|
|
103
|
+
callInfo?: ListenerCallInfo;
|
|
72
104
|
};
|
|
73
105
|
|
|
74
106
|
export type CreateListenerInput = {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
107
|
+
driveId: string;
|
|
108
|
+
label?: string;
|
|
109
|
+
block: boolean;
|
|
110
|
+
system: boolean;
|
|
111
|
+
filter: ListenerFilter;
|
|
112
|
+
callInfo?: ListenerCallInfo;
|
|
81
113
|
};
|
|
82
114
|
|
|
83
115
|
export enum TransmitterType {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
116
|
+
Internal,
|
|
117
|
+
SwitchboardPush,
|
|
118
|
+
PullResponder,
|
|
119
|
+
SecureConnect,
|
|
120
|
+
MatrixConnect,
|
|
121
|
+
RESTWebhook,
|
|
90
122
|
}
|
|
91
123
|
|
|
92
124
|
export type ListenerRevision = {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
125
|
+
driveId: string;
|
|
126
|
+
documentId: string;
|
|
127
|
+
scope: string;
|
|
128
|
+
branch: string;
|
|
129
|
+
status: UpdateStatus;
|
|
130
|
+
revision: number;
|
|
131
|
+
error?: string;
|
|
100
132
|
};
|
|
101
133
|
|
|
102
|
-
export type ListenerRevisionWithError = Omit<ListenerRevision,
|
|
103
|
-
|
|
134
|
+
export type ListenerRevisionWithError = Omit<ListenerRevision, "error"> & {
|
|
135
|
+
error?: Error;
|
|
104
136
|
};
|
|
105
137
|
|
|
106
138
|
export type ListenerUpdate = {
|
|
107
|
-
|
|
108
|
-
|
|
139
|
+
listenerId: string;
|
|
140
|
+
listenerRevisions: ListenerRevision[];
|
|
109
141
|
};
|
|
110
142
|
|
|
111
|
-
export type UpdateStatus =
|
|
112
|
-
export type ErrorStatus = Exclude<UpdateStatus,
|
|
143
|
+
export type UpdateStatus = "SUCCESS" | "CONFLICT" | "MISSING" | "ERROR";
|
|
144
|
+
export type ErrorStatus = Exclude<UpdateStatus, "SUCCESS">;
|
|
113
145
|
|
|
114
146
|
export type OperationUpdate = {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
147
|
+
timestamp: string;
|
|
148
|
+
index: number;
|
|
149
|
+
skip: number;
|
|
150
|
+
type: string;
|
|
151
|
+
input: object;
|
|
152
|
+
hash: string;
|
|
153
|
+
context?: ActionContext;
|
|
154
|
+
id?: string;
|
|
123
155
|
};
|
|
124
156
|
|
|
125
157
|
export type StrandUpdate = {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
158
|
+
driveId: string;
|
|
159
|
+
documentId: string;
|
|
160
|
+
scope: OperationScope;
|
|
161
|
+
branch: string;
|
|
162
|
+
operations: OperationUpdate[];
|
|
131
163
|
};
|
|
132
164
|
|
|
133
|
-
export type SyncStatus =
|
|
165
|
+
export type SyncStatus = "INITIAL_SYNC" | "SYNCING" | UpdateStatus;
|
|
166
|
+
|
|
167
|
+
export type PullSyncStatus = SyncStatus;
|
|
168
|
+
export type PushSyncStatus = SyncStatus;
|
|
169
|
+
|
|
170
|
+
export type SyncUnitStatusObject = {
|
|
171
|
+
push?: PushSyncStatus;
|
|
172
|
+
pull?: PullSyncStatus;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export type AddRemoteDriveStatus =
|
|
176
|
+
| "SUCCESS"
|
|
177
|
+
| "ERROR"
|
|
178
|
+
| "PENDING"
|
|
179
|
+
| "ADDING"
|
|
180
|
+
| "ALREADY_ADDED";
|
|
134
181
|
|
|
135
182
|
export interface DriveEvents {
|
|
136
|
-
|
|
137
|
-
|
|
183
|
+
syncStatus: (
|
|
184
|
+
driveId: string,
|
|
185
|
+
status: SyncStatus,
|
|
186
|
+
error?: Error,
|
|
187
|
+
syncUnitStatus?: SyncUnitStatusObject,
|
|
188
|
+
) => void;
|
|
189
|
+
defaultRemoteDrive: (
|
|
190
|
+
status: AddRemoteDriveStatus,
|
|
191
|
+
defaultDrives: Map<string, DefaultRemoteDriveInfo>,
|
|
192
|
+
driveInput: DefaultRemoteDriveInput,
|
|
193
|
+
driveId?: string,
|
|
194
|
+
driveName?: string,
|
|
195
|
+
error?: Error,
|
|
196
|
+
) => void;
|
|
197
|
+
strandUpdate: (update: StrandUpdate) => void;
|
|
198
|
+
clientStrandsError: (
|
|
199
|
+
driveId: string,
|
|
200
|
+
trigger: Trigger,
|
|
201
|
+
status: number,
|
|
202
|
+
errorMessage: string,
|
|
203
|
+
) => void;
|
|
204
|
+
documentModels: (documentModels: DocumentModel[]) => void;
|
|
138
205
|
}
|
|
139
206
|
|
|
140
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
141
207
|
export type PartialRecord<K extends keyof any, T> = {
|
|
142
|
-
|
|
208
|
+
[P in K]?: T;
|
|
143
209
|
};
|
|
144
210
|
|
|
145
211
|
export type RevisionsFilter = PartialRecord<OperationScope, number>;
|
|
146
212
|
|
|
147
213
|
export type GetDocumentOptions = ReducerOptions & {
|
|
148
|
-
|
|
149
|
-
|
|
214
|
+
revisions?: RevisionsFilter;
|
|
215
|
+
checkHashes?: boolean;
|
|
150
216
|
};
|
|
151
217
|
|
|
152
|
-
export
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
abstract addRemoteDrive(
|
|
157
|
-
url: string,
|
|
158
|
-
options: RemoteDriveOptions
|
|
159
|
-
): Promise<DocumentDriveDocument>;
|
|
160
|
-
abstract deleteDrive(id: string): Promise<void>;
|
|
161
|
-
abstract getDrive(
|
|
162
|
-
id: string,
|
|
163
|
-
options?: GetDocumentOptions
|
|
164
|
-
): Promise<DocumentDriveDocument>;
|
|
165
|
-
|
|
166
|
-
abstract getDriveBySlug(
|
|
167
|
-
slug: string,
|
|
168
|
-
): Promise<DocumentDriveDocument>;
|
|
169
|
-
|
|
170
|
-
abstract getDocuments(drive: string): Promise<string[]>;
|
|
171
|
-
abstract getDocument(
|
|
172
|
-
drive: string,
|
|
173
|
-
id: string,
|
|
174
|
-
options?: GetDocumentOptions
|
|
175
|
-
): Promise<Document>;
|
|
176
|
-
|
|
177
|
-
abstract addOperation(
|
|
178
|
-
drive: string,
|
|
179
|
-
id: string,
|
|
180
|
-
operation: Operation,
|
|
181
|
-
forceSync?: boolean
|
|
182
|
-
): Promise<IOperationResult>;
|
|
183
|
-
|
|
184
|
-
abstract addOperations(
|
|
185
|
-
drive: string,
|
|
186
|
-
id: string,
|
|
187
|
-
operations: Operation[],
|
|
188
|
-
forceSync?: boolean
|
|
189
|
-
): Promise<IOperationResult>;
|
|
190
|
-
|
|
191
|
-
abstract queueOperation(
|
|
192
|
-
drive: string,
|
|
193
|
-
id: string,
|
|
194
|
-
operation: Operation,
|
|
195
|
-
forceSync?: boolean
|
|
196
|
-
): Promise<IOperationResult>;
|
|
197
|
-
|
|
198
|
-
abstract queueOperations(
|
|
199
|
-
drive: string,
|
|
200
|
-
id: string,
|
|
201
|
-
operations: Operation[],
|
|
202
|
-
forceSync?: boolean
|
|
203
|
-
): Promise<IOperationResult>;
|
|
204
|
-
|
|
205
|
-
abstract queueAction(
|
|
206
|
-
drive: string,
|
|
207
|
-
id: string,
|
|
208
|
-
action: Action,
|
|
209
|
-
forceSync?: boolean
|
|
210
|
-
): Promise<IOperationResult>;
|
|
211
|
-
|
|
212
|
-
abstract queueActions(
|
|
213
|
-
drive: string,
|
|
214
|
-
id: string,
|
|
215
|
-
actions: Action[],
|
|
216
|
-
forceSync?: boolean
|
|
217
|
-
): Promise<IOperationResult>;
|
|
218
|
-
|
|
219
|
-
abstract addDriveOperation(
|
|
220
|
-
drive: string,
|
|
221
|
-
operation: Operation<DocumentDriveAction | BaseAction>,
|
|
222
|
-
forceSync?: boolean
|
|
223
|
-
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
224
|
-
abstract addDriveOperations(
|
|
225
|
-
drive: string,
|
|
226
|
-
operations: Operation<DocumentDriveAction | BaseAction>[],
|
|
227
|
-
forceSync?: boolean
|
|
228
|
-
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
229
|
-
|
|
230
|
-
abstract queueDriveOperation(
|
|
231
|
-
drive: string,
|
|
232
|
-
operation: Operation<DocumentDriveAction | BaseAction>,
|
|
233
|
-
forceSync?: boolean
|
|
234
|
-
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
235
|
-
abstract queueDriveOperations(
|
|
236
|
-
drive: string,
|
|
237
|
-
operations: Operation<DocumentDriveAction | BaseAction>[],
|
|
238
|
-
forceSync?: boolean
|
|
239
|
-
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
240
|
-
|
|
241
|
-
abstract queueDriveAction(
|
|
242
|
-
drive: string,
|
|
243
|
-
action: DocumentDriveAction | BaseAction,
|
|
244
|
-
forceSync?: boolean
|
|
245
|
-
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
246
|
-
|
|
247
|
-
abstract queueDriveActions(
|
|
248
|
-
drive: string,
|
|
249
|
-
actions: Array<DocumentDriveAction | BaseAction>,
|
|
250
|
-
forceSync?: boolean
|
|
251
|
-
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
252
|
-
|
|
253
|
-
abstract addAction(
|
|
254
|
-
drive: string,
|
|
255
|
-
id: string,
|
|
256
|
-
action: Action,
|
|
257
|
-
forceSync?: boolean
|
|
258
|
-
): Promise<IOperationResult>;
|
|
259
|
-
abstract addActions(
|
|
260
|
-
drive: string,
|
|
261
|
-
id: string,
|
|
262
|
-
actions: Action[],
|
|
263
|
-
forceSync?: boolean
|
|
264
|
-
): Promise<IOperationResult>;
|
|
265
|
-
|
|
266
|
-
abstract addDriveAction(
|
|
267
|
-
drive: string,
|
|
268
|
-
action: DocumentDriveAction | BaseAction,
|
|
269
|
-
forceSync?: boolean
|
|
270
|
-
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
271
|
-
abstract addDriveActions(
|
|
272
|
-
drive: string,
|
|
273
|
-
actions: (DocumentDriveAction | BaseAction)[],
|
|
274
|
-
forceSync?: boolean
|
|
275
|
-
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
276
|
-
|
|
277
|
-
abstract getSyncStatus(drive: string): SyncStatus;
|
|
278
|
-
|
|
279
|
-
/** Synchronization methods */
|
|
280
|
-
abstract getSynchronizationUnits(
|
|
281
|
-
driveId: string,
|
|
282
|
-
documentId?: string[],
|
|
283
|
-
scope?: string[],
|
|
284
|
-
branch?: string[],
|
|
285
|
-
documentType?: string[]
|
|
286
|
-
): Promise<SynchronizationUnit[]>;
|
|
287
|
-
|
|
288
|
-
abstract getSynchronizationUnit(
|
|
289
|
-
driveId: string,
|
|
290
|
-
syncId: string
|
|
291
|
-
): Promise<SynchronizationUnit | undefined>;
|
|
292
|
-
|
|
293
|
-
abstract getSynchronizationUnitsIds(
|
|
294
|
-
driveId: string,
|
|
295
|
-
documentId?: string[],
|
|
296
|
-
scope?: string[],
|
|
297
|
-
branch?: string[],
|
|
298
|
-
documentType?: string[]
|
|
299
|
-
): Promise<SynchronizationUnitQuery[]>;
|
|
300
|
-
|
|
301
|
-
abstract getOperationData(
|
|
302
|
-
driveId: string,
|
|
303
|
-
syncId: string,
|
|
304
|
-
filter: {
|
|
305
|
-
since?: string;
|
|
306
|
-
fromRevision?: number;
|
|
307
|
-
}
|
|
308
|
-
): Promise<OperationUpdate[]>;
|
|
309
|
-
|
|
310
|
-
/** Internal methods **/
|
|
311
|
-
protected abstract createDocument(
|
|
312
|
-
drive: string,
|
|
313
|
-
document: CreateDocumentInput
|
|
314
|
-
): Promise<Document>;
|
|
315
|
-
protected abstract deleteDocument(drive: string, id: string): Promise<void>;
|
|
316
|
-
|
|
317
|
-
/** Event methods **/
|
|
318
|
-
protected abstract emit<K extends keyof DriveEvents>(
|
|
319
|
-
this: this,
|
|
320
|
-
event: K,
|
|
321
|
-
...args: Parameters<DriveEvents[K]>
|
|
322
|
-
): void;
|
|
323
|
-
abstract on<K extends keyof DriveEvents>(
|
|
324
|
-
this: this,
|
|
325
|
-
event: K,
|
|
326
|
-
cb: DriveEvents[K]
|
|
327
|
-
): Unsubscribe;
|
|
328
|
-
|
|
329
|
-
abstract getTransmitter(
|
|
330
|
-
driveId: string,
|
|
331
|
-
listenerId: string
|
|
332
|
-
): Promise<ITransmitter | undefined>;
|
|
333
|
-
|
|
334
|
-
abstract clearStorage(): Promise<void>;
|
|
335
|
-
}
|
|
218
|
+
export type AddOperationOptions = {
|
|
219
|
+
forceSync?: boolean;
|
|
220
|
+
source: StrandUpdateSource;
|
|
221
|
+
};
|
|
336
222
|
|
|
337
|
-
export
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
223
|
+
export type DefaultRemoteDriveInput = {
|
|
224
|
+
url: string;
|
|
225
|
+
options: RemoteDriveOptions;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
export type DefaultRemoteDriveInfo = DefaultRemoteDriveInput & {
|
|
229
|
+
status: AddRemoteDriveStatus;
|
|
230
|
+
metadata?: DriveInfo;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
export type RemoveDriveStrategy = "remove" | "detach";
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Options for removing old remote drives.
|
|
237
|
+
*
|
|
238
|
+
* Allows specifying different strategies for handling old remote drives:
|
|
239
|
+
*
|
|
240
|
+
* - `remove-all`: Remove all remote drives.
|
|
241
|
+
* - `preserve-all`: Preserve all remote drives (this is the default behavior).
|
|
242
|
+
* - `remove-by-id`: Remove the remote drives specified by their IDs.
|
|
243
|
+
* - `remove-by-url`: Remove the remote drives specified by their URLs.
|
|
244
|
+
* - `preserve-by-id`: Preserve remote drives by their IDs and remove the rest.
|
|
245
|
+
* - `preserve-by-url`: Preserve remote drives by their URLs and remove the rest.
|
|
246
|
+
* - `detach-by-id`: Detach remote drives by their IDs (changes the remote drive to a local drive).
|
|
247
|
+
* - `detach-by-url`: Detach remote drives by their URLs (changes the remote drive to a local drive).
|
|
248
|
+
* - `preserve-by-id-and-detach`: Preserve the remote drives specified by their IDs and detach the rest.
|
|
249
|
+
* - `preserve-by-url-and-detach`: Preserve the remote drives specified by their URLs and detach the rest.
|
|
250
|
+
*
|
|
251
|
+
* Each strategy is represented by an object with a `strategy` property and,
|
|
252
|
+
* depending on the strategy, additional properties such as `ids` or `urls`.
|
|
253
|
+
*/
|
|
254
|
+
export type RemoveOldRemoteDrivesOption =
|
|
255
|
+
| {
|
|
256
|
+
strategy: "remove-all";
|
|
257
|
+
}
|
|
258
|
+
| {
|
|
259
|
+
strategy: "preserve-all";
|
|
260
|
+
}
|
|
261
|
+
| {
|
|
262
|
+
strategy: "remove-by-id";
|
|
263
|
+
ids: string[];
|
|
264
|
+
}
|
|
265
|
+
| {
|
|
266
|
+
strategy: "remove-by-url";
|
|
267
|
+
urls: string[];
|
|
351
268
|
}
|
|
269
|
+
| {
|
|
270
|
+
strategy: "preserve-by-id";
|
|
271
|
+
ids: string[];
|
|
272
|
+
}
|
|
273
|
+
| {
|
|
274
|
+
strategy: "preserve-by-url";
|
|
275
|
+
urls: string[];
|
|
276
|
+
}
|
|
277
|
+
| {
|
|
278
|
+
strategy: "detach-by-id";
|
|
279
|
+
ids: string[];
|
|
280
|
+
}
|
|
281
|
+
| {
|
|
282
|
+
strategy: "detach-by-url";
|
|
283
|
+
urls: string[];
|
|
284
|
+
}
|
|
285
|
+
| {
|
|
286
|
+
strategy: "preserve-by-id-and-detach";
|
|
287
|
+
ids: string[];
|
|
288
|
+
}
|
|
289
|
+
| {
|
|
290
|
+
strategy: "preserve-by-url-and-detach";
|
|
291
|
+
urls: string[];
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
export type DocumentDriveServerOptions = {
|
|
295
|
+
defaultDrives: {
|
|
296
|
+
loadOnInit?: boolean; // defaults to true
|
|
297
|
+
remoteDrives?: Array<DefaultRemoteDriveInput>;
|
|
298
|
+
removeOldRemoteDrives?: RemoveOldRemoteDrivesOption;
|
|
299
|
+
};
|
|
300
|
+
/* method to queue heavy tasks that might block the event loop.
|
|
301
|
+
* If set to null then it will queued as micro task.
|
|
302
|
+
* Defaults to the most appropriate method according to the system
|
|
303
|
+
*/
|
|
304
|
+
taskQueueMethod?: RunAsap.RunAsap<unknown> | null;
|
|
305
|
+
listenerManager?: ListenerManagerOptions;
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
export type GetStrandsOptions = {
|
|
309
|
+
limit?: number;
|
|
310
|
+
since?: string;
|
|
311
|
+
fromRevision?: number;
|
|
312
|
+
};
|
|
352
313
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
314
|
+
export abstract class AbstractDocumentDriveServer {
|
|
315
|
+
/** Public methods **/
|
|
316
|
+
abstract initialize(): Promise<Error[] | null>;
|
|
317
|
+
abstract setDocumentModels(models: DocumentModel[]): void;
|
|
318
|
+
abstract getDrives(): Promise<string[]>;
|
|
319
|
+
abstract addDrive(drive: DriveInput): Promise<DocumentDriveDocument>;
|
|
320
|
+
abstract addRemoteDrive(
|
|
321
|
+
url: string,
|
|
322
|
+
options: RemoteDriveOptions,
|
|
323
|
+
): Promise<DocumentDriveDocument>;
|
|
324
|
+
abstract deleteDrive(id: string): Promise<void>;
|
|
325
|
+
abstract getDrive(
|
|
326
|
+
id: string,
|
|
327
|
+
options?: GetDocumentOptions,
|
|
328
|
+
): Promise<DocumentDriveDocument>;
|
|
329
|
+
|
|
330
|
+
abstract getDriveBySlug(slug: string): Promise<DocumentDriveDocument>;
|
|
331
|
+
|
|
332
|
+
abstract getDocuments(drive: string): Promise<string[]>;
|
|
333
|
+
abstract getDocument(
|
|
334
|
+
drive: string,
|
|
335
|
+
id: string,
|
|
336
|
+
options?: GetDocumentOptions,
|
|
337
|
+
): Promise<Document>;
|
|
338
|
+
|
|
339
|
+
abstract addOperation(
|
|
340
|
+
drive: string,
|
|
341
|
+
id: string,
|
|
342
|
+
operation: Operation,
|
|
343
|
+
options?: AddOperationOptions,
|
|
344
|
+
): Promise<IOperationResult>;
|
|
345
|
+
|
|
346
|
+
abstract addOperations(
|
|
347
|
+
drive: string,
|
|
348
|
+
id: string,
|
|
349
|
+
operations: Operation[],
|
|
350
|
+
options?: AddOperationOptions,
|
|
351
|
+
): Promise<IOperationResult>;
|
|
352
|
+
|
|
353
|
+
abstract queueOperation(
|
|
354
|
+
drive: string,
|
|
355
|
+
id: string,
|
|
356
|
+
operation: Operation,
|
|
357
|
+
options?: AddOperationOptions,
|
|
358
|
+
): Promise<IOperationResult>;
|
|
359
|
+
|
|
360
|
+
abstract queueOperations(
|
|
361
|
+
drive: string,
|
|
362
|
+
id: string,
|
|
363
|
+
operations: Operation[],
|
|
364
|
+
options?: AddOperationOptions,
|
|
365
|
+
): Promise<IOperationResult>;
|
|
366
|
+
|
|
367
|
+
abstract queueAction(
|
|
368
|
+
drive: string,
|
|
369
|
+
id: string,
|
|
370
|
+
action: Action,
|
|
371
|
+
options?: AddOperationOptions,
|
|
372
|
+
): Promise<IOperationResult>;
|
|
373
|
+
|
|
374
|
+
abstract queueActions(
|
|
375
|
+
drive: string,
|
|
376
|
+
id: string,
|
|
377
|
+
actions: Action[],
|
|
378
|
+
options?: AddOperationOptions,
|
|
379
|
+
): Promise<IOperationResult>;
|
|
380
|
+
|
|
381
|
+
abstract addDriveOperation(
|
|
382
|
+
drive: string,
|
|
383
|
+
operation: Operation<DocumentDriveAction | BaseAction>,
|
|
384
|
+
options?: AddOperationOptions,
|
|
385
|
+
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
386
|
+
abstract addDriveOperations(
|
|
387
|
+
drive: string,
|
|
388
|
+
operations: Operation<DocumentDriveAction | BaseAction>[],
|
|
389
|
+
options?: AddOperationOptions,
|
|
390
|
+
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
391
|
+
|
|
392
|
+
abstract queueDriveOperation(
|
|
393
|
+
drive: string,
|
|
394
|
+
operation: Operation<DocumentDriveAction | BaseAction>,
|
|
395
|
+
options?: AddOperationOptions,
|
|
396
|
+
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
397
|
+
|
|
398
|
+
abstract queueDriveOperations(
|
|
399
|
+
drive: string,
|
|
400
|
+
operations: Operation<DocumentDriveAction | BaseAction>[],
|
|
401
|
+
options?: AddOperationOptions,
|
|
402
|
+
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
403
|
+
|
|
404
|
+
abstract queueDriveAction(
|
|
405
|
+
drive: string,
|
|
406
|
+
action: DocumentDriveAction | BaseAction,
|
|
407
|
+
options?: AddOperationOptions,
|
|
408
|
+
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
409
|
+
|
|
410
|
+
abstract queueDriveActions(
|
|
411
|
+
drive: string,
|
|
412
|
+
actions: Array<DocumentDriveAction | BaseAction>,
|
|
413
|
+
options?: AddOperationOptions,
|
|
414
|
+
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
415
|
+
|
|
416
|
+
abstract addAction(
|
|
417
|
+
drive: string,
|
|
418
|
+
id: string,
|
|
419
|
+
action: Action,
|
|
420
|
+
options?: AddOperationOptions,
|
|
421
|
+
): Promise<IOperationResult>;
|
|
422
|
+
abstract addActions(
|
|
423
|
+
drive: string,
|
|
424
|
+
id: string,
|
|
425
|
+
actions: Action[],
|
|
426
|
+
options?: AddOperationOptions,
|
|
427
|
+
): Promise<IOperationResult>;
|
|
428
|
+
|
|
429
|
+
abstract addDriveAction(
|
|
430
|
+
drive: string,
|
|
431
|
+
action: DocumentDriveAction | BaseAction,
|
|
432
|
+
options?: AddOperationOptions,
|
|
433
|
+
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
434
|
+
abstract addDriveActions(
|
|
435
|
+
drive: string,
|
|
436
|
+
actions: (DocumentDriveAction | BaseAction)[],
|
|
437
|
+
options?: AddOperationOptions,
|
|
438
|
+
): Promise<IOperationResult<DocumentDriveDocument>>;
|
|
439
|
+
|
|
440
|
+
abstract getSyncStatus(
|
|
441
|
+
syncUnitId: string,
|
|
442
|
+
): SyncStatus | SynchronizationUnitNotFoundError;
|
|
443
|
+
|
|
444
|
+
/** Synchronization methods */
|
|
445
|
+
abstract getSynchronizationUnits(
|
|
446
|
+
driveId: string,
|
|
447
|
+
documentId?: string[],
|
|
448
|
+
scope?: string[],
|
|
449
|
+
branch?: string[],
|
|
450
|
+
documentType?: string[],
|
|
451
|
+
loadedDrive?: DocumentDriveDocument,
|
|
452
|
+
): Promise<SynchronizationUnit[]>;
|
|
453
|
+
|
|
454
|
+
abstract getSynchronizationUnit(
|
|
455
|
+
driveId: string,
|
|
456
|
+
syncId: string,
|
|
457
|
+
loadedDrive?: DocumentDriveDocument,
|
|
458
|
+
): Promise<SynchronizationUnit | undefined>;
|
|
459
|
+
|
|
460
|
+
abstract getSynchronizationUnitsIds(
|
|
461
|
+
driveId: string,
|
|
462
|
+
documentId?: string[],
|
|
463
|
+
scope?: string[],
|
|
464
|
+
branch?: string[],
|
|
465
|
+
documentType?: string[],
|
|
466
|
+
): Promise<SynchronizationUnitQuery[]>;
|
|
467
|
+
|
|
468
|
+
abstract getOperationData(
|
|
469
|
+
driveId: string,
|
|
470
|
+
syncId: string,
|
|
471
|
+
filter: GetStrandsOptions,
|
|
472
|
+
loadedDrive?: DocumentDriveDocument,
|
|
473
|
+
): Promise<OperationUpdate[]>;
|
|
474
|
+
|
|
475
|
+
/** Internal methods **/
|
|
476
|
+
protected abstract createDocument(
|
|
477
|
+
drive: string,
|
|
478
|
+
document: CreateDocumentInput,
|
|
479
|
+
): Promise<Document>;
|
|
480
|
+
protected abstract deleteDocument(drive: string, id: string): Promise<void>;
|
|
481
|
+
|
|
482
|
+
protected abstract getDocumentModel(documentType: string): DocumentModel;
|
|
483
|
+
|
|
484
|
+
/** Event methods **/
|
|
485
|
+
protected abstract emit<K extends keyof DriveEvents>(
|
|
486
|
+
this: this,
|
|
487
|
+
event: K,
|
|
488
|
+
...args: Parameters<DriveEvents[K]>
|
|
489
|
+
): void;
|
|
490
|
+
abstract on<K extends keyof DriveEvents>(
|
|
491
|
+
this: this,
|
|
492
|
+
event: K,
|
|
493
|
+
cb: DriveEvents[K],
|
|
494
|
+
): Unsubscribe;
|
|
495
|
+
|
|
496
|
+
abstract getTransmitter(
|
|
497
|
+
driveId: string,
|
|
498
|
+
listenerId: string,
|
|
499
|
+
): Promise<ITransmitter | undefined>;
|
|
500
|
+
|
|
501
|
+
abstract clearStorage(): Promise<void>;
|
|
502
|
+
|
|
503
|
+
abstract registerPullResponderTrigger(
|
|
504
|
+
id: string,
|
|
505
|
+
url: string,
|
|
506
|
+
options: Pick<RemoteDriveOptions, "pullFilter" | "pullInterval">,
|
|
507
|
+
): Promise<PullResponderTrigger>;
|
|
393
508
|
}
|
|
394
509
|
|
|
395
|
-
export type
|
|
396
|
-
|
|
397
|
-
|
|
510
|
+
export type ListenerManagerOptions = {
|
|
511
|
+
sequentialUpdates?: boolean;
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
export const DefaultListenerManagerOptions = {
|
|
515
|
+
sequentialUpdates: true,
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
export type IBaseDocumentDriveServer = Pick<
|
|
519
|
+
AbstractDocumentDriveServer,
|
|
520
|
+
keyof AbstractDocumentDriveServer
|
|
398
521
|
>;
|
|
399
522
|
|
|
523
|
+
export type IDocumentDriveServer = IBaseDocumentDriveServer &
|
|
524
|
+
IDefaultDrivesManager &
|
|
525
|
+
IReadModeDriveServer;
|
|
526
|
+
|
|
527
|
+
export abstract class BaseListenerManager {
|
|
528
|
+
protected drive: IBaseDocumentDriveServer;
|
|
529
|
+
protected listenerState = new Map<string, Map<string, ListenerState>>();
|
|
530
|
+
protected options: ListenerManagerOptions;
|
|
531
|
+
protected transmitters: Record<
|
|
532
|
+
DocumentDriveState["id"],
|
|
533
|
+
Record<Listener["listenerId"], ITransmitter>
|
|
534
|
+
> = {};
|
|
535
|
+
|
|
536
|
+
constructor(
|
|
537
|
+
drive: IBaseDocumentDriveServer,
|
|
538
|
+
listenerState = new Map<string, Map<string, ListenerState>>(),
|
|
539
|
+
options: ListenerManagerOptions = DefaultListenerManagerOptions,
|
|
540
|
+
) {
|
|
541
|
+
this.drive = drive;
|
|
542
|
+
this.listenerState = listenerState;
|
|
543
|
+
this.options = { ...DefaultListenerManagerOptions, ...options };
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
abstract initDrive(drive: DocumentDriveDocument): Promise<void>;
|
|
547
|
+
abstract removeDrive(driveId: DocumentDriveState["id"]): Promise<void>;
|
|
548
|
+
|
|
549
|
+
abstract driveHasListeners(driveId: string): boolean;
|
|
550
|
+
abstract addListener(listener: Listener): Promise<ITransmitter>;
|
|
551
|
+
abstract removeListener(
|
|
552
|
+
driveId: string,
|
|
553
|
+
listenerId: string,
|
|
554
|
+
): Promise<boolean>;
|
|
555
|
+
abstract getListener(
|
|
556
|
+
driveId: string,
|
|
557
|
+
listenerId: string,
|
|
558
|
+
): Promise<ListenerState | undefined>;
|
|
559
|
+
|
|
560
|
+
abstract getTransmitter(
|
|
561
|
+
driveId: string,
|
|
562
|
+
listenerId: string,
|
|
563
|
+
): Promise<ITransmitter | undefined>;
|
|
564
|
+
|
|
565
|
+
abstract getStrands(
|
|
566
|
+
driveId: string,
|
|
567
|
+
listenerId: string,
|
|
568
|
+
options?: GetStrandsOptions,
|
|
569
|
+
): Promise<StrandUpdate[]>;
|
|
570
|
+
|
|
571
|
+
abstract updateSynchronizationRevisions(
|
|
572
|
+
driveId: string,
|
|
573
|
+
syncUnits: SynchronizationUnit[],
|
|
574
|
+
source: StrandUpdateSource,
|
|
575
|
+
willUpdate?: (listeners: Listener[]) => void,
|
|
576
|
+
onError?: (error: Error, driveId: string, listener: ListenerState) => void,
|
|
577
|
+
): Promise<ListenerUpdate[]>;
|
|
578
|
+
|
|
579
|
+
abstract updateListenerRevision(
|
|
580
|
+
listenerId: string,
|
|
581
|
+
driveId: string,
|
|
582
|
+
syncId: string,
|
|
583
|
+
listenerRev: number,
|
|
584
|
+
): Promise<void>;
|
|
585
|
+
}
|
|
586
|
+
|
|
400
587
|
export type ListenerStatus =
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
588
|
+
| "CREATED"
|
|
589
|
+
| "PENDING"
|
|
590
|
+
| "SUCCESS"
|
|
591
|
+
| "MISSING"
|
|
592
|
+
| "CONFLICT"
|
|
593
|
+
| "ERROR";
|
|
407
594
|
|
|
408
595
|
export interface ListenerState {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
596
|
+
driveId: string;
|
|
597
|
+
block: boolean;
|
|
598
|
+
pendingTimeout: string;
|
|
599
|
+
listener: Listener;
|
|
600
|
+
syncUnits: Map<SynchronizationUnit["syncId"], SyncronizationUnitState>;
|
|
601
|
+
listenerStatus: ListenerStatus;
|
|
415
602
|
}
|
|
416
603
|
|
|
417
604
|
export interface SyncronizationUnitState {
|
|
418
|
-
|
|
419
|
-
|
|
605
|
+
listenerRev: number;
|
|
606
|
+
lastUpdated: string;
|
|
420
607
|
}
|