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 +1 -1
- package/src/server/index.ts +56 -23
- package/src/server/types.ts +29 -2
- package/src/storage/prisma.ts +7 -5
- package/src/storage/types.ts +2 -2
- package/src/utils/index.ts +10 -1
package/package.json
CHANGED
package/src/server/index.ts
CHANGED
|
@@ -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
|
-
|
|
578
|
-
|
|
579
|
-
|
|
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
|
-
|
|
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:
|
|
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) => {
|
package/src/server/types.ts
CHANGED
|
@@ -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(
|
package/src/storage/prisma.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
162
|
+
input: JSON.stringify(op.input),
|
|
161
163
|
timestamp: op.timestamp,
|
|
162
164
|
type: op.type,
|
|
163
165
|
scope: op.scope,
|
package/src/storage/types.ts
CHANGED
|
@@ -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 {
|
package/src/utils/index.ts
CHANGED
|
@@ -11,7 +11,16 @@ import {
|
|
|
11
11
|
Operation,
|
|
12
12
|
OperationScope
|
|
13
13
|
} from 'document-model/document';
|
|
14
|
-
import {
|
|
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
|