document-drive 1.0.0-experimental.4 → 1.0.0-experimental.6

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-experimental.4",
3
+ "version": "1.0.0-experimental.6",
4
4
  "license": "AGPL-3.0-only",
5
5
  "type": "module",
6
6
  "module": "./src/index.ts",
@@ -28,7 +28,7 @@ import { MemoryStorage } from '../storage/memory';
28
28
  import type {
29
29
  DocumentDriveStorage,
30
30
  DocumentStorage,
31
- IDriveStorage
31
+ IDriveStorage,
32
32
  } from '../storage/types';
33
33
  import { generateUUID, isBefore, isDocumentDrive } from '../utils';
34
34
  import {
@@ -438,7 +438,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
438
438
  return documentModel;
439
439
  }
440
440
 
441
- async addDrive(drive: DriveInput) {
441
+ async addDrive(drive: DriveInput): Promise<DocumentDriveDocument> {
442
442
  const id = drive.global.id || generateUUID();
443
443
  if (!id) {
444
444
  throw new Error('Invalid Drive Id');
@@ -459,7 +459,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
459
459
  return document;
460
460
  }
461
461
 
462
- async addRemoteDrive(url: string, options: RemoteDriveOptions) {
462
+ async addRemoteDrive(url: string, options: RemoteDriveOptions): Promise<DocumentDriveDocument> {
463
463
  const { id, name, slug, icon } = await requestPublicDrive(url);
464
464
  const {
465
465
  pullFilter,
@@ -560,8 +560,8 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
560
560
  }
561
561
  const documentStorage =
562
562
  await this.storage.getDocument(drive, id);
563
+ const document = this._buildDocument(documentStorage, options)
563
564
 
564
- const document = this._buildDocument(documentStorage, options);
565
565
  this.cache.setDocument(drive, id, document).catch(logger.error);
566
566
  return document;
567
567
  }
@@ -574,11 +574,41 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
574
574
  driveId: string,
575
575
  input: CreateDocumentInput
576
576
  ) {
577
- const documentModel = this._getDocumentModel(input.documentType);
578
- // TODO validate input.document is of documentType
579
- const document = input.document ?? documentModel.utils.createDocument();
577
+ // if a document was provided then checks if it's valid
578
+ if (input.document) {
579
+ if (input.documentType !== input.document.documentType) {
580
+ throw new Error(`Provided document is not ${input.documentType}`);
581
+ }
582
+ this._buildDocument(input.document);
583
+ }
580
584
 
581
- await this.storage.createDocument(driveId, input.id, document);
585
+ // if no document was provided then create a new one
586
+ const document = input.document ??
587
+ this._getDocumentModel(input.documentType).utils.createDocument();
588
+
589
+ // stores document information
590
+ const documentStorage: DocumentStorage = {
591
+ name: document.name,
592
+ revision: document.revision,
593
+ documentType: document.documentType,
594
+ created: document.created,
595
+ lastModified: document.lastModified,
596
+ operations: { global: [], local: [] },
597
+ initialState: document.initialState,
598
+ clipboard: [],
599
+ };
600
+ await this.storage.createDocument(driveId, input.id, documentStorage);
601
+
602
+ // if the document contains operations then
603
+ // stores the operations in the storage
604
+ const operations = Object.values(document.operations).flat();
605
+ if (operations.length) {
606
+ if (isDocumentDrive(document)) {
607
+ await this.storage.addDriveOperations(driveId, operations as Operation<DocumentDriveAction>[], document);
608
+ } else {
609
+ await this.storage.addDocumentOperations(driveId, input.id, operations, document)
610
+ }
611
+ }
582
612
 
583
613
  return document;
584
614
  }
@@ -610,6 +640,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
610
640
  const storageDocumentOperations =
611
641
  storageDocument.operations[scope as OperationScope];
612
642
 
643
+ // TODO two equal operations done by two clients will be considered the same, ie: { type: "INCREMENT" }
613
644
  const branch = removeExistingOperations(
614
645
  operationsByScope[scope as OperationScope] || [],
615
646
  storageDocumentOperations
@@ -702,7 +733,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
702
733
  undefined,
703
734
  documentStorage,
704
735
  undefined,
705
- { checkHashes: false }
736
+ { checkHashes: options?.checkHashes ?? true }
706
737
  ) as T;
707
738
  }
708
739
 
@@ -788,8 +819,8 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
788
819
  };
