document-drive 1.0.0-experimental.8 → 1.0.0-websockets

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.
@@ -14,6 +14,7 @@ import type {
14
14
  Document,
15
15
  Operation,
16
16
  OperationScope,
17
+ ReducerOptions,
17
18
  Signal,
18
19
  State
19
20
  } from 'document-model/document';
@@ -58,6 +59,8 @@ export type SynchronizationUnit = {
58
59
  revision: number;
59
60
  };
60
61
 
62
+ export type SynchronizationUnitQuery = Omit<SynchronizationUnit, "revision" | "lastUpdated">;
63
+
61
64
  export type Listener = {
62
65
  driveId: string;
63
66
  listenerId: string;
@@ -116,6 +119,7 @@ export type OperationUpdate = {
116
119
  input: object;
117
120
  hash: string;
118
121
  context?: ActionContext;
122
+ id?: string;
119
123
  };
120
124
 
121
125
  export type StrandUpdate = {
@@ -140,7 +144,7 @@ export type PartialRecord<K extends keyof any, T> = {
140
144
 
141
145
  export type RevisionsFilter = PartialRecord<OperationScope, number>;
142
146
 
143
- export type GetDocumentOptions = {
147
+ export type GetDocumentOptions = ReducerOptions & {
144
148
  revisions?: RevisionsFilter;
145
149
  checkHashes?: boolean;
146
150
  };
@@ -159,6 +163,10 @@ export abstract class BaseDocumentDriveServer {
159
163
  options?: GetDocumentOptions
160
164
  ): Promise<DocumentDriveDocument>;
161
165
 
166
+ abstract getDriveBySlug(
167
+ slug: string,
168
+ ): Promise<DocumentDriveDocument>;
169
+
162
170
  abstract getDocuments(drive: string): Promise<string[]>;
163
171
  abstract getDocument(
164
172
  drive: string,
@@ -172,6 +180,7 @@ export abstract class BaseDocumentDriveServer {
172
180
  operation: Operation,
173
181
  forceSync?: boolean
174
182
  ): Promise<IOperationResult>;
183
+
175
184
  abstract addOperations(
176
185
  drive: string,
177
186
  id: string,
@@ -185,6 +194,7 @@ export abstract class BaseDocumentDriveServer {
185
194
  operation: Operation,
186
195
  forceSync?: boolean
187
196
  ): Promise<IOperationResult>;
197
+
188
198
  abstract queueOperations(
189
199
  drive: string,
190
200
  id: string,
@@ -192,6 +202,20 @@ export abstract class BaseDocumentDriveServer {
192
202
  forceSync?: boolean
193
203
  ): Promise<IOperationResult>;
194
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
+
195
219
  abstract addDriveOperation(
196
220
  drive: string,
197
221
  operation: Operation<DocumentDriveAction | BaseAction>,
@@ -214,24 +238,40 @@ export abstract class BaseDocumentDriveServer {
214
238
  forceSync?: boolean
215
239
  ): Promise<IOperationResult<DocumentDriveDocument>>;
216
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
+
217
253
  abstract addAction(
218
254
  drive: string,
219
255
  id: string,
220
- action: Action
256
+ action: Action,
257
+ forceSync?: boolean
221
258
  ): Promise<IOperationResult>;
222
259
  abstract addActions(
223
260
  drive: string,
224
261
  id: string,
225
- actions: Action[]
262
+ actions: Action[],
263
+ forceSync?: boolean
226
264
  ): Promise<IOperationResult>;
227
265
 
228
266
  abstract addDriveAction(
229
267
  drive: string,
230
- action: DocumentDriveAction | BaseAction
268
+ action: DocumentDriveAction | BaseAction,
269
+ forceSync?: boolean
231
270
  ): Promise<IOperationResult<DocumentDriveDocument>>;
232
271
  abstract addDriveActions(
233
272
  drive: string,
234
- actions: (DocumentDriveAction | BaseAction)[]
273
+ actions: (DocumentDriveAction | BaseAction)[],
274
+ forceSync?: boolean
235
275
  ): Promise<IOperationResult<DocumentDriveDocument>>;
236
276
 
237
277
  abstract getSyncStatus(drive: string): SyncStatus;
@@ -248,7 +288,15 @@ export abstract class BaseDocumentDriveServer {
248
288
  abstract getSynchronizationUnit(
249
289
  driveId: string,
250
290
  syncId: string
251
- ): Promise<SynchronizationUnit>;
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[]>;
252
300
 
253
301
  abstract getOperationData(
254
302
  driveId: string,
@@ -303,6 +351,7 @@ export abstract class BaseListenerManager {
303
351
  }
304
352
 
305
353
  abstract initDrive(drive: DocumentDriveDocument): Promise<void>;
354
+ abstract removeDrive(driveId: DocumentDriveState["id"]): Promise<void>;
306
355
 
307
356
  abstract addListener(listener: Listener): Promise<ITransmitter>;
308
357
  abstract removeListener(
@@ -3,10 +3,15 @@ import {
3
3
  BaseAction,
4
4
  Document,
5
5
  DocumentHeader,
6
- Operation
6
+ Operation,
7
+ OperationScope
7
8
  } from 'document-model/document';
8
- import { mergeOperations } from '..';
9
- import { DocumentDriveStorage, DocumentStorage, IDriveStorage } from './types';
9
+ import { mergeOperations, type SynchronizationUnitQuery } from '..';
10
+ import {
11
+ DocumentDriveStorage,
12
+ DocumentStorage,
13
+ IDriveStorage,
14
+ } from './types';
10
15
 
11
16
  export class BrowserStorage implements IDriveStorage {
12
17
  private db: Promise<LocalForage>;
@@ -18,7 +23,9 @@ export class BrowserStorage implements IDriveStorage {
18
23
  constructor(namespace?: string) {
19
24
  this.db = import('localforage').then(localForage =>
20
25
  localForage.default.createInstance({
21
- name: namespace ? `${namespace}:${BrowserStorage.DBName}` : BrowserStorage.DBName
26
+ name: namespace
27
+ ? `${namespace}:${BrowserStorage.DBName}`
28
+ : BrowserStorage.DBName
22
29
  })
23
30
  );
24
31
  }
@@ -68,7 +75,7 @@ export class BrowserStorage implements IDriveStorage {
68
75
  drive: string,
69
76
  id: string,
70
77
  operations: Operation[],
71
- header: DocumentHeader,
78
+ header: DocumentHeader
72
79
  ): Promise<void> {
73
80
  const document = await this.getDocument(drive, id);
74
81
  if (!document) {
@@ -154,4 +161,61 @@ export class BrowserStorage implements IDriveStorage {
154
161
  });
155
162
  return;
156
163
  }
164
+
165
+ async getSynchronizationUnitsRevision(
166
+ units: SynchronizationUnitQuery[]
167
+ ): Promise<
168
+ {
169
+ driveId: string;
170
+ documentId: string;
171
+ scope: string;
172
+ branch: string;
173
+ lastUpdated: string;
174
+ revision: number;
175
+ }[]
176
+ > {
177
+ const results = await Promise.allSettled(
178
+ units.map(async unit => {
179
+ try {
180
+ const document = await (unit.documentId
181
+ ? this.getDocument(unit.driveId, unit.documentId)
182
+ : this.getDrive(unit.driveId));
183
+ if (!document) {
184
+ return undefined;
185
+ }
186
+ const operation =
187
+ document.operations[unit.scope as OperationScope]?.at(
188
+ -1
189
+ );
190
+ if (operation) {
191
+ return {
192
+ driveId: unit.driveId,
193
+ documentId: unit.documentId,
194
+ scope: unit.scope,
195
+ branch: unit.branch,
196
+ lastUpdated: operation.timestamp,
197
+ revision: operation.index
198
+ };
199
+ }
200
+ } catch {
201
+ return undefined;
202
+ }
203
+ })
204
+ );
205
+ return results.reduce<
206
+ {
207
+ driveId: string;
208
+ documentId: string;
209
+ scope: string;
210
+ branch: string;
211
+ lastUpdated: string;
212
+ revision: number;
213
+ }[]
214
+ >((acc, curr) => {
215
+ if (curr.status === 'fulfilled' && curr.value !== undefined) {
216
+ acc.push(curr.value);
217
+ }
218
+ return acc;
219
+ }, []);
220
+ }
157
221
  }
@@ -1,5 +1,5 @@
1
1
  import { DocumentDriveAction } from 'document-model-libs/document-drive';
2
- import { BaseAction, DocumentHeader, Operation } from 'document-model/document';
2
+ import { BaseAction, DocumentHeader, Operation, OperationScope } from 'document-model/document';
3
3
  import type { Dirent } from 'fs';
4
4
  import {
5
5
  existsSync,
@@ -12,7 +12,8 @@ import fs from 'fs/promises';
12
12
  import stringify from 'json-stringify-deterministic';
13
13
  import path from 'path';
14
14
  import sanitize from 'sanitize-filename';
15
- import { mergeOperations } from '..';
15
+ import type { SynchronizationUnitQuery } from '../server/types';
16
+ import { mergeOperations } from '../utils';
16
17
  import { DocumentDriveStorage, DocumentStorage, IDriveStorage } from './types';
17
18
 
18
19
  type FSError = {
@@ -235,4 +236,61 @@ export class FilesystemStorage implements IDriveStorage {
235
236
  operations: mergedOperations
236
237
  });
237
238
  }
239
+
240
+ async getSynchronizationUnitsRevision(
241
+ units: SynchronizationUnitQuery[]
242
+ ): Promise<
243
+ {
244
+ driveId: string;
245
+ documentId: string;
246
+ scope: string;
247
+ branch: string;
248
+ lastUpdated: string;
249
+ revision: number;
250
+ }[]
251
+ > {
252
+ const results = await Promise.allSettled(
253
+ units.map(async unit => {
254
+ try {
255
+ const document = await (unit.documentId
256
+ ? this.getDocument(unit.driveId, unit.documentId)
257
+ : this.getDrive(unit.driveId));
258
+ if (!document) {
259
+ return undefined;
260
+ }
261
+ const operation =
262
+ document.operations[unit.scope as OperationScope]?.at(
263
+ -1
264
+ );
265
+ if (operation) {
266
+ return {
267
+ driveId: unit.driveId,
268
+ documentId: unit.documentId,
269
+ scope: unit.scope,
270
+ branch: unit.branch,
271
+ lastUpdated: operation.timestamp,
272
+ revision: operation.index
273
+ };
274
+ }
275
+ } catch {
276
+ return undefined;
277
+ }
278
+ })
279
+ );
280
+ return results.reduce<
281
+ {
282
+ driveId: string;
283
+ documentId: string;
284
+ scope: string;
285
+ branch: string;
286
+ lastUpdated: string;
287
+ revision: number;
288
+ }[]
289
+ >((acc, curr) => {
290
+ if (curr.status === 'fulfilled' && curr.value !== undefined) {
291
+ acc.push(curr.value);
292
+ }
293
+ return acc;
294
+ }, []);
295
+ }
238
296
  }
@@ -3,10 +3,16 @@ import {
3
3
  BaseAction,
4
4
  Document,
5
5
  DocumentHeader,
6
- Operation
6
+ Operation,
7
+ OperationScope
7
8
  } from 'document-model/document';
8
- import { mergeOperations } from '..';
9
- import { DocumentDriveStorage, DocumentStorage, IDriveStorage } from './types';
9
+ import {
10
+ DocumentDriveStorage,
11
+ DocumentStorage,
12
+ IDriveStorage,
13
+ } from './types';
14
+ import type { SynchronizationUnitQuery } from '../server/types';
15
+ import { mergeOperations } from '../utils';
10
16
 
11
17
  export class MemoryStorage implements IDriveStorage {
12
18
  private documents: Record<string, Record<string, DocumentStorage>>;
@@ -19,7 +25,7 @@ export class MemoryStorage implements IDriveStorage {
19
25
  }
20
26
 
21
27
  checkDocumentExists(drive: string, id: string): Promise<boolean> {
22
- return Promise.resolve(this.documents[drive]?.[id] !== undefined)
28
+ return Promise.resolve(this.documents[drive]?.[id] !== undefined);
23
29
  }
24
30
 
25
31
  async getDocuments(drive: string) {
@@ -153,4 +159,61 @@ export class MemoryStorage implements IDriveStorage {
153
159
  delete this.documents[id];
154
160
  delete this.drives[id];
155
161
  }
162
+
163
+ async getSynchronizationUnitsRevision(
164
+ units: SynchronizationUnitQuery[]
165
+ ): Promise<
166
+ {
167
+ driveId: string;
168
+ documentId: string;
169
+ scope: string;
170
+ branch: string;
171
+ lastUpdated: string;
172
+ revision: number;
173
+ }[]
174
+ > {
175
+ const results = await Promise.allSettled(
176
+ units.map(async unit => {
177
+ try {
178
+ const document = await (unit.documentId
179
+ ? this.getDocument(unit.driveId, unit.documentId)
180
+ : this.getDrive(unit.driveId));
181
+ if (!document) {
182
+ return undefined;
183
+ }
184
+ const operation =
185
+ document.operations[unit.scope as OperationScope]?.at(
186
+ -1
187
+ );
188
+ if (operation) {
189
+ return {
190
+ driveId: unit.driveId,
191
+ documentId: unit.documentId,
192
+ scope: unit.scope,
193
+ branch: unit.branch,
194
+ lastUpdated: operation.timestamp,
195
+ revision: operation.index
196
+ };
197
+ }
198
+ } catch {
199
+ return undefined;
200
+ }
201
+ })
202
+ );
203
+ return results.reduce<
204
+ {
205
+ driveId: string;
206
+ documentId: string;
207
+ scope: string;
208
+ branch: string;
209
+ lastUpdated: string;
210
+ revision: number;
211
+ }[]
212
+ >((acc, curr) => {
213
+ if (curr.status === 'fulfilled' && curr.value !== undefined) {
214
+ acc.push(curr.value);
215
+ }
216
+ return acc;
217
+ }, []);
218
+ }
156
219
  }