document-drive 1.0.0-alpha.80 → 1.0.0-alpha.81

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document-drive",
3
- "version": "1.0.0-alpha.80",
3
+ "version": "1.0.0-alpha.81",
4
4
  "license": "AGPL-3.0-only",
5
5
  "type": "module",
6
6
  "module": "./src/index.ts",
@@ -194,7 +194,14 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
194
194
  const drive = await this.getDrive(driveId);
195
195
  let driveTriggers = this.triggerMap.get(driveId);
196
196
 
197
- const syncUnits = await this.getSynchronizationUnitsIds(driveId);
197
+ const syncUnits = await this.getSynchronizationUnitsIds(
198
+ driveId,
199
+ undefined,
200
+ undefined,
201
+ undefined,
202
+ undefined,
203
+ drive
204
+ );
198
205
 
199
206
  for (const trigger of drive.state.local.triggers) {
200
207
  if (driveTriggers?.get(trigger.id)) {
@@ -374,9 +381,10 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
374
381
  documentId?: string[],
375
382
  scope?: string[],
376
383
  branch?: string[],
377
- documentType?: string[]
384
+ documentType?: string[],
385
+ loadedDrive?: DocumentDriveDocument
378
386
  ) {
379
- const drive = await this.getDrive(driveId);
387
+ const drive = loadedDrive || (await this.getDrive(driveId));
380
388
 
381
389
  const synchronizationUnitsQuery = await this.getSynchronizationUnitsIds(
382
390
  driveId,
@@ -490,9 +498,10 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
490
498
 
491
499
  public async getSynchronizationUnitIdInfo(
492
500
  driveId: string,
493
- syncId: string
501
+ syncId: string,
502
+ loadedDrive?: DocumentDriveDocument
494
503
  ): Promise<SynchronizationUnitQuery | undefined> {
495
- const drive = await this.getDrive(driveId);
504
+ const drive = loadedDrive || (await this.getDrive(driveId));
496
505
  const node = drive.state.global.nodes.find(
497
506
  node =>
498
507
  isFileNode(node) &&
@@ -523,11 +532,13 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
523
532
 
524
533
  public async getSynchronizationUnit(
525
534
  driveId: string,
526
- syncId: string
535
+ syncId: string,
536
+ loadedDrive?: DocumentDriveDocument
527
537
  ): Promise<SynchronizationUnit | undefined> {
528
538
  const syncUnit = await this.getSynchronizationUnitIdInfo(
529
539
  driveId,
530
- syncId
540
+ syncId,
541
+ loadedDrive
531
542
  );
532
543
 
533
544
  if (!syncUnit) {
@@ -559,12 +570,17 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
559
570
  filter: {
560
571
  since?: string | undefined;
561
572
  fromRevision?: number | undefined;
562
- }
573
+ },
574
+ loadedDrive?: DocumentDriveDocument
563
575
  ): Promise<OperationUpdate[]> {
564
576
  const syncUnit =
565
577
  syncId === '0'
566
578
  ? { documentId: '', scope: 'global' }
567
- : await this.getSynchronizationUnitIdInfo(driveId, syncId);
579
+ : await this.getSynchronizationUnitIdInfo(
580
+ driveId,
581
+ syncId,
582
+ loadedDrive
583
+ );
568
584
 
569
585
  if (!syncUnit) {
570
586
  throw new Error(`Invalid Sync Id ${syncId} in drive ${driveId}`);
@@ -572,7 +588,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
572
588
 
573
589
  const document =
574
590
  syncId === '0'
575
- ? await this.getDrive(driveId)
591
+ ? loadedDrive || (await this.getDrive(driveId))
576
592
  : await this.getDocument(driveId, syncUnit.documentId); // TODO replace with getDocumentOperations
577
593
 
578
594
  const operations =
@@ -830,19 +846,24 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
830
846
  async _processOperations<T extends Document, A extends Action>(
831
847
  drive: string,
832
848
  documentId: string | undefined,
833
- storageDocument: DocumentStorage<T>,
849
+ documentStorage: DocumentStorage<T>,
834
850
  operations: Operation<A | BaseAction>[]
835
851
  ) {
836
852
  const operationsApplied: Operation<A | BaseAction>[] = [];
837
853
  const signals: SignalResult[] = [];
838
- let document: T = this._buildDocument(storageDocument);
854
+ const documentStorageWithState = await this._addDocumentResultingStage(
855
+ documentStorage,
856
+ drive,
857
+ documentId
858
+ );
839
859
 
860
+ let document: T = this._buildDocument(documentStorageWithState);
840
861
  let error: OperationError | undefined; // TODO: replace with an array of errors/consistency issues
841
862
  const operationsByScope = groupOperationsByScope(operations);
842
863
 
843
864
  for (const scope of Object.keys(operationsByScope)) {
844
865
  const storageDocumentOperations =
845
- storageDocument.operations[scope as OperationScope];
866
+ documentStorage.operations[scope as OperationScope];
846
867
 
847
868
  // TODO two equal operations done by two clients will be considered the same, ie: { type: "INCREMENT" }
848
869
  const branch = removeExistingOperations(
@@ -922,6 +943,57 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
922
943
  } as const;
923
944
  }
924
945
 
946
+ private async _addDocumentResultingStage<T extends Document>(
947
+ document: DocumentStorage<T>,
948
+ drive: string,
949
+ documentId?: string,
950
+ options?: GetDocumentOptions
951
+ ): Promise<DocumentStorage<T>> {
952
+ // apply skip header operations to all scopes
953
+ const operations =
954
+ options?.revisions !== undefined
955
+ ? filterOperationsByRevision(
956
+ document.operations,
957
+ options.revisions
958
+ )
959
+ : document.operations;
960
+ const documentOperations =
961
+ DocumentUtils.documentHelpers.garbageCollectDocumentOperations(
962
+ operations
963
+ );
964
+
965
+ for (const scope of Object.keys(documentOperations)) {
966
+ const lastRemainingOperation =
967
+ documentOperations[scope as OperationScope].at(-1);
968
+ // if the latest operation doesn't have a resulting state then tries
969
+ // to retrieve it from the db to avoid rerunning all the operations
970
+ if (
971
+ lastRemainingOperation &&
972
+ !lastRemainingOperation.resultingState
973
+ ) {
974
+ lastRemainingOperation.resultingState = await (documentId
975
+ ? this.storage.getOperationResultingState?.(
976
+ drive,
977
+ documentId,
978
+ lastRemainingOperation.index,
979
+ lastRemainingOperation.scope,
980
+ 'main'
981
+ )
982
+ : this.storage.getDriveOperationResultingState?.(
983
+ drive,
984
+ lastRemainingOperation.index,
985
+ lastRemainingOperation.scope,
986
+ 'main'
987
+ ));
988
+ }
989
+ }
990
+
991
+ return {
992
+ ...document,
993
+ operations: documentOperations
994
+ };
995
+ }
996
+
925
997
  private _buildDocument<T extends Document>(
926
998
  documentStorage: DocumentStorage<T>,
927
999
  options?: GetDocumentOptions
@@ -1614,14 +1686,12 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
1614
1686
  }
1615
1687
 
1616
1688
  const prevSyncUnits = await this.getSynchronizationUnitsIds(drive);
1617
-
1618
1689
  try {
1619
1690
  await this._addDriveOperations(drive, async documentStorage => {
1620
1691
  const result = await this._processOperations<
1621
1692
  DocumentDriveDocument,
1622
1693
  DocumentDriveAction
1623
1694
  >(drive, undefined, documentStorage, operations.slice());
1624
-
1625
1695
  document = result.document;
1626
1696
  operationsApplied.push(...result.operationsApplied);
1627
1697
  signals.push(...result.signals);
@@ -1654,7 +1724,14 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
1654
1724
  }
1655
1725
  }
1656
1726
 
1657
- const syncUnits = await this.getSynchronizationUnitsIds(drive);
1727
+ const syncUnits = await this.getSynchronizationUnitsIds(
1728
+ drive,
1729
+ undefined,
1730
+ undefined,
1731
+ undefined,
1732
+ undefined,
1733
+ document
1734
+ );
1658
1735
 
1659
1736
  const prevSyncUnitsIds = prevSyncUnits.map(unit => unit.syncId);
1660
1737
  const syncUnitsIds = syncUnits.map(unit => unit.syncId);
@@ -239,43 +239,47 @@ export class ListenerManager extends BaseListenerManager {
239
239
  );
240
240
 
241
241
  const strandUpdates: StrandUpdate[] = [];
242
- for (const syncUnit of syncUnits) {
243
- const unitState = listener.syncUnits.get(syncUnit.syncId);
244
-
245
- if (
246
- unitState &&
247
- unitState.listenerRev >= syncUnit.revision
248
- ) {
249
- continue;
250
- }
251
-
252
- const opData: OperationUpdate[] = [];
253
- try {
254
- const data = await this.drive.getOperationData(
255
- // TODO - join queries, DEAL WITH INVALID SYNC ID ERROR
256
- driveId,
257
- syncUnit.syncId,
258
- {
259
- fromRevision: unitState?.listenerRev
260
- }
242
+ await Promise.all(
243
+ syncUnits.map(async syncUnit => {
244
+ const unitState = listener.syncUnits.get(
245
+ syncUnit.syncId
261
246
  );
262
- opData.push(...data);
263
- } catch (e) {
264
- logger.error(e);
265
- }
266
247
 
267
- if (!opData.length) {
268
- continue;
269
- }
248
+ if (
249
+ unitState &&
250
+ unitState.listenerRev >= syncUnit.revision
251
+ ) {
252
+ return;
253
+ }
270
254
 
271
- strandUpdates.push({
272
- driveId,
273
- documentId: syncUnit.documentId,
274
- branch: syncUnit.branch,
275
- operations: opData,
276
- scope: syncUnit.scope as OperationScope
277
- });
278
- }
255
+ const opData: OperationUpdate[] = [];
256
+ try {
257
+ const data = await this.drive.getOperationData(
258
+ // TODO - join queries, DEAL WITH INVALID SYNC ID ERROR
259
+ driveId,
260
+ syncUnit.syncId,
261
+ {
262
+ fromRevision: unitState?.listenerRev
263
+ }
264
+ );
265
+ opData.push(...data);
266
+ } catch (e) {
267
+ logger.error(e);
268
+ }
269
+
270
+ if (!opData.length) {
271
+ return;
272
+ }
273
+
274
+ strandUpdates.push({
275
+ driveId,
276
+ documentId: syncUnit.documentId,
277
+ branch: syncUnit.branch,
278
+ operations: opData,
279
+ scope: syncUnit.scope as OperationScope
280
+ });
281
+ })
282
+ );
279
283
 
280
284
  if (strandUpdates.length == 0) {
281
285
  continue;
@@ -369,7 +373,11 @@ export class ListenerManager extends BaseListenerManager {
369
373
  return false;
370
374
  }
371
375
 
372
- getListenerSyncUnits(driveId: string, listenerId: string) {
376
+ getListenerSyncUnits(
377
+ driveId: string,
378
+ listenerId: string,
379
+ loadedDrive?: DocumentDriveDocument
380
+ ) {
373
381
  const listener = this.listenerState.get(driveId)?.get(listenerId);
374
382
  if (!listener) {
375
383
  return [];
@@ -380,7 +388,8 @@ export class ListenerManager extends BaseListenerManager {
380
388
  filter.documentId ?? ['*'],
381
389
  filter.scope ?? ['*'],
382
390
  filter.branch ?? ['*'],
383
- filter.documentType ?? ['*']
391
+ filter.documentType ?? ['*'],
392
+ loadedDrive
384
393
  );
385
394
  }
386
395
 
@@ -453,45 +462,53 @@ export class ListenerManager extends BaseListenerManager {
453
462
  // fetch operations from drive and prepare strands
454
463
  const strands: StrandUpdate[] = [];
455
464
 
456
- const syncUnits = await this.getListenerSyncUnits(driveId, listenerId);
465
+ const drive = await this.drive.getDrive(driveId);
466
+ const syncUnits = await this.getListenerSyncUnits(
467
+ driveId,
468
+ listenerId,
469
+ drive
470
+ );
457
471
 
458
- for (const syncUnit of syncUnits) {
459
- if (syncUnit.revision < 0) {
460
- continue;
461
- }
462
- const entry = listener.syncUnits.get(syncUnit.syncId);
463
- if (entry && entry.listenerRev >= syncUnit.revision) {
464
- continue;
465
- }
472
+ await Promise.all(
473
+ syncUnits.map(async syncUnit => {
474
+ if (syncUnit.revision < 0) {
475
+ return;
476
+ }
477
+ const entry = listener.syncUnits.get(syncUnit.syncId);
478
+ if (entry && entry.listenerRev >= syncUnit.revision) {
479
+ return;
480
+ }
466
481
 
467
- const { documentId, driveId, scope, branch } = syncUnit;
468
- try {
469
- const operations = await this.drive.getOperationData(
470
- // DEAL WITH INVALID SYNC ID ERROR
471
- driveId,
472
- syncUnit.syncId,
473
- {
474
- since,
475
- fromRevision: entry?.listenerRev
482
+ const { documentId, driveId, scope, branch } = syncUnit;
483
+ try {
484
+ const operations = await this.drive.getOperationData(
485
+ // DEAL WITH INVALID SYNC ID ERROR
486
+ driveId,
487
+ syncUnit.syncId,
488
+ {
489
+ since,
490
+ fromRevision: entry?.listenerRev
491
+ },
492
+ drive
493
+ );
494
+
495
+ if (!operations.length) {
496
+ return;
476
497
  }
477
- );
478
498
 
479
- if (!operations.length) {
480
- continue;
499
+ strands.push({
500
+ driveId,
501
+ documentId,
502
+ scope: scope as OperationScope,
503
+ branch,
504
+ operations
505
+ });
506
+ } catch (error) {
507
+ logger.error(error);
508
+ return;
481
509
  }
482
-
483
- strands.push({
484
- driveId,
485
- documentId,
486
- scope: scope as OperationScope,
487
- branch,
488
- operations
489
- });
490
- } catch (error) {
491
- logger.error(error);
492
- continue;
493
- }
494
- }
510
+ })
511
+ );
495
512
 
496
513
  return strands;
497
514
  }
@@ -289,12 +289,14 @@ export abstract class BaseDocumentDriveServer {
289
289
  documentId?: string[],
290
290
  scope?: string[],
291
291
  branch?: string[],
292
- documentType?: string[]
292
+ documentType?: string[],
293
+ loadedDrive?: DocumentDriveDocument
293
294
  ): Promise<SynchronizationUnit[]>;
294
295
 
295
296
  abstract getSynchronizationUnit(
296
297
  driveId: string,
297
- syncId: string
298
+ syncId: string,
299
+ loadedDrive?: DocumentDriveDocument
298
300
  ): Promise<SynchronizationUnit | undefined>;
299
301
 
300
302
  abstract getSynchronizationUnitsIds(
@@ -311,7 +313,8 @@ export abstract class BaseDocumentDriveServer {
311
313
  filter: {
312
314
  since?: string;
313
315
  fromRevision?: number;
314
- }
316
+ },
317
+ loadedDrive?: DocumentDriveDocument
315
318
  ): Promise<OperationUpdate[]>;
316
319
 
317
320
  /** Internal methods **/
@@ -309,7 +309,7 @@ export class PrismaStorage implements IDriveStorage {
309
309
  header
310
310
  );
311
311
  },
312
- { isolationLevel: 'Serializable' }
312
+ { isolationLevel: 'Serializable', maxWait: 10000, timeout: 20000 }
313
313
  );
314
314
 
315
315
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition