document-drive 1.0.0-alpha.54 → 1.0.0-alpha.55
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 +51 -46
- package/src/server/types.ts +1 -0
- package/src/storage/filesystem.ts +2 -2
- package/src/storage/prisma.ts +9 -7
- package/src/storage/types.ts +2 -2
- package/src/utils/index.ts +9 -0
package/package.json
CHANGED
package/src/server/index.ts
CHANGED
|
@@ -28,7 +28,8 @@ import { MemoryStorage } from '../storage/memory';
|
|
|
28
28
|
import type {
|
|
29
29
|
DocumentDriveStorage,
|
|
30
30
|
DocumentStorage,
|
|
31
|
-
IDriveStorage
|
|
31
|
+
IDriveStorage,
|
|
32
|
+
StrictDocumentStorage
|
|
32
33
|
} from '../storage/types';
|
|
33
34
|
import { generateUUID, isBefore, isDocumentDrive } from '../utils';
|
|
34
35
|
import {
|
|
@@ -494,7 +495,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
494
495
|
logger.error('Error getting drive from cache', e);
|
|
495
496
|
}
|
|
496
497
|
const driveStorage = await this.storage.getDrive(drive);
|
|
497
|
-
const document = this.
|
|
498
|
+
const document = this._buildDocument(driveStorage, options);
|
|
498
499
|
if (!isDocumentDrive(document)) {
|
|
499
500
|
throw new Error(
|
|
500
501
|
`Document with id ${drive} is not a Document Drive`
|
|
@@ -518,7 +519,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
518
519
|
}
|
|
519
520
|
|
|
520
521
|
const driveStorage = await this.storage.getDriveBySlug(slug);
|
|
521
|
-
const document = this.
|
|
522
|
+
const document = this._buildDocument(driveStorage, options);
|
|
522
523
|
if (!isDocumentDrive(document)) {
|
|
523
524
|
throw new Error(
|
|
524
525
|
`Document with slug ${slug} is not a Document Drive`
|
|
@@ -540,20 +541,10 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
540
541
|
} catch (e) {
|
|
541
542
|
logger.error('Error getting document from cache', e);
|
|
542
543
|
}
|
|
543
|
-
const
|
|
544
|
+
const documentStorage =
|
|
544
545
|
await this.storage.getDocument(drive, id);
|
|
546
|
+
const document = this._buildDocument(documentStorage, options)
|
|
545
547
|
|
|
546
|
-
const documentModel = this._getDocumentModel(header.documentType);
|
|
547
|
-
|
|
548
|
-
const document = baseUtils.replayDocument(
|
|
549
|
-
initialState,
|
|
550
|
-
filterOperationsByRevision(operations, options?.revisions),
|
|
551
|
-
documentModel.reducer,
|
|
552
|
-
undefined,
|
|
553
|
-
header,
|
|
554
|
-
undefined,
|
|
555
|
-
{ checkHashes: false }
|
|
556
|
-
);
|
|
557
548
|
this.cache.setDocument(drive, id, document).catch(logger.error);
|
|
558
549
|
return document;
|
|
559
550
|
}
|
|
@@ -566,11 +557,41 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
566
557
|
driveId: string,
|
|
567
558
|
input: CreateDocumentInput
|
|
568
559
|
) {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
560
|
+
// if a document was provided then checks if it's valid
|
|
561
|
+
if (input.document) {
|
|
562
|
+
if (input.documentType !== input.document.documentType) {
|
|
563
|
+
throw new Error(`Provided document is not ${input.documentType}`);
|
|
564
|
+
}
|
|
565
|
+
this._buildDocument(input.document);
|
|
566
|
+
}
|
|
572
567
|
|
|
573
|
-
|
|
568
|
+
// if no document was provided then create a new one
|
|
569
|
+
const document = input.document ??
|
|
570
|
+
this._getDocumentModel(input.documentType).utils.createDocument();
|
|
571
|
+
|
|
572
|
+
// stores document information
|
|
573
|
+
const documentStorage: DocumentStorage = {
|
|
574
|
+
name: document.name,
|
|
575
|
+
revision: document.revision,
|
|
576
|
+
documentType: document.documentType,
|
|
577
|
+
created: document.created,
|
|
578
|
+
lastModified: document.lastModified,
|
|
579
|
+
operations: { global: [], local: [] },
|
|
580
|
+
initialState: document.initialState,
|
|
581
|
+
clipboard: [],
|
|
582
|
+
};
|
|
583
|
+
await this.storage.createDocument(driveId, input.id, documentStorage);
|
|
584
|
+
|
|
585
|
+
// if the document contains operations then
|
|
586
|
+
// stores the operations in the storage
|
|
587
|
+
const operations = Object.values(document.operations).flat();
|
|
588
|
+
if (operations.length) {
|
|
589
|
+
if (isDocumentDrive(document)) {
|
|
590
|
+
await this.storage.addDriveOperations(driveId, operations as Operation<DocumentDriveAction>[], document);
|
|
591
|
+
} else {
|
|
592
|
+
await this.storage.addDocumentOperations(driveId, input.id, operations, document)
|
|
593
|
+
}
|
|
594
|
+
}
|
|
574
595
|
|
|
575
596
|
return document;
|
|
576
597
|
}
|
|
@@ -603,6 +624,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
603
624
|
const storageDocumentOperations =
|
|
604
625
|
storageDocument.operations[scope as OperationScope];
|
|
605
626
|
|
|
627
|
+
// TODO two equal operations done by two clients will be considered the same, ie: { type: "INCREMENT" }
|
|
606
628
|
const branch = removeExistingOperations(
|
|
607
629
|
operationsByScope[scope as OperationScope] || [],
|
|
608
630
|
storageDocumentOperations
|
|
@@ -698,20 +720,21 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
698
720
|
}
|
|
699
721
|
|
|
700
722
|
private _buildDocument<T extends Document>(
|
|
701
|
-
documentStorage: DocumentStorage<T
|
|
702
|
-
|
|
703
|
-
const
|
|
704
|
-
documentStorage.documentType
|
|
705
|
-
);
|
|
706
|
-
return baseUtils.replayDocument(
|
|
723
|
+
documentStorage: DocumentStorage<T>, options?: GetDocumentOptions) {
|
|
724
|
+
const documentModel = this._getDocumentModel(documentStorage.documentType);
|
|
725
|
+
const document = baseUtils.replayDocument(
|
|
707
726
|
documentStorage.initialState,
|
|
708
|
-
|
|
727
|
+
filterOperationsByRevision(
|
|
728
|
+
documentStorage.operations,
|
|
729
|
+
options?.revisions
|
|
730
|
+
),
|
|
709
731
|
documentModel.reducer,
|
|
710
732
|
undefined,
|
|
711
733
|
documentStorage,
|
|
712
734
|
undefined,
|
|
713
|
-
{ checkHashes:
|
|
714
|
-
)
|
|
735
|
+
{ checkHashes: options?.checkHashes ?? true }
|
|
736
|
+
);
|
|
737
|
+
return document as T;
|
|
715
738
|
}
|
|
716
739
|
|
|
717
740
|
private async _performOperation<T extends Document>(
|
|
@@ -1280,22 +1303,4 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
|
|
|
1280
1303
|
logger.debug(`Emitting event ${event}`, args);
|
|
1281
1304
|
return this.emitter.emit(event, ...args);
|
|
1282
1305
|
}
|
|
1283
|
-
|
|
1284
|
-
private _replayDocument(documentStorage: DocumentStorage, options?: GetDocumentOptions) {
|
|
1285
|
-
const documentModel = this._getDocumentModel(documentStorage.documentType);
|
|
1286
|
-
const document = baseUtils.replayDocument(
|
|
1287
|
-
documentStorage.initialState,
|
|
1288
|
-
filterOperationsByRevision(
|
|
1289
|
-
documentStorage.operations,
|
|
1290
|
-
options?.revisions
|
|
1291
|
-
),
|
|
1292
|
-
documentModel.reducer,
|
|
1293
|
-
undefined,
|
|
1294
|
-
documentStorage,
|
|
1295
|
-
undefined,
|
|
1296
|
-
{ checkHashes: false }
|
|
1297
|
-
);
|
|
1298
|
-
|
|
1299
|
-
return document;
|
|
1300
|
-
}
|
|
1301
1306
|
}
|
package/src/server/types.ts
CHANGED
|
@@ -90,8 +90,8 @@ export class FilesystemStorage implements IDriveStorage {
|
|
|
90
90
|
|
|
91
91
|
async createDocument(drive: string, id: string, document: DocumentStorage) {
|
|
92
92
|
const documentPath = this._buildDocumentPath(drive, id);
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
ensureDir(path.dirname(documentPath));
|
|
94
|
+
writeFileSync(documentPath, stringify(document), {
|
|
95
95
|
encoding: 'utf-8'
|
|
96
96
|
});
|
|
97
97
|
}
|
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 = {
|
|
@@ -78,7 +80,7 @@ export class PrismaStorage implements IDriveStorage {
|
|
|
78
80
|
async createDrive(id: string, drive: DocumentDriveStorage): Promise<void> {
|
|
79
81
|
// drive for all drive documents
|
|
80
82
|
await this.createDocument('drives', id, drive as DocumentStorage);
|
|
81
|
-
|
|
83
|
+
await this.db.drive.upsert({
|
|
82
84
|
where: {
|
|
83
85
|
slug: drive.initialState.state.global.slug ?? id
|
|
84
86
|
},
|
|
@@ -159,7 +161,7 @@ export class PrismaStorage implements IDriveStorage {
|
|
|
159
161
|
documentId: id,
|
|
160
162
|
hash: op.hash,
|
|
161
163
|
index: op.index,
|
|
162
|
-
input: op.input
|
|
164
|
+
input: JSON.stringify(op.input),
|
|
163
165
|
timestamp: op.timestamp,
|
|
164
166
|
type: op.type,
|
|
165
167
|
scope: op.scope,
|
|
@@ -186,7 +188,7 @@ export class PrismaStorage implements IDriveStorage {
|
|
|
186
188
|
documentId: id,
|
|
187
189
|
hash: op.hash,
|
|
188
190
|
index: op.index,
|
|
189
|
-
input: op.input
|
|
191
|
+
input: JSON.stringify(op.input),
|
|
190
192
|
timestamp: op.timestamp,
|
|
191
193
|
type: op.type,
|
|
192
194
|
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,6 +11,15 @@ import {
|
|
|
11
11
|
Operation
|
|
12
12
|
} from 'document-model/document';
|
|
13
13
|
import { ConflictOperationError } from '../server/error';
|
|
14
|
+
import { DocumentDriveStorage, DocumentStorage } from '../storage';
|
|
15
|
+
|
|
16
|
+
export function isDocumentDriveStorage(
|
|
17
|
+
document: DocumentStorage
|
|
18
|
+
): document is DocumentDriveStorage {
|
|
19
|
+
return (
|
|
20
|
+
document.documentType === DocumentDriveModel.id
|
|
21
|
+
);
|
|
22
|
+
}
|
|
14
23
|
|
|
15
24
|
export function isDocumentDrive(
|
|
16
25
|
document: Document
|