789
820
  }
790
821
 
791
- addOperation(drive: string, id: string, operation: Operation) {
792
- return this.addOperations(drive, id, [operation]);
822
+ addOperation(drive: string, id: string, operation: Operation, forceSync = true): Promise<IOperationResult> {
823
+ return this.addOperations(drive, id, [operation], forceSync);
793
824
  }
794
825
 
795
826
  private async _addOperations(
@@ -823,22 +854,19 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
823
854
  }
824
855
  }
825
856
 
857
+ queueOperation(drive: string, id: string, operation: Operation, forceSync = true): Promise<IOperationResult> {
858
+ return this.queueOperations(drive, id, [operation], forceSync);
859
+ }
826
860
 
827
861
  async queueOperations(drive: string,
828
862
  id: string,
829
863
  operations: Operation[],
830
864
  forceSync = true) {
831
- // try {
832
- // await this.getDocument(drive, id);
833
- // } catch (error) {
834
- // logger.error('Error getting document', error);
835
- // throw error;
836
- // }
837
865
 
838
866
  try {
839
867
  const jobId = await this.queueManager.addJob({ driveId: drive, documentId: id, operations, forceSync });
840
868
 
841
- return new Promise((resolve, reject) => {
869
+ return new Promise<IOperationResult>((resolve, reject) => {
842
870
  const unsubscribe = this.queueManager.on('jobCompleted', (job, result) => {
843
871
  if (job.jobId === jobId) {
844
872
  unsubscribe();
@@ -974,9 +1002,10 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
974
1002
 
975
1003
  addDriveOperation(
976
1004
  drive: string,
977
- operation: Operation<DocumentDriveAction | BaseAction>
1005
+ operation: Operation<DocumentDriveAction | BaseAction>,
1006
+ forceSync = true
978
1007
  ) {
979
- return this.addDriveOperations(drive, [operation]);
1008
+ return this.addDriveOperations(drive, [operation], forceSync);
980
1009
  }
981
1010
 
982
1011
  async clearStorage() {
@@ -1014,18 +1043,22 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
1014
1043
  }
1015
1044
  }
1016
1045
 
1046
+ queueDriveOperation(drive: string, operation: Operation<DocumentDriveAction | BaseAction>, forceSync = true): Promise<IOperationResult<DocumentDriveDocument>> {
1047
+ return this.queueDriveOperations(drive, [operation], forceSync);
1048
+ }
1049
+
1017
1050
  async queueDriveOperations(
1018
1051
  drive: string,
1019
1052
  operations: Operation<DocumentDriveAction | BaseAction>[],
1020
1053
  forceSync = true
1021
- ): Promise<IOperationResult> {
1054
+ ): Promise<IOperationResult<DocumentDriveDocument>> {
1022
1055
  const jobId = await this.queueManager.addJob({ driveId: drive, operations, forceSync });
1023
- return new Promise((resolve, reject) => {
1056
+ return new Promise<IOperationResult<DocumentDriveDocument>>((resolve, reject) => {
1024
1057
  const unsubscribe = this.queueManager.on('jobCompleted', (job, result) => {
1025
1058
  if (job.jobId === jobId) {
1026
1059
  unsubscribe();
1027
1060
  unsubscribeError();
1028
- resolve(result);
1061
+ resolve(result as IOperationResult<DocumentDriveDocument>);
1029
1062
  }
1030
1063
  });
1031
1064
  const unsubscribeError = this.queueManager.on('jobFailed', (job, error) => {
@@ -142,6 +142,7 @@ export type RevisionsFilter = PartialRecord<OperationScope, number>;
142
142
 
143
143
  export type GetDocumentOptions = {
144
144
  revisions?: RevisionsFilter;
145
+ checkHashes?: boolean;
145
146
  };
146
147
 
147
148
  export abstract class BaseDocumentDriveServer {
@@ -178,13 +179,39 @@ export abstract class BaseDocumentDriveServer {
178
179
  forceSync?: boolean
179
180
  ): Promise<IOperationResult>;
180
181
 
182
+ abstract queueOperation(
183
+ drive: string,
184
+ id: string,
185
+ operation: Operation,
186
+ forceSync?: boolean
187
+ ): Promise<IOperationResult>;
188
+ abstract queueOperations(
189
+ drive: string,
190
+ id: string,
191
+ operations: Operation[],
192
+ forceSync?: boolean
193
+ ): Promise<IOperationResult>;
194
+
181
195
  abstract addDriveOperation(
182
196
  drive: string,
183
- operation: Operation<DocumentDriveAction | BaseAction>
197
+ operation: Operation<DocumentDriveAction | BaseAction>,
198
+ forceSync?: boolean
184
199
  ): Promise<IOperationResult<DocumentDriveDocument>>;
185
200
  abstract addDriveOperations(
186
201
  drive: string,
187
- operations: Operation<DocumentDriveAction | BaseAction>[]
202
+ operations: Operation<DocumentDriveAction | BaseAction>[],
203
+ forceSync?: boolean
204
+ ): Promise<IOperationResult<DocumentDriveDocument>>;
205
+
206
+ abstract queueDriveOperation(
207
+ drive: string,
208
+ operation: Operation<DocumentDriveAction | BaseAction>,
209
+ forceSync?: boolean
210
+ ): Promise<IOperationResult<DocumentDriveDocument>>;
211
+ abstract queueDriveOperations(
212
+ drive: string,
213
+ operations: Operation<DocumentDriveAction | BaseAction>[],
214
+ forceSync?: boolean
188
215
  ): Promise<IOperationResult<DocumentDriveDocument>>;
189
216
 
190
217
  abstract addAction(
@@ -7,7 +7,6 @@ import {
7
7
  DocumentDriveState
8
8
  } from 'document-model-libs/document-drive';
9
9
  import type {
10
- ActionContext,
11
10
  BaseAction,
12
11
  DocumentHeader,
13
12
  ExtendedState,
@@ -26,17 +25,20 @@ type Transaction = Omit<
26
25
  function storageToOperation(
27
26
  op: Prisma.$OperationPayload['scalars']
28
27
  ): Operation {
29
- return {
28
+ const operation: Operation = {
30
29
  skip: op.skip,
31
30
  hash: op.hash,
32
31
  index: op.index,
33
32
  timestamp: new Date(op.timestamp).toISOString(),
34
- input: op.input,
33
+ input: JSON.parse(op.input),
35
34
  type: op.type,
36
35
  scope: op.scope as OperationScope,
37
- context: op.context ? op.context as ActionContext : undefined,
38
36
  // attachments: fileRegistry
39
37
  };
38
+ if (op.context) {
39
+ operation.context = op.context as Prisma.JsonObject;
40
+ }
41
+ return operation;
40
42
  }
41
43
 
42
44
  export type PrismaStorageOptions = {
@@ -157,7 +159,7 @@ export class PrismaStorage implements IDriveStorage {
157
159
  documentId: id,
158
160
  hash: op.hash,
159
161
  index: op.index,
160
- input: op.input as Prisma.InputJsonObject,
162
+ input: JSON.stringify(op.input),
161
163
  timestamp: op.timestamp,
162
164
  type: op.type,
163
165
  scope: op.scope,
@@ -1,6 +1,6 @@
1
1
  import type {
2
2
  DocumentDriveAction,
3
- DocumentDriveDocument
3
+ DocumentDriveDocument,
4
4
  } from 'document-model-libs/document-drive';
5
5
  import type {
6
6
  BaseAction,
@@ -8,12 +8,12 @@ import type {
8
8
  DocumentHeader,
9
9
  Operation
10
10
  } from 'document-model/document';
11
- import { GetDocumentOptions } from '../server';
12
11
 
13
12
  export type DocumentStorage<D extends Document = Document> = Omit<
14
13
  D,
15
14
  'state' | 'attachments'
16
15
  >;
16
+
17
17
  export type DocumentDriveStorage = DocumentStorage<DocumentDriveDocument>;
18
18
 
19
19
  export interface IStorage {
@@ -11,7 +11,16 @@ import {
11
11
  Operation,
12
12
  OperationScope
13
13
  } from 'document-model/document';
14
- import { ConflictOperationError, OperationError } from '../server/error';
14
+ import { OperationError } from '../server/error';
15
+ import { DocumentDriveStorage, DocumentStorage } from '../storage';
16
+
17
+ export function isDocumentDriveStorage(
18
+ document: DocumentStorage
19
+ ): document is DocumentDriveStorage {
20
+ return (
21
+ document.documentType === DocumentDriveModel.id
22
+ );
23
+ }
15
24
 
16
25
  export function isDocumentDrive(
17
26
  document: Document