document-drive 4.0.4 → 4.1.0-dev.10
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/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/prisma/schema.prisma +1 -2
- package/dist/src/drive-document-model/gen/document-model.js +3 -3
- package/dist/src/drive-document-model/gen/document-model.js.map +1 -1
- package/dist/src/drive-document-model/gen/drive/actions.d.ts +34 -10
- package/dist/src/drive-document-model/gen/drive/actions.d.ts.map +1 -1
- package/dist/src/drive-document-model/gen/node/actions.d.ts +30 -9
- package/dist/src/drive-document-model/gen/node/actions.d.ts.map +1 -1
- package/dist/src/drive-document-model/gen/node/creators.d.ts +6 -2
- package/dist/src/drive-document-model/gen/node/creators.d.ts.map +1 -1
- package/dist/src/drive-document-model/gen/node/creators.js +8 -1
- package/dist/src/drive-document-model/gen/node/creators.js.map +1 -1
- package/dist/src/drive-document-model/gen/reducer.d.ts.map +1 -1
- package/dist/src/drive-document-model/gen/reducer.js +32 -31
- package/dist/src/drive-document-model/gen/reducer.js.map +1 -1
- package/dist/src/drive-document-model/gen/schema/types.d.ts +1 -14
- package/dist/src/drive-document-model/gen/schema/types.d.ts.map +1 -1
- package/dist/src/drive-document-model/gen/schema/zod.d.ts +1 -5
- package/dist/src/drive-document-model/gen/schema/zod.d.ts.map +1 -1
- package/dist/src/drive-document-model/gen/schema/zod.js +0 -12
- package/dist/src/drive-document-model/gen/schema/zod.js.map +1 -1
- package/dist/src/drive-document-model/gen/types.d.ts +17 -5
- package/dist/src/drive-document-model/gen/types.d.ts.map +1 -1
- package/dist/src/drive-document-model/gen/types.js.map +1 -1
- package/dist/src/drive-document-model/module.d.ts +2 -1
- package/dist/src/drive-document-model/module.d.ts.map +1 -1
- package/dist/src/drive-document-model/src/reducers/node.d.ts.map +1 -1
- package/dist/src/drive-document-model/src/reducers/node.js +1 -25
- package/dist/src/drive-document-model/src/reducers/node.js.map +1 -1
- package/dist/src/drive-document-model/src/tests/actions.test.js +1 -23
- package/dist/src/drive-document-model/src/tests/actions.test.js.map +1 -1
- package/dist/src/drive-document-model/src/tests/base.test.js +0 -32
- package/dist/src/drive-document-model/src/tests/base.test.js.map +1 -1
- package/dist/src/drive-document-model/src/tests/node.test.js +4 -0
- package/dist/src/drive-document-model/src/tests/node.test.js.map +1 -1
- package/dist/src/drive-document-model/src/tests/utils.test.js +7 -37
- package/dist/src/drive-document-model/src/tests/utils.test.js.map +1 -1
- package/dist/src/drive-document-model/src/utils.d.ts +2 -8
- package/dist/src/drive-document-model/src/utils.d.ts.map +1 -1
- package/dist/src/drive-document-model/src/utils.js +6 -55
- package/dist/src/drive-document-model/src/utils.js.map +1 -1
- package/dist/src/processors/types.d.ts +2 -1
- package/dist/src/processors/types.d.ts.map +1 -1
- package/dist/src/queue/base.d.ts +6 -27
- package/dist/src/queue/base.d.ts.map +1 -1
- package/dist/src/queue/base.js +15 -203
- package/dist/src/queue/base.js.map +1 -1
- package/dist/src/queue/event.d.ts +41 -0
- package/dist/src/queue/event.d.ts.map +1 -0
- package/dist/src/queue/event.js +222 -0
- package/dist/src/queue/event.js.map +1 -0
- package/dist/src/queue/redis.d.ts +1 -27
- package/dist/src/queue/redis.d.ts.map +1 -1
- package/dist/src/queue/redis.js +122 -110
- package/dist/src/queue/redis.js.map +1 -1
- package/dist/src/queue/types.d.ts +18 -12
- package/dist/src/queue/types.d.ts.map +1 -1
- package/dist/src/queue/types.js +3 -0
- package/dist/src/queue/types.js.map +1 -1
- package/dist/src/read-mode/server.js.map +1 -1
- package/dist/src/read-mode/types.d.ts.map +1 -1
- package/dist/src/server/base-server.d.ts +90 -13
- package/dist/src/server/base-server.d.ts.map +1 -1
- package/dist/src/server/base-server.js +552 -250
- package/dist/src/server/base-server.js.map +1 -1
- package/dist/src/server/builder.js +2 -2
- package/dist/src/server/builder.js.map +1 -1
- package/dist/src/server/error.d.ts +3 -3
- package/dist/src/server/error.d.ts.map +1 -1
- package/dist/src/server/error.js +2 -2
- package/dist/src/server/error.js.map +1 -1
- package/dist/src/server/listener/listener-manager.d.ts +5 -6
- package/dist/src/server/listener/listener-manager.d.ts.map +1 -1
- package/dist/src/server/listener/listener-manager.js +62 -79
- package/dist/src/server/listener/listener-manager.js.map +1 -1
- package/dist/src/server/listener/transmitter/internal.d.ts +4 -4
- package/dist/src/server/listener/transmitter/internal.d.ts.map +1 -1
- package/dist/src/server/listener/transmitter/internal.js +18 -13
- package/dist/src/server/listener/transmitter/internal.js.map +1 -1
- package/dist/src/server/listener/transmitter/pull-responder.d.ts.map +1 -1
- package/dist/src/server/listener/transmitter/pull-responder.js +14 -8
- package/dist/src/server/listener/transmitter/pull-responder.js.map +1 -1
- package/dist/src/server/listener/transmitter/switchboard-push.d.ts.map +1 -1
- package/dist/src/server/listener/transmitter/switchboard-push.js +13 -8
- package/dist/src/server/listener/transmitter/switchboard-push.js.map +1 -1
- package/dist/src/server/listener/util.d.ts +1 -1
- package/dist/src/server/listener/util.d.ts.map +1 -1
- package/dist/src/server/listener/util.js +1 -1
- package/dist/src/server/listener/util.js.map +1 -1
- package/dist/src/server/sync-manager.d.ts +8 -10
- package/dist/src/server/sync-manager.d.ts.map +1 -1
- package/dist/src/server/sync-manager.js +62 -147
- package/dist/src/server/sync-manager.js.map +1 -1
- package/dist/src/server/sync-unit-map.d.ts +137 -0
- package/dist/src/server/sync-unit-map.d.ts.map +1 -0
- package/dist/src/server/sync-unit-map.js +234 -0
- package/dist/src/server/sync-unit-map.js.map +1 -0
- package/dist/src/server/types.d.ts +122 -38
- package/dist/src/server/types.d.ts.map +1 -1
- package/dist/src/server/types.js.map +1 -1
- package/dist/src/server/utils.d.ts +12 -3
- package/dist/src/server/utils.d.ts.map +1 -1
- package/dist/src/server/utils.js +44 -1
- package/dist/src/server/utils.js.map +1 -1
- package/dist/src/storage/browser.d.ts +8 -4
- package/dist/src/storage/browser.d.ts.map +1 -1
- package/dist/src/storage/browser.js +80 -22
- package/dist/src/storage/browser.js.map +1 -1
- package/dist/src/storage/filesystem.d.ts +8 -4
- package/dist/src/storage/filesystem.d.ts.map +1 -1
- package/dist/src/storage/filesystem.js +79 -22
- package/dist/src/storage/filesystem.js.map +1 -1
- package/dist/src/storage/ipfs.d.ts.map +1 -1
- package/dist/src/storage/ipfs.js.map +1 -1
- package/dist/src/storage/memory.d.ts +9 -5
- package/dist/src/storage/memory.d.ts.map +1 -1
- package/dist/src/storage/memory.js +76 -22
- package/dist/src/storage/memory.js.map +1 -1
- package/dist/src/storage/prisma/client/edge.js +5 -4
- package/dist/src/storage/prisma/client/index-browser.js +2 -1
- package/dist/src/storage/prisma/client/index.d.ts +58 -249
- package/dist/src/storage/prisma/client/index.js +5 -4
- package/dist/src/storage/prisma/client/package.json +1 -1
- package/dist/src/storage/prisma/client/schema.prisma +2 -3
- package/dist/src/storage/prisma/client/wasm.js +2 -1
- package/dist/src/storage/prisma/prisma.d.ts +13 -8
- package/dist/src/storage/prisma/prisma.d.ts.map +1 -1
- package/dist/src/storage/prisma/prisma.js +119 -51
- package/dist/src/storage/prisma/prisma.js.map +1 -1
- package/dist/src/storage/types.d.ts +45 -6
- package/dist/src/storage/types.d.ts.map +1 -1
- package/dist/src/storage/utils.d.ts +3 -0
- package/dist/src/storage/utils.d.ts.map +1 -1
- package/dist/src/storage/utils.js +14 -0
- package/dist/src/storage/utils.js.map +1 -1
- package/dist/src/utils/default-drives-manager.d.ts.map +1 -1
- package/dist/src/utils/default-drives-manager.js.map +1 -1
- package/dist/src/utils/gql-transformations.d.ts +14 -5
- package/dist/src/utils/gql-transformations.d.ts.map +1 -1
- package/dist/src/utils/gql-transformations.js +1 -0
- package/dist/src/utils/gql-transformations.js.map +1 -1
- package/dist/src/utils/graphql.d.ts +2 -2
- package/dist/src/utils/graphql.d.ts.map +1 -1
- package/dist/src/utils/migrations.d.ts.map +1 -1
- package/dist/src/utils/migrations.js.map +1 -1
- package/dist/src/utils/misc.d.ts +4 -2
- package/dist/src/utils/misc.d.ts.map +1 -1
- package/dist/src/utils/misc.js +4 -0
- package/dist/src/utils/misc.js.map +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +5 -4
- package/dist/test/cache.test.d.ts +0 -2
- package/dist/test/cache.test.d.ts.map +0 -1
- package/dist/test/cache.test.js +0 -281
- package/dist/test/cache.test.js.map +0 -1
- package/dist/test/default-remote-drives.test.d.ts +0 -2
- package/dist/test/default-remote-drives.test.d.ts.map +0 -1
- package/dist/test/default-remote-drives.test.js +0 -446
- package/dist/test/default-remote-drives.test.js.map +0 -1
- package/dist/test/document-helpers/addUndo.test.d.ts +0 -2
- package/dist/test/document-helpers/addUndo.test.d.ts.map +0 -1
- package/dist/test/document-helpers/addUndo.test.js +0 -120
- package/dist/test/document-helpers/addUndo.test.js.map +0 -1
- package/dist/test/document-helpers/attachBranch.test.d.ts +0 -2
- package/dist/test/document-helpers/attachBranch.test.d.ts.map +0 -1
- package/dist/test/document-helpers/attachBranch.test.js +0 -333
- package/dist/test/document-helpers/attachBranch.test.js.map +0 -1
- package/dist/test/document-helpers/checkCleanedOperationsIntegrity.test.d.ts +0 -2
- package/dist/test/document-helpers/checkCleanedOperationsIntegrity.test.d.ts.map +0 -1
- package/dist/test/document-helpers/checkCleanedOperationsIntegrity.test.js +0 -252
- package/dist/test/document-helpers/checkCleanedOperationsIntegrity.test.js.map +0 -1
- package/dist/test/document-helpers/garbageCollect.test.d.ts +0 -2
- package/dist/test/document-helpers/garbageCollect.test.d.ts.map +0 -1
- package/dist/test/document-helpers/garbageCollect.test.js +0 -136
- package/dist/test/document-helpers/garbageCollect.test.js.map +0 -1
- package/dist/test/document-helpers/groupOperationsByScope.test.d.ts +0 -2
- package/dist/test/document-helpers/groupOperationsByScope.test.d.ts.map +0 -1
- package/dist/test/document-helpers/groupOperationsByScope.test.js +0 -98
- package/dist/test/document-helpers/groupOperationsByScope.test.js.map +0 -1
- package/dist/test/document-helpers/merge.test.d.ts +0 -2
- package/dist/test/document-helpers/merge.test.d.ts.map +0 -1
- package/dist/test/document-helpers/merge.test.js +0 -757
- package/dist/test/document-helpers/merge.test.js.map +0 -1
- package/dist/test/document-helpers/nextSkipNumber.test.d.ts +0 -2
- package/dist/test/document-helpers/nextSkipNumber.test.d.ts.map +0 -1
- package/dist/test/document-helpers/nextSkipNumber.test.js +0 -123
- package/dist/test/document-helpers/nextSkipNumber.test.js.map +0 -1
- package/dist/test/document-helpers/prepareOperations.test.d.ts +0 -2
- package/dist/test/document-helpers/prepareOperations.test.d.ts.map +0 -1
- package/dist/test/document-helpers/prepareOperations.test.js +0 -304
- package/dist/test/document-helpers/prepareOperations.test.js.map +0 -1
- package/dist/test/document-helpers/removeExistingOperations.test.d.ts +0 -2
- package/dist/test/document-helpers/removeExistingOperations.test.d.ts.map +0 -1
- package/dist/test/document-helpers/removeExistingOperations.test.js +0 -150
- package/dist/test/document-helpers/removeExistingOperations.test.js.map +0 -1
- package/dist/test/document-helpers/reshuffleByTimestamp.test.d.ts +0 -2
- package/dist/test/document-helpers/reshuffleByTimestamp.test.d.ts.map +0 -1
- package/dist/test/document-helpers/reshuffleByTimestamp.test.js +0 -148
- package/dist/test/document-helpers/reshuffleByTimestamp.test.js.map +0 -1
- package/dist/test/document-helpers/reshuffleByTimestampAndIndex.test.d.ts +0 -2
- package/dist/test/document-helpers/reshuffleByTimestampAndIndex.test.d.ts.map +0 -1
- package/dist/test/document-helpers/reshuffleByTimestampAndIndex.test.js +0 -200
- package/dist/test/document-helpers/reshuffleByTimestampAndIndex.test.js.map +0 -1
- package/dist/test/document-helpers/sortOperations.test.d.ts +0 -2
- package/dist/test/document-helpers/sortOperations.test.d.ts.map +0 -1
- package/dist/test/document-helpers/sortOperations.test.js +0 -101
- package/dist/test/document-helpers/sortOperations.test.js.map +0 -1
- package/dist/test/document-helpers/split.test.d.ts +0 -2
- package/dist/test/document-helpers/split.test.d.ts.map +0 -1
- package/dist/test/document-helpers/split.test.js +0 -121
- package/dist/test/document-helpers/split.test.js.map +0 -1
- package/dist/test/document-helpers/utils.d.ts +0 -8
- package/dist/test/document-helpers/utils.d.ts.map +0 -1
- package/dist/test/document-helpers/utils.js +0 -22
- package/dist/test/document-helpers/utils.js.map +0 -1
- package/dist/test/drive-operations.test.d.ts +0 -2
- package/dist/test/drive-operations.test.d.ts.map +0 -1
- package/dist/test/drive-operations.test.js +0 -145
- package/dist/test/drive-operations.test.js.map +0 -1
- package/dist/test/graphql.test.d.ts +0 -2
- package/dist/test/graphql.test.d.ts.map +0 -1
- package/dist/test/graphql.test.js +0 -10
- package/dist/test/graphql.test.js.map +0 -1
- package/dist/test/internal-listener.test.d.ts +0 -2
- package/dist/test/internal-listener.test.d.ts.map +0 -1
- package/dist/test/internal-listener.test.js +0 -277
- package/dist/test/internal-listener.test.js.map +0 -1
- package/dist/test/queue.test.d.ts +0 -2
- package/dist/test/queue.test.d.ts.map +0 -1
- package/dist/test/queue.test.js +0 -338
- package/dist/test/queue.test.js.map +0 -1
- package/dist/test/read-mode.test.d.ts +0 -2
- package/dist/test/read-mode.test.d.ts.map +0 -1
- package/dist/test/read-mode.test.js +0 -578
- package/dist/test/read-mode.test.js.map +0 -1
- package/dist/test/server/driveOperationsConflictResolution.test.d.ts +0 -2
- package/dist/test/server/driveOperationsConflictResolution.test.d.ts.map +0 -1
- package/dist/test/server/driveOperationsConflictResolution.test.js +0 -460
- package/dist/test/server/driveOperationsConflictResolution.test.js.map +0 -1
- package/dist/test/server/mergeOperations.test.d.ts +0 -2
- package/dist/test/server/mergeOperations.test.d.ts.map +0 -1
- package/dist/test/server/mergeOperations.test.js +0 -107
- package/dist/test/server/mergeOperations.test.js.map +0 -1
- package/dist/test/server/processOperations.test.d.ts +0 -2
- package/dist/test/server/processOperations.test.d.ts.map +0 -1
- package/dist/test/server/processOperations.test.js +0 -380
- package/dist/test/server/processOperations.test.js.map +0 -1
- package/dist/test/server.test.d.ts +0 -2
- package/dist/test/server.test.d.ts.map +0 -1
- package/dist/test/server.test.js +0 -899
- package/dist/test/server.test.js.map +0 -1
- package/dist/test/signature-migration.test.d.ts +0 -2
- package/dist/test/signature-migration.test.d.ts.map +0 -1
- package/dist/test/signature-migration.test.js +0 -239
- package/dist/test/signature-migration.test.js.map +0 -1
- package/dist/test/storage.test.d.ts +0 -2
- package/dist/test/storage.test.d.ts.map +0 -1
- package/dist/test/storage.test.js +0 -523
- package/dist/test/storage.test.js.map +0 -1
- package/dist/test/utils.d.ts +0 -48
- package/dist/test/utils.d.ts.map +0 -1
- package/dist/test/utils.js +0 -133
- package/dist/test/utils.js.map +0 -1
- package/dist/test/utils.test.d.ts +0 -2
- package/dist/test/utils.test.d.ts.map +0 -1
- package/dist/test/utils.test.js +0 -89
- package/dist/test/utils.test.js.map +0 -1
- package/dist/test/vitest-setup.d.ts +0 -2
- package/dist/test/vitest-setup.d.ts.map +0 -1
- package/dist/test/vitest-setup.js +0 -5
- package/dist/test/vitest-setup.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/vitest.config.d.ts +0 -3
- package/dist/vitest.config.d.ts.map +0 -1
- package/dist/vitest.config.js +0 -27
- package/dist/vitest.config.js.map +0 -1
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { removeListener, removeTrigger, setSharingType, } from "#drive-document-model/gen/creators";
|
|
2
2
|
import { createDocument } from "#drive-document-model/gen/utils";
|
|
3
|
-
import { isActionJob, isOperationJob, } from "#queue/types";
|
|
3
|
+
import { isActionJob, isDocumentJob, isOperationJob, } from "#queue/types";
|
|
4
4
|
import { ReadModeServer } from "#read-mode/server";
|
|
5
5
|
import { DefaultDrivesManager, } from "#utils/default-drives-manager";
|
|
6
6
|
import { requestPublicDriveWithTokenFromReactor } from "#utils/graphql";
|
|
7
7
|
import { isDocumentDrive, runAsapAsync } from "#utils/misc";
|
|
8
8
|
import { RunAsap } from "#utils/run-asap";
|
|
9
|
-
import { childLogger, } from "document-drive";
|
|
9
|
+
import { DocumentAlreadyExistsError, childLogger, } from "document-drive";
|
|
10
10
|
import { attachBranch, createPresignedHeader, garbageCollect, garbageCollectDocumentOperations, groupOperationsByScope, merge, precedes, removeExistingOperations, replayDocument, reshuffleByTimestamp, skipHeaderOperations, sortOperations, validateHeader, } from "document-model";
|
|
11
11
|
import { ClientError } from "graphql-request";
|
|
12
12
|
import { ConflictOperationError, OperationError, } from "./error.js";
|
|
13
13
|
import { PullResponderTransmitter, } from "./listener/transmitter/pull-responder.js";
|
|
14
14
|
import { SwitchboardPushTransmitter } from "./listener/transmitter/switchboard-push.js";
|
|
15
15
|
import { DefaultListenerManagerOptions, } from "./types.js";
|
|
16
|
-
import { filterOperationsByRevision, isAtRevision } from "./utils.js";
|
|
16
|
+
import { filterOperationsByRevision, isAtRevision, resolveCreateDocumentInput, } from "./utils.js";
|
|
17
17
|
export class BaseDocumentDriveServer {
|
|
18
18
|
logger = childLogger(["BaseDocumentDriveServer"]);
|
|
19
19
|
// external dependencies
|
|
@@ -35,15 +35,47 @@ export class BaseDocumentDriveServer {
|
|
|
35
35
|
};
|
|
36
36
|
queueDelegate = {
|
|
37
37
|
exists: (documentId) => this.documentStorage.exists(documentId),
|
|
38
|
-
processOperationJob: async ({
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
processOperationJob: async ({ documentId, operations, options, }) => {
|
|
39
|
+
const document = await this.getDocument(documentId);
|
|
40
|
+
return isDocumentDrive(document)
|
|
41
|
+
? this.processDriveOperations(documentId, operations, options)
|
|
42
|
+
: this.processOperations(documentId, operations, options);
|
|
42
43
|
},
|
|
43
|
-
processActionJob: async ({
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
processActionJob: async ({ documentId, actions, options }) => {
|
|
45
|
+
const document = await this.getDocument(documentId);
|
|
46
|
+
return isDocumentDrive(document)
|
|
47
|
+
? this.processDriveActions(documentId, actions, options)
|
|
48
|
+
: this.processActions(documentId, actions, options);
|
|
49
|
+
},
|
|
50
|
+
processDocumentJob: async ({ documentId, documentType, initialState, options, }) => {
|
|
51
|
+
const documentModelModule = this.getDocumentModelModule(documentType);
|
|
52
|
+
const document = documentModelModule.utils.createDocument({
|
|
53
|
+
...initialState,
|
|
54
|
+
});
|
|
55
|
+
// TODO: header must be included
|
|
56
|
+
const header = createPresignedHeader(documentId, documentType);
|
|
57
|
+
document.header.id = documentId;
|
|
58
|
+
document.header.sig = header.sig;
|
|
59
|
+
document.header.documentType = documentType;
|
|
60
|
+
try {
|
|
61
|
+
const createdDocument = await this.createDocument({ document }, options?.source ?? { type: "local" }, initialState?.header.meta);
|
|
62
|
+
return {
|
|
63
|
+
status: "SUCCESS",
|
|
64
|
+
operations: [],
|
|
65
|
+
document: createdDocument,
|
|
66
|
+
signals: [],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
const cause = error instanceof Error ? error : new Error(JSON.stringify(error));
|
|
71
|
+
return {
|
|
72
|
+
status: "ERROR",
|
|
73
|
+
error: new OperationError("ERROR", undefined, `Error creating document: ${cause.message}`, cause),
|
|
74
|
+
operations: [],
|
|
75
|
+
document: undefined,
|
|
76
|
+
signals: [],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
47
79
|
},
|
|
48
80
|
processJob: async (job) => {
|
|
49
81
|
if (isOperationJob(job)) {
|
|
@@ -52,6 +84,9 @@ export class BaseDocumentDriveServer {
|
|
|
52
84
|
else if (isActionJob(job)) {
|
|
53
85
|
return this.queueDelegate.processActionJob(job);
|
|
54
86
|
}
|
|
87
|
+
else if (isDocumentJob(job)) {
|
|
88
|
+
return this.queueDelegate.processDocumentJob(job);
|
|
89
|
+
}
|
|
55
90
|
else {
|
|
56
91
|
throw new Error("Unknown job type", job);
|
|
57
92
|
}
|
|
@@ -98,7 +133,7 @@ export class BaseDocumentDriveServer {
|
|
|
98
133
|
return this.initializePromise;
|
|
99
134
|
}
|
|
100
135
|
async _initialize() {
|
|
101
|
-
await this.listenerManager.initialize(this.handleListenerError);
|
|
136
|
+
await this.listenerManager.initialize(this.handleListenerError.bind(this));
|
|
102
137
|
await this.queueManager.init(this.queueDelegate, (error) => {
|
|
103
138
|
this.logger.error(`Error initializing queue manager`, error);
|
|
104
139
|
errors.push(error);
|
|
@@ -153,7 +188,7 @@ export class BaseDocumentDriveServer {
|
|
|
153
188
|
}
|
|
154
189
|
async startSyncRemoteDrive(driveId) {
|
|
155
190
|
let driveTriggers = this.triggerMap.get(driveId);
|
|
156
|
-
const syncUnits = await this.getSynchronizationUnitsIds(driveId);
|
|
191
|
+
const syncUnits = await this.synchronizationManager.getSynchronizationUnitsIds(driveId);
|
|
157
192
|
const drive = await this.getDrive(driveId);
|
|
158
193
|
for (const trigger of drive.state.local.triggers) {
|
|
159
194
|
if (driveTriggers?.get(trigger.id)) {
|
|
@@ -166,7 +201,7 @@ export class BaseDocumentDriveServer {
|
|
|
166
201
|
pull: "SYNCING",
|
|
167
202
|
});
|
|
168
203
|
for (const syncUnit of syncUnits) {
|
|
169
|
-
this.synchronizationManager.updateSyncStatus(syncUnit
|
|
204
|
+
this.synchronizationManager.updateSyncStatus(syncUnit, {
|
|
170
205
|
pull: "SYNCING",
|
|
171
206
|
});
|
|
172
207
|
}
|
|
@@ -178,46 +213,43 @@ export class BaseDocumentDriveServer {
|
|
|
178
213
|
if (error instanceof ClientError) {
|
|
179
214
|
this.eventEmitter.emit("clientStrandsError", driveId, trigger, error.response.status, error.message);
|
|
180
215
|
}
|
|
181
|
-
}, (revisions) => {
|
|
182
|
-
const
|
|
183
|
-
if (
|
|
216
|
+
}, async (revisions) => {
|
|
217
|
+
const errorRevisions = revisions.filter((r) => r.status !== "SUCCESS");
|
|
218
|
+
if (errorRevisions.length < 1) {
|
|
184
219
|
this.synchronizationManager.updateSyncStatus(driveId, {
|
|
185
220
|
pull: "SUCCESS",
|
|
186
221
|
});
|
|
187
222
|
}
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
.
|
|
191
|
-
|
|
192
|
-
.then((revSyncUnits) => {
|
|
193
|
-
for (const syncUnit of revSyncUnits) {
|
|
194
|
-
const fileErrorRevision = errorRevision.find((r) => r.documentId === syncUnit.documentId);
|
|
195
|
-
if (fileErrorRevision) {
|
|
196
|
-
this.synchronizationManager.updateSyncStatus(syncUnit.syncId, { pull: fileErrorRevision.status }, fileErrorRevision.error);
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
this.synchronizationManager.updateSyncStatus(syncUnit.syncId, {
|
|
200
|
-
pull: "SUCCESS",
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
})
|
|
205
|
-
.catch(console.error);
|
|
223
|
+
for (const revision of revisions) {
|
|
224
|
+
const { documentId, scope, branch, status, error } = revision;
|
|
225
|
+
this.synchronizationManager.updateSyncStatus({ documentId, scope, branch }, { pull: status }, error);
|
|
226
|
+
}
|
|
206
227
|
// if it is the first pull and returns empty
|
|
207
|
-
// then updates
|
|
228
|
+
// then updates drive documents to "SUCCESS" and
|
|
229
|
+
// updates corresponding push transmitter
|
|
208
230
|
if (firstPull) {
|
|
209
231
|
firstPull = false;
|
|
232
|
+
const syncUnitsIds = await this.synchronizationManager.getSynchronizationUnitsIds(driveId);
|
|
233
|
+
const unchangedSyncUnits = syncUnitsIds.filter((syncUnit) => {
|
|
234
|
+
return !revisions.find((revision) => {
|
|
235
|
+
return (revision.documentId === syncUnit.documentId &&
|
|
236
|
+
revision.scope === syncUnit.scope &&
|
|
237
|
+
revision.branch === syncUnit.branch);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
unchangedSyncUnits.forEach((syncUnit) => {
|
|
241
|
+
this.synchronizationManager.updateSyncStatus(syncUnit, {
|
|
242
|
+
pull: "SUCCESS",
|
|
243
|
+
});
|
|
244
|
+
});
|
|
210
245
|
const pushListener = drive.state.local.listeners.find((listener) => trigger.data.url === listener.callInfo?.data);
|
|
211
246
|
if (pushListener) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
})
|
|
220
|
-
.catch(this.logger.error);
|
|
247
|
+
for (const revision of revisions) {
|
|
248
|
+
const { documentId, scope, branch } = revision;
|
|
249
|
+
this.listenerManager
|
|
250
|
+
.updateListenerRevision(pushListener.listenerId, driveId, { documentId, scope, branch }, revision.revision)
|
|
251
|
+
.catch(this.logger.error);
|
|
252
|
+
}
|
|
221
253
|
}
|
|
222
254
|
}
|
|
223
255
|
}, undefined, this.listeners);
|
|
@@ -227,15 +259,12 @@ export class BaseDocumentDriveServer {
|
|
|
227
259
|
}
|
|
228
260
|
}
|
|
229
261
|
async stopSyncRemoteDrive(driveId) {
|
|
230
|
-
const syncUnits = await this.getSynchronizationUnitsIds(driveId);
|
|
231
|
-
const filesNodeSyncId = syncUnits
|
|
232
|
-
.filter((syncUnit) => syncUnit.documentId !== "")
|
|
233
|
-
.map((syncUnit) => syncUnit.syncId);
|
|
234
262
|
const triggers = this.triggerMap.get(driveId);
|
|
235
263
|
triggers?.forEach((cancel) => cancel());
|
|
236
264
|
this.synchronizationManager.updateSyncStatus(driveId, null);
|
|
237
|
-
|
|
238
|
-
|
|
265
|
+
const syncUnits = await this.synchronizationManager.getSynchronizationUnitsIds(driveId);
|
|
266
|
+
for (const syncUnit of syncUnits) {
|
|
267
|
+
this.synchronizationManager.updateSyncStatus(syncUnit, null);
|
|
239
268
|
}
|
|
240
269
|
return this.triggerMap.delete(driveId);
|
|
241
270
|
}
|
|
@@ -298,19 +327,6 @@ export class BaseDocumentDriveServer {
|
|
|
298
327
|
}
|
|
299
328
|
}
|
|
300
329
|
}
|
|
301
|
-
// Delegate synchronization methods to synchronizationManager
|
|
302
|
-
getSynchronizationUnits(driveId, documentId, scope, branch, documentType) {
|
|
303
|
-
return this.synchronizationManager.getSynchronizationUnits(driveId, documentId, scope, branch, documentType);
|
|
304
|
-
}
|
|
305
|
-
getSynchronizationUnitsIds(driveId, documentId, scope, branch, documentType) {
|
|
306
|
-
return this.synchronizationManager.getSynchronizationUnitsIds(driveId, documentId, scope, branch, documentType);
|
|
307
|
-
}
|
|
308
|
-
getOperationData(driveId, syncId, filter) {
|
|
309
|
-
return this.synchronizationManager.getOperationData(driveId, syncId, filter);
|
|
310
|
-
}
|
|
311
|
-
getSynchronizationUnitsRevision(driveId, syncUnitsQuery) {
|
|
312
|
-
return this.synchronizationManager.getSynchronizationUnitsRevision(driveId, syncUnitsQuery);
|
|
313
|
-
}
|
|
314
330
|
getDocumentModelModule(documentType) {
|
|
315
331
|
const documentModelModule = this.documentModelModules.find((module) => module.documentModel.id === documentType);
|
|
316
332
|
if (!documentModelModule) {
|
|
@@ -321,6 +337,12 @@ export class BaseDocumentDriveServer {
|
|
|
321
337
|
getDocumentModelModules() {
|
|
322
338
|
return [...this.documentModelModules];
|
|
323
339
|
}
|
|
340
|
+
addDocument(documentOrType, meta) {
|
|
341
|
+
const input = typeof documentOrType === "string"
|
|
342
|
+
? { documentType: documentOrType }
|
|
343
|
+
: { document: documentOrType };
|
|
344
|
+
return this.createDocument(input, { type: "local" }, meta);
|
|
345
|
+
}
|
|
324
346
|
async addDrive(input, preferredEditor) {
|
|
325
347
|
const document = createDocument({
|
|
326
348
|
state: {
|
|
@@ -428,6 +450,7 @@ export class BaseDocumentDriveServer {
|
|
|
428
450
|
}
|
|
429
451
|
else {
|
|
430
452
|
if (!options?.revisions) {
|
|
453
|
+
this.cache.setDocument(driveId, result).catch(this.logger.error);
|
|
431
454
|
this.cache.setDrive(driveId, result).catch(this.logger.error);
|
|
432
455
|
}
|
|
433
456
|
return result;
|
|
@@ -466,7 +489,12 @@ export class BaseDocumentDriveServer {
|
|
|
466
489
|
const driveStorage = await this.documentStorage.getBySlug(slug);
|
|
467
490
|
return driveStorage.header.id;
|
|
468
491
|
}
|
|
469
|
-
|
|
492
|
+
getDocument(driveId, documentId, options) {
|
|
493
|
+
const id = typeof documentId === "string" ? documentId : driveId;
|
|
494
|
+
const resolvedOptions = typeof documentId === "object" ? documentId : options;
|
|
495
|
+
return this._getDocument(id, resolvedOptions);
|
|
496
|
+
}
|
|
497
|
+
async _getDocument(documentId, options) {
|
|
470
498
|
let cachedDocument;
|
|
471
499
|
try {
|
|
472
500
|
cachedDocument = await this.cache.getDocument(documentId); // TODO support GetDocumentOptions
|
|
@@ -487,115 +515,166 @@ export class BaseDocumentDriveServer {
|
|
|
487
515
|
getDocuments(driveId) {
|
|
488
516
|
return this.documentStorage.getChildren(driveId);
|
|
489
517
|
}
|
|
490
|
-
async
|
|
518
|
+
async addChild(parentId, documentId) {
|
|
519
|
+
// TODO: check if document exists? Should that be a concern here?
|
|
520
|
+
try {
|
|
521
|
+
await this.documentStorage.addChild(parentId, documentId);
|
|
522
|
+
// TODO: update listener manager?
|
|
523
|
+
}
|
|
524
|
+
catch (e) {
|
|
525
|
+
this.logger.error("Error adding child document", e);
|
|
526
|
+
throw e;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
async removeChild(parentId, documentId) {
|
|
530
|
+
// TODO: check if document exists? Should that be a concern here?
|
|
531
|
+
// cleanup child sync units state from the parent listeners
|
|
532
|
+
try {
|
|
533
|
+
const childSynUnits = await this.synchronizationManager.getSynchronizationUnitsIds(parentId, [
|
|
534
|
+
documentId,
|
|
535
|
+
]);
|
|
536
|
+
await this.listenerManager.removeSyncUnits(parentId, childSynUnits);
|
|
537
|
+
}
|
|
538
|
+
catch (e) {
|
|
539
|
+
this.logger.warn("Error removing sync units of child", e);
|
|
540
|
+
}
|
|
541
|
+
// remove child relationship from storage
|
|
542
|
+
try {
|
|
543
|
+
await this.documentStorage.removeChild(parentId, documentId);
|
|
544
|
+
}
|
|
545
|
+
catch (e) {
|
|
546
|
+
this.logger.error("Error adding child document", e);
|
|
547
|
+
throw e;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
async createDocument(input, source, meta) {
|
|
551
|
+
const { documentType, document: inputDocument } = resolveCreateDocumentInput(input);
|
|
491
552
|
// if a document was provided then checks if it's valid
|
|
492
553
|
let state = undefined;
|
|
493
|
-
if (
|
|
494
|
-
if (
|
|
495
|
-
|
|
554
|
+
if (inputDocument) {
|
|
555
|
+
if ("documentType" in input &&
|
|
556
|
+
documentType !== inputDocument.header.documentType) {
|
|
557
|
+
throw new Error(`Provided document is not ${documentType}`);
|
|
496
558
|
}
|
|
497
|
-
const doc = this._buildDocument(
|
|
559
|
+
const doc = this._buildDocument(inputDocument);
|
|
498
560
|
state = doc.state;
|
|
499
561
|
}
|
|
500
562
|
// if no document was provided then create a new one
|
|
501
|
-
const document =
|
|
502
|
-
this.getDocumentModelModule(
|
|
563
|
+
const document = inputDocument ??
|
|
564
|
+
this.getDocumentModelModule(documentType).utils.createDocument({
|
|
565
|
+
state,
|
|
566
|
+
});
|
|
503
567
|
// get the header
|
|
504
568
|
let header;
|
|
505
569
|
// handle the legacy case where an id is provided
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
if (input.document) {
|
|
570
|
+
if ("id" in input && input.id) {
|
|
571
|
+
if (inputDocument) {
|
|
509
572
|
header = document.header;
|
|
510
|
-
// eslint-disable-next-line
|
|
511
573
|
document.header.id = input.id;
|
|
512
574
|
this.logger.warn("Assigning an id to a document is deprecated. Use the header field instead.");
|
|
513
575
|
}
|
|
514
576
|
else {
|
|
515
577
|
this.logger.warn("Creating a document with an id is deprecated. Use the header field instead.");
|
|
516
|
-
header = createPresignedHeader();
|
|
517
|
-
// eslint-disable-next-line
|
|
518
|
-
header.id = input.id;
|
|
519
|
-
header.documentType = input.documentType;
|
|
578
|
+
header = createPresignedHeader(input.id, documentType);
|
|
520
579
|
}
|
|
521
580
|
}
|
|
522
|
-
else if (input
|
|
581
|
+
else if ("header" in input) {
|
|
523
582
|
// validate the header passed in
|
|
524
583
|
await validateHeader(input.header);
|
|
525
584
|
header = input.header;
|
|
526
585
|
}
|
|
586
|
+
else if (inputDocument?.header) {
|
|
587
|
+
if (!inputDocument.header.id) {
|
|
588
|
+
throw new Error("Document header id is required");
|
|
589
|
+
}
|
|
590
|
+
if (!inputDocument.header.documentType) {
|
|
591
|
+
throw new Error("Document header documentType is required");
|
|
592
|
+
}
|
|
593
|
+
if (!inputDocument.header.createdAtUtcIso) {
|
|
594
|
+
throw new Error("Document header createdAtUtcIso is required");
|
|
595
|
+
}
|
|
596
|
+
if (!inputDocument.header.sig.nonce) {
|
|
597
|
+
this.logger.warn("Creating a document with an unsigned id is deprecated. Use createSignedHeaderForSigner.");
|
|
598
|
+
// throw new Error("Document header sig nonce is required"); TODO: uncomment when ready to enforce signed documents
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
await validateHeader(inputDocument.header);
|
|
602
|
+
}
|
|
603
|
+
header = inputDocument.header;
|
|
604
|
+
}
|
|
527
605
|
else {
|
|
528
606
|
// otherwise, generate a header
|
|
529
|
-
header = createPresignedHeader();
|
|
530
|
-
|
|
607
|
+
header = createPresignedHeader(undefined, documentType);
|
|
608
|
+
}
|
|
609
|
+
if (meta) {
|
|
610
|
+
header.meta = { ...header.meta, ...meta };
|
|
531
611
|
}
|
|
532
612
|
// stores document information
|
|
533
613
|
const documentStorage = {
|
|
534
614
|
header,
|
|
535
615
|
history: document.history,
|
|
536
|
-
operations:
|
|
616
|
+
operations: { global: [], local: [] },
|
|
537
617
|
initialState: document.initialState,
|
|
538
618
|
clipboard: [],
|
|
619
|
+
attachments: document.attachments,
|
|
539
620
|
state: state ?? document.state,
|
|
540
621
|
};
|
|
541
622
|
await this.documentStorage.create(documentStorage);
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
//
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
push: this.listenerManager.driveHasListeners(driveId)
|
|
561
|
-
? "SUCCESS"
|
|
562
|
-
: undefined,
|
|
563
|
-
});
|
|
564
|
-
}
|
|
623
|
+
// TODO set initial state for document sync units
|
|
624
|
+
// if (source.type === "trigger") {
|
|
625
|
+
// for (const scope of Object.keys(document.state)) {
|
|
626
|
+
// this.synchronizationManager.updateSyncStatus(
|
|
627
|
+
// {
|
|
628
|
+
// documentId: document.id,
|
|
629
|
+
// scope,
|
|
630
|
+
// branch: "main" /* TODO handle branches */,
|
|
631
|
+
// },
|
|
632
|
+
// {
|
|
633
|
+
// pull: "INITIAL_SYNC",
|
|
634
|
+
// push: this.listenerManager.driveHasListeners(driveId)
|
|
635
|
+
// ? "SUCCESS"
|
|
636
|
+
// : undefined,
|
|
637
|
+
// },
|
|
638
|
+
// );
|
|
639
|
+
// }
|
|
640
|
+
// }
|
|
565
641
|
// if the document contains operations then
|
|
566
642
|
// stores the operations in the storage
|
|
567
643
|
const operations = Object.values(document.operations).flat();
|
|
568
644
|
if (operations.length) {
|
|
569
645
|
if (isDocumentDrive(document)) {
|
|
570
|
-
await this.legacyStorage.addDriveOperations(
|
|
646
|
+
await this.legacyStorage.addDriveOperations(header.id, operations, document);
|
|
571
647
|
}
|
|
572
648
|
else {
|
|
573
|
-
await this.legacyStorage.addDocumentOperations(
|
|
649
|
+
await this.legacyStorage.addDocumentOperations(header.id, operations, document);
|
|
574
650
|
}
|
|
575
651
|
}
|
|
576
652
|
return document;
|
|
577
653
|
}
|
|
578
|
-
async deleteDocument(
|
|
654
|
+
async deleteDocument(documentId) {
|
|
579
655
|
try {
|
|
580
|
-
const syncUnits = await this.getSynchronizationUnitsIds(
|
|
581
|
-
documentId,
|
|
582
|
-
]);
|
|
656
|
+
const syncUnits = await this.synchronizationManager.getSynchronizationUnitsIds(undefined, [documentId]);
|
|
583
657
|
// remove document sync units status when a document is deleted
|
|
584
658
|
for (const syncUnit of syncUnits) {
|
|
585
|
-
this.synchronizationManager.updateSyncStatus(syncUnit
|
|
659
|
+
this.synchronizationManager.updateSyncStatus(syncUnit, null);
|
|
660
|
+
}
|
|
661
|
+
const parents = await this.documentStorage.getParents(documentId);
|
|
662
|
+
for (const parent of parents) {
|
|
663
|
+
this.listenerManager
|
|
664
|
+
.removeSyncUnits(parent, syncUnits)
|
|
665
|
+
.catch(this.logger.warn);
|
|
586
666
|
}
|
|
587
|
-
await this.listenerManager.removeSyncUnits(driveId, syncUnits);
|
|
588
667
|
}
|
|
589
668
|
catch (error) {
|
|
590
669
|
this.logger.warn("Error deleting document", error);
|
|
591
670
|
}
|
|
592
671
|
await this.cache.deleteDocument(documentId);
|
|
593
|
-
|
|
672
|
+
await this.documentStorage.delete(documentId);
|
|
594
673
|
}
|
|
595
|
-
async _processOperations(
|
|
674
|
+
async _processOperations(documentId, documentStorage, operations) {
|
|
596
675
|
const operationsApplied = [];
|
|
597
676
|
const signals = [];
|
|
598
|
-
const documentStorageWithState = await this._addDocumentResultingStage(documentStorage,
|
|
677
|
+
const documentStorageWithState = await this._addDocumentResultingStage(documentStorage, documentId);
|
|
599
678
|
let document = this._buildDocument(documentStorageWithState);
|
|
600
679
|
let error; // TODO: replace with an array of errors/consistency issues
|
|
601
680
|
const operationsByScope = groupOperationsByScope(operations);
|
|
@@ -627,7 +706,7 @@ export class BaseDocumentDriveServer {
|
|
|
627
706
|
try {
|
|
628
707
|
// runs operation on next available tick, to avoid blocking the main thread
|
|
629
708
|
const taskQueueMethod = this.options.taskQueueMethod;
|
|
630
|
-
const task = () => this._performOperation(
|
|
709
|
+
const task = () => this._performOperation(documentId, document, nextOperation, skipHashValidation);
|
|
631
710
|
const appliedResult = await (taskQueueMethod
|
|
632
711
|
? runAsapAsync(task, taskQueueMethod)
|
|
633
712
|
: task());
|
|
@@ -653,7 +732,7 @@ export class BaseDocumentDriveServer {
|
|
|
653
732
|
error,
|
|
654
733
|
};
|
|
655
734
|
}
|
|
656
|
-
async _addDocumentResultingStage(document,
|
|
735
|
+
async _addDocumentResultingStage(document, documentId, options) {
|
|
657
736
|
// apply skip header operations to all scopes
|
|
658
737
|
const operations = options?.revisions !== undefined
|
|
659
738
|
? filterOperationsByRevision(document.operations, options.revisions)
|
|
@@ -664,9 +743,9 @@ export class BaseDocumentDriveServer {
|
|
|
664
743
|
// if the latest operation doesn't have a resulting state then tries
|
|
665
744
|
// to retrieve it from the db to avoid rerunning all the operations
|
|
666
745
|
if (lastRemainingOperation && !lastRemainingOperation.resultingState) {
|
|
667
|
-
lastRemainingOperation.resultingState = await (
|
|
668
|
-
? this.legacyStorage.getOperationResultingState?.(
|
|
669
|
-
: this.legacyStorage.getDriveOperationResultingState?.(
|
|
746
|
+
lastRemainingOperation.resultingState = await (isDocumentDrive(document)
|
|
747
|
+
? this.legacyStorage.getOperationResultingState?.(documentId, lastRemainingOperation.index, lastRemainingOperation.scope, "main")
|
|
748
|
+
: this.legacyStorage.getDriveOperationResultingState?.(documentId, lastRemainingOperation.index, lastRemainingOperation.scope, "main"));
|
|
670
749
|
}
|
|
671
750
|
}
|
|
672
751
|
return {
|
|
@@ -691,7 +770,7 @@ export class BaseDocumentDriveServer {
|
|
|
691
770
|
reuseOperationResultingState: options?.checkHashes ?? true,
|
|
692
771
|
});
|
|
693
772
|
}
|
|
694
|
-
async _performOperation(
|
|
773
|
+
async _performOperation(documentId, document, operation, skipHashValidation = false) {
|
|
695
774
|
const documentModelModule = this.getDocumentModelModule(document.header.documentType);
|
|
696
775
|
const signalResults = [];
|
|
697
776
|
let newDocument = document;
|
|
@@ -704,36 +783,22 @@ export class BaseDocumentDriveServer {
|
|
|
704
783
|
// if the latest operation doesn't have a resulting state then tries
|
|
705
784
|
// to retrieve it from the db to avoid rerunning all the operations
|
|
706
785
|
if (lastRemainingOperation && !lastRemainingOperation.resultingState) {
|
|
707
|
-
lastRemainingOperation.resultingState = await (
|
|
708
|
-
? this.legacyStorage.getOperationResultingState?.(
|
|
709
|
-
: this.legacyStorage.getDriveOperationResultingState?.(
|
|
786
|
+
lastRemainingOperation.resultingState = await (isDocumentDrive(document)
|
|
787
|
+
? this.legacyStorage.getOperationResultingState?.(documentId, lastRemainingOperation.index, lastRemainingOperation.scope, "main")
|
|
788
|
+
: this.legacyStorage.getDriveOperationResultingState?.(documentId, lastRemainingOperation.index, lastRemainingOperation.scope, "main"));
|
|
710
789
|
}
|
|
711
790
|
const operationSignals = [];
|
|
712
791
|
newDocument = documentModelModule.reducer(newDocument, operation, (signal) => {
|
|
713
792
|
let handler = undefined;
|
|
714
793
|
switch (signal.type) {
|
|
715
794
|
case "CREATE_CHILD_DOCUMENT":
|
|
716
|
-
handler = () => this.
|
|
795
|
+
handler = () => this.addChild(documentId, signal.input.id);
|
|
717
796
|
break;
|
|
718
797
|
case "DELETE_CHILD_DOCUMENT":
|
|
719
|
-
handler = () => this.
|
|
798
|
+
handler = () => this.removeChild(documentId, signal.input.id);
|
|
720
799
|
break;
|
|
721
800
|
case "COPY_CHILD_DOCUMENT":
|
|
722
|
-
handler = () => this.
|
|
723
|
-
const doc = {
|
|
724
|
-
...documentToCopy,
|
|
725
|
-
header: {
|
|
726
|
-
...documentToCopy.header,
|
|
727
|
-
slug: signal.input.newId,
|
|
728
|
-
},
|
|
729
|
-
};
|
|
730
|
-
return this.createDocument(driveId, {
|
|
731
|
-
id: signal.input.newId,
|
|
732
|
-
documentType: documentToCopy.header.documentType,
|
|
733
|
-
document: doc,
|
|
734
|
-
synchronizationUnits: signal.input.synchronizationUnits,
|
|
735
|
-
});
|
|
736
|
-
});
|
|
801
|
+
handler = () => this.addChild(documentId, signal.input.newId);
|
|
737
802
|
break;
|
|
738
803
|
}
|
|
739
804
|
if (handler) {
|
|
@@ -748,6 +813,7 @@ export class BaseDocumentDriveServer {
|
|
|
748
813
|
if (!appliedOperation.error &&
|
|
749
814
|
appliedOperation.hash !== operation.hash &&
|
|
750
815
|
!skipHashValidation) {
|
|
816
|
+
this.logger.warn(JSON.stringify(appliedOperation, null, 2));
|
|
751
817
|
throw new ConflictOperationError(operation, appliedOperation);
|
|
752
818
|
}
|
|
753
819
|
for (const signalHandler of operationSignals) {
|
|
@@ -760,28 +826,103 @@ export class BaseDocumentDriveServer {
|
|
|
760
826
|
operation: appliedOperation,
|
|
761
827
|
};
|
|
762
828
|
}
|
|
763
|
-
addOperation(
|
|
764
|
-
|
|
829
|
+
addOperation(driveIdOrDocumentId, documentIdOrOperation, operationOrOptions, maybeOptions) {
|
|
830
|
+
let documentId;
|
|
831
|
+
let operation;
|
|
832
|
+
let options;
|
|
833
|
+
if (typeof documentIdOrOperation === "string") {
|
|
834
|
+
// Deprecated overload: (driveId, documentId, operation, options)
|
|
835
|
+
documentId = documentIdOrOperation;
|
|
836
|
+
operation = operationOrOptions;
|
|
837
|
+
options = maybeOptions;
|
|
838
|
+
}
|
|
839
|
+
else {
|
|
840
|
+
// Standard overload: (documentId, operation, options)
|
|
841
|
+
documentId = driveIdOrDocumentId;
|
|
842
|
+
operation = documentIdOrOperation;
|
|
843
|
+
options = operationOrOptions;
|
|
844
|
+
}
|
|
845
|
+
return this.addOperations(documentId, [operation], options);
|
|
765
846
|
}
|
|
766
|
-
async _addOperations(
|
|
847
|
+
async _addOperations(documentId, callback) {
|
|
767
848
|
if (!this.legacyStorage.addDocumentOperationsWithTransaction) {
|
|
768
849
|
const documentStorage = await this.documentStorage.get(documentId);
|
|
769
850
|
const result = await callback(documentStorage);
|
|
770
851
|
// saves the applied operations to storage
|
|
771
852
|
if (result.operations.length > 0) {
|
|
772
|
-
await this.legacyStorage.addDocumentOperations(
|
|
853
|
+
await this.legacyStorage.addDocumentOperations(documentId, result.operations, result.document);
|
|
773
854
|
}
|
|
774
855
|
}
|
|
775
856
|
else {
|
|
776
|
-
await this.legacyStorage.addDocumentOperationsWithTransaction(
|
|
857
|
+
await this.legacyStorage.addDocumentOperationsWithTransaction(documentId, callback);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
async queueDocument(input, options) {
|
|
861
|
+
const { id, documentType, document } = resolveCreateDocumentInput(input);
|
|
862
|
+
if (!id) {
|
|
863
|
+
throw new Error("Document id is required", { cause: input });
|
|
864
|
+
}
|
|
865
|
+
if (!documentType) {
|
|
866
|
+
throw new Error("Document type is required", { cause: input });
|
|
867
|
+
}
|
|
868
|
+
const exists = await this.documentStorage.exists(id);
|
|
869
|
+
if (exists) {
|
|
870
|
+
throw new DocumentAlreadyExistsError(id);
|
|
871
|
+
}
|
|
872
|
+
// add listeners first
|
|
873
|
+
let jobId;
|
|
874
|
+
const promise = new Promise((resolve, reject) => {
|
|
875
|
+
const unsubscribe = this.queueManager.on("jobCompleted", (job, result) => {
|
|
876
|
+
if (job.jobId === jobId) {
|
|
877
|
+
unsubscribe();
|
|
878
|
+
unsubscribeError();
|
|
879
|
+
resolve(result);
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
const unsubscribeError = this.queueManager.on("jobFailed", (job, error) => {
|
|
883
|
+
if (job.jobId === jobId) {
|
|
884
|
+
unsubscribe();
|
|
885
|
+
unsubscribeError();
|
|
886
|
+
reject(error);
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
});
|
|
890
|
+
// now queue the job
|
|
891
|
+
try {
|
|
892
|
+
jobId = await this.queueManager.addJob({
|
|
893
|
+
documentId: id,
|
|
894
|
+
documentType,
|
|
895
|
+
initialState: document,
|
|
896
|
+
options,
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
catch (error) {
|
|
900
|
+
this.logger.error("Error adding job", error);
|
|
901
|
+
throw error;
|
|
777
902
|
}
|
|
903
|
+
return promise;
|
|
778
904
|
}
|
|
779
|
-
queueOperation(
|
|
780
|
-
|
|
905
|
+
queueOperation(driveIdOrDocumentId, documentIdOrOperation, operationOrOptions, maybeOptions) {
|
|
906
|
+
let documentId;
|
|
907
|
+
let operation;
|
|
908
|
+
let options;
|
|
909
|
+
if (typeof documentIdOrOperation === "string") {
|
|
910
|
+
// Deprecated overload: (driveId, documentId, operation, options)
|
|
911
|
+
documentId = documentIdOrOperation;
|
|
912
|
+
operation = operationOrOptions;
|
|
913
|
+
options = maybeOptions;
|
|
914
|
+
}
|
|
915
|
+
else {
|
|
916
|
+
// Standard overload: (documentId, operation, options)
|
|
917
|
+
documentId = driveIdOrDocumentId;
|
|
918
|
+
operation = documentIdOrOperation;
|
|
919
|
+
options = operationOrOptions;
|
|
920
|
+
}
|
|
921
|
+
return this._queueOperations(documentId, [operation], options);
|
|
781
922
|
}
|
|
782
|
-
async resultIfExistingOperations(
|
|
923
|
+
async resultIfExistingOperations(id, operations) {
|
|
783
924
|
try {
|
|
784
|
-
const document = await this.getDocument(
|
|
925
|
+
const document = await this.getDocument(id);
|
|
785
926
|
const newOperation = operations.find((op) => !op.id ||
|
|
786
927
|
!document.operations[op.scope].find((existingOp) => existingOp.id === op.id &&
|
|
787
928
|
existingOp.index === op.index &&
|
|
@@ -806,9 +947,27 @@ export class BaseDocumentDriveServer {
|
|
|
806
947
|
return undefined;
|
|
807
948
|
}
|
|
808
949
|
}
|
|
809
|
-
|
|
950
|
+
queueOperations(driveIdOrDocumentId, documentIdOrOperations, operationsOrOptions, maybeOptions) {
|
|
951
|
+
let documentId;
|
|
952
|
+
let operations;
|
|
953
|
+
let options;
|
|
954
|
+
if (typeof documentIdOrOperations === "string") {
|
|
955
|
+
// Deprecated overload: (driveId, documentId, operations, options)
|
|
956
|
+
documentId = documentIdOrOperations;
|
|
957
|
+
operations = operationsOrOptions;
|
|
958
|
+
options = maybeOptions;
|
|
959
|
+
}
|
|
960
|
+
else {
|
|
961
|
+
// Standard overload: (documentId, operations, options)
|
|
962
|
+
documentId = driveIdOrDocumentId;
|
|
963
|
+
operations = documentIdOrOperations;
|
|
964
|
+
options = operationsOrOptions;
|
|
965
|
+
}
|
|
966
|
+
return this._queueOperations(documentId, operations, options);
|
|
967
|
+
}
|
|
968
|
+
async _queueOperations(documentId, operations, options) {
|
|
810
969
|
// if operations are already stored then returns cached document
|
|
811
|
-
const result = await this.resultIfExistingOperations(
|
|
970
|
+
const result = await this.resultIfExistingOperations(documentId, operations);
|
|
812
971
|
if (result) {
|
|
813
972
|
return result;
|
|
814
973
|
}
|
|
@@ -833,8 +992,7 @@ export class BaseDocumentDriveServer {
|
|
|
833
992
|
// now queue the job
|
|
834
993
|
try {
|
|
835
994
|
jobId = await this.queueManager.addJob({
|
|
836
|
-
|
|
837
|
-
documentId: documentId,
|
|
995
|
+
documentId,
|
|
838
996
|
operations,
|
|
839
997
|
options,
|
|
840
998
|
});
|
|
@@ -845,14 +1003,46 @@ export class BaseDocumentDriveServer {
|
|
|
845
1003
|
}
|
|
846
1004
|
return promise;
|
|
847
1005
|
}
|
|
848
|
-
|
|
849
|
-
|
|
1006
|
+
queueAction(driveIdOrDocumentId, documentIdOrAction, actionOrOptions, maybeOptions) {
|
|
1007
|
+
let documentId;
|
|
1008
|
+
let action;
|
|
1009
|
+
let options;
|
|
1010
|
+
if (typeof documentIdOrAction === "string") {
|
|
1011
|
+
// Deprecated overload: (driveId, documentId, action, options)
|
|
1012
|
+
documentId = documentIdOrAction;
|
|
1013
|
+
action = actionOrOptions;
|
|
1014
|
+
options = maybeOptions;
|
|
1015
|
+
}
|
|
1016
|
+
else {
|
|
1017
|
+
// Standard overload: (documentId, action, options)
|
|
1018
|
+
documentId = driveIdOrDocumentId;
|
|
1019
|
+
action = documentIdOrAction;
|
|
1020
|
+
options = actionOrOptions;
|
|
1021
|
+
}
|
|
1022
|
+
return this._queueActions(documentId, [action], options);
|
|
1023
|
+
}
|
|
1024
|
+
queueActions(driveIdOrDocumentId, documentIdOrActions, actionsOrOptions, maybeOptions) {
|
|
1025
|
+
let documentId;
|
|
1026
|
+
let actions;
|
|
1027
|
+
let options;
|
|
1028
|
+
if (typeof documentIdOrActions === "string") {
|
|
1029
|
+
// Deprecated overload: (driveId, documentId, actions, options)
|
|
1030
|
+
documentId = documentIdOrActions;
|
|
1031
|
+
actions = actionsOrOptions;
|
|
1032
|
+
options = maybeOptions;
|
|
1033
|
+
}
|
|
1034
|
+
else {
|
|
1035
|
+
// Standard overload: (documentId, actions, options)
|
|
1036
|
+
documentId = driveIdOrDocumentId;
|
|
1037
|
+
actions = documentIdOrActions;
|
|
1038
|
+
options = actionsOrOptions;
|
|
1039
|
+
}
|
|
1040
|
+
return this._queueActions(documentId, actions, options);
|
|
850
1041
|
}
|
|
851
|
-
async
|
|
1042
|
+
async _queueActions(documentId, actions, options) {
|
|
852
1043
|
try {
|
|
853
1044
|
const jobId = await this.queueManager.addJob({
|
|
854
|
-
|
|
855
|
-
documentId: documentId,
|
|
1045
|
+
documentId,
|
|
856
1046
|
actions,
|
|
857
1047
|
options,
|
|
858
1048
|
});
|
|
@@ -878,13 +1068,19 @@ export class BaseDocumentDriveServer {
|
|
|
878
1068
|
throw error;
|
|
879
1069
|
}
|
|
880
1070
|
}
|
|
1071
|
+
/**
|
|
1072
|
+
* @deprecated Use the {@link queueAction} method instead.
|
|
1073
|
+
*/
|
|
881
1074
|
async queueDriveAction(driveId, action, options) {
|
|
882
1075
|
return this.queueDriveActions(driveId, [action], options);
|
|
883
1076
|
}
|
|
1077
|
+
/**
|
|
1078
|
+
* @deprecated Use the {@link queueActions} method instead.
|
|
1079
|
+
*/
|
|
884
1080
|
async queueDriveActions(driveId, actions, options) {
|
|
885
1081
|
try {
|
|
886
1082
|
const jobId = await this.queueManager.addJob({
|
|
887
|
-
|
|
1083
|
+
documentId: driveId,
|
|
888
1084
|
actions,
|
|
889
1085
|
options,
|
|
890
1086
|
});
|
|
@@ -910,12 +1106,27 @@ export class BaseDocumentDriveServer {
|
|
|
910
1106
|
throw error;
|
|
911
1107
|
}
|
|
912
1108
|
}
|
|
913
|
-
|
|
914
|
-
|
|
1109
|
+
addOperations(driveIdOrDocumentId, documentIdOrOperations, operationsOrOptions, maybeOptions) {
|
|
1110
|
+
let documentId;
|
|
1111
|
+
let operations;
|
|
1112
|
+
let options;
|
|
1113
|
+
if (typeof documentIdOrOperations === "string") {
|
|
1114
|
+
// Deprecated overload: (driveId, documentId, operations, options)
|
|
1115
|
+
documentId = documentIdOrOperations;
|
|
1116
|
+
operations = operationsOrOptions;
|
|
1117
|
+
options = maybeOptions;
|
|
1118
|
+
}
|
|
1119
|
+
else {
|
|
1120
|
+
// Standard overload: (documentId, operations, options)
|
|
1121
|
+
documentId = driveIdOrDocumentId;
|
|
1122
|
+
operations = documentIdOrOperations;
|
|
1123
|
+
options = operationsOrOptions;
|
|
1124
|
+
}
|
|
1125
|
+
return this._queueOperations(documentId, operations, options);
|
|
915
1126
|
}
|
|
916
|
-
async processOperations(
|
|
1127
|
+
async processOperations(documentId, operations, options) {
|
|
917
1128
|
// if operations are already stored then returns the result
|
|
918
|
-
const result = await this.resultIfExistingOperations(
|
|
1129
|
+
const result = await this.resultIfExistingOperations(documentId, operations);
|
|
919
1130
|
if (result) {
|
|
920
1131
|
return result;
|
|
921
1132
|
}
|
|
@@ -924,8 +1135,8 @@ export class BaseDocumentDriveServer {
|
|
|
924
1135
|
const signals = [];
|
|
925
1136
|
let error;
|
|
926
1137
|
try {
|
|
927
|
-
await this._addOperations(
|
|
928
|
-
const result = await this._processOperations(
|
|
1138
|
+
await this._addOperations(documentId, async (documentStorage) => {
|
|
1139
|
+
const result = await this._processOperations(documentId, documentStorage, operations);
|
|
929
1140
|
if (!result.document) {
|
|
930
1141
|
this.logger.error("Invalid document");
|
|
931
1142
|
throw result.error ?? new Error("Invalid document");
|
|
@@ -939,17 +1150,28 @@ export class BaseDocumentDriveServer {
|
|
|
939
1150
|
document: result.document,
|
|
940
1151
|
};
|
|
941
1152
|
});
|
|
1153
|
+
const syncUnits = new Array();
|
|
942
1154
|
if (document) {
|
|
943
1155
|
this.cache.setDocument(documentId, document).catch(this.logger.error);
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1156
|
+
// creates array of unique sync units from the applied operations
|
|
1157
|
+
for (const operation of operationsApplied) {
|
|
1158
|
+
const syncUnit = {
|
|
1159
|
+
documentId,
|
|
1160
|
+
documentType: document.header.documentType,
|
|
1161
|
+
scope: operation.scope,
|
|
1162
|
+
branch: "main", // TODO: handle branches
|
|
1163
|
+
revision: operation.index + 1,
|
|
1164
|
+
lastUpdated: operation.timestamp,
|
|
1165
|
+
};
|
|
1166
|
+
// checks if this sync unit was already added
|
|
1167
|
+
const exists = syncUnits.some((unit) => unit.documentId === syncUnit.documentId &&
|
|
1168
|
+
unit.scope === syncUnit.scope &&
|
|
1169
|
+
unit.branch === syncUnit.branch);
|
|
1170
|
+
if (!exists) {
|
|
1171
|
+
syncUnits.push(syncUnit);
|
|
1172
|
+
}
|
|
949
1173
|
}
|
|
950
|
-
|
|
951
|
-
}, { scopes: [], branches: ["main"] });
|
|
952
|
-
const syncUnits = await this.getSynchronizationUnits(driveId, [documentId], scopes, branches);
|
|
1174
|
+
}
|
|
953
1175
|
// checks if any of the provided operations where reshufled
|
|
954
1176
|
const newOp = operationsApplied.find((appliedOp) => !operations.find((o) => o.id === appliedOp.id &&
|
|
955
1177
|
o.index === appliedOp.index &&
|
|
@@ -963,47 +1185,51 @@ export class BaseDocumentDriveServer {
|
|
|
963
1185
|
: (options?.source ?? { type: "local" });
|
|
964
1186
|
// update listener cache
|
|
965
1187
|
const operationSource = this.getOperationSource(source);
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
this.synchronizationManager.updateSyncStatus(syncUnit.syncId, {
|
|
1188
|
+
// TODO Decouple the operation processing from syncing it to the listeners?
|
|
1189
|
+
// Listener manager should be the one keeping the sync status since it depends on the listeners
|
|
1190
|
+
if (syncUnits.length) {
|
|
1191
|
+
this.listenerManager
|
|
1192
|
+
.updateSynchronizationRevisions(syncUnits, source, () => {
|
|
1193
|
+
this.synchronizationManager.updateSyncStatus(documentId, {
|
|
973
1194
|
[operationSource]: "SYNCING",
|
|
974
1195
|
});
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
this.synchronizationManager.updateSyncStatus(
|
|
1196
|
+
for (const syncUnit of syncUnits) {
|
|
1197
|
+
this.synchronizationManager.updateSyncStatus(syncUnit, {
|
|
1198
|
+
[operationSource]: "SYNCING",
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
}, this.handleListenerError.bind(this), options?.forceSync ?? source.type === "local")
|
|
1202
|
+
.then((updates) => {
|
|
1203
|
+
if (updates.length) {
|
|
1204
|
+
this.synchronizationManager.updateSyncStatus(documentId, {
|
|
1205
|
+
[operationSource]: "SUCCESS",
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
for (const syncUnit of syncUnits) {
|
|
1209
|
+
this.synchronizationManager.updateSyncStatus(syncUnit, {
|
|
1210
|
+
[operationSource]: "SUCCESS",
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
})
|
|
1214
|
+
.catch((error) => {
|
|
1215
|
+
this.logger.error("Non handled error updating sync revision", error);
|
|
1216
|
+
this.synchronizationManager.updateSyncStatus(documentId, {
|
|
996
1217
|
[operationSource]: "ERROR",
|
|
997
1218
|
}, error);
|
|
998
|
-
|
|
999
|
-
|
|
1219
|
+
for (const syncUnit of syncUnits) {
|
|
1220
|
+
this.synchronizationManager.updateSyncStatus(syncUnit, {
|
|
1221
|
+
[operationSource]: "ERROR",
|
|
1222
|
+
}, error);
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1000
1226
|
// after applying all the valid operations,throws
|
|
1001
1227
|
// an error if there was an invalid operation
|
|
1002
1228
|
if (error) {
|
|
1003
1229
|
throw error;
|
|
1004
1230
|
}
|
|
1005
|
-
this.eventEmitter.emit("documentOperationsAdded",
|
|
1006
|
-
this.eventEmitter.emit("operationsAdded",
|
|
1231
|
+
this.eventEmitter.emit("documentOperationsAdded", documentId, operations);
|
|
1232
|
+
this.eventEmitter.emit("operationsAdded", documentId, operations);
|
|
1007
1233
|
return {
|
|
1008
1234
|
status: "SUCCESS",
|
|
1009
1235
|
document,
|
|
@@ -1024,6 +1250,9 @@ export class BaseDocumentDriveServer {
|
|
|
1024
1250
|
};
|
|
1025
1251
|
}
|
|
1026
1252
|
}
|
|
1253
|
+
/**
|
|
1254
|
+
* @deprecated Use the {@link addOperation} method instead.
|
|
1255
|
+
*/
|
|
1027
1256
|
addDriveOperation(driveId, operation, options) {
|
|
1028
1257
|
return this.addDriveOperations(driveId, [operation], options);
|
|
1029
1258
|
}
|
|
@@ -1041,6 +1270,9 @@ export class BaseDocumentDriveServer {
|
|
|
1041
1270
|
return this.legacyStorage.addDriveOperationsWithTransaction(driveId, callback);
|
|
1042
1271
|
}
|
|
1043
1272
|
}
|
|
1273
|
+
/**
|
|
1274
|
+
* @deprecated Use the {@link queueOperation} method instead.
|
|
1275
|
+
*/
|
|
1044
1276
|
queueDriveOperation(driveId, operation, options) {
|
|
1045
1277
|
return this.queueDriveOperations(driveId, [operation], options);
|
|
1046
1278
|
}
|
|
@@ -1069,6 +1301,9 @@ export class BaseDocumentDriveServer {
|
|
|
1069
1301
|
return undefined;
|
|
1070
1302
|
}
|
|
1071
1303
|
}
|
|
1304
|
+
/**
|
|
1305
|
+
* @deprecated Use the {@link queueOperations} method instead.
|
|
1306
|
+
*/
|
|
1072
1307
|
async queueDriveOperations(driveId, operations, options) {
|
|
1073
1308
|
// if operations are already stored then returns cached document
|
|
1074
1309
|
const result = await this.resultIfExistingDriveOperations(driveId, operations);
|
|
@@ -1077,7 +1312,7 @@ export class BaseDocumentDriveServer {
|
|
|
1077
1312
|
}
|
|
1078
1313
|
try {
|
|
1079
1314
|
const jobId = await this.queueManager.addJob({
|
|
1080
|
-
|
|
1315
|
+
documentId: driveId,
|
|
1081
1316
|
operations,
|
|
1082
1317
|
options,
|
|
1083
1318
|
});
|
|
@@ -1103,6 +1338,9 @@ export class BaseDocumentDriveServer {
|
|
|
1103
1338
|
throw error;
|
|
1104
1339
|
}
|
|
1105
1340
|
}
|
|
1341
|
+
/**
|
|
1342
|
+
* @deprecated Use the {@link addOperations} method instead.
|
|
1343
|
+
*/
|
|
1106
1344
|
async addDriveOperations(driveId, operations, options) {
|
|
1107
1345
|
return this.queueDriveOperations(driveId, operations, options);
|
|
1108
1346
|
}
|
|
@@ -1118,7 +1356,7 @@ export class BaseDocumentDriveServer {
|
|
|
1118
1356
|
}
|
|
1119
1357
|
try {
|
|
1120
1358
|
await this._addDriveOperations(driveId, async (documentStorage) => {
|
|
1121
|
-
const result = await this._processOperations(driveId,
|
|
1359
|
+
const result = await this._processOperations(driveId, documentStorage, operations.slice());
|
|
1122
1360
|
document = result.document;
|
|
1123
1361
|
operationsApplied.push(...result.operationsApplied);
|
|
1124
1362
|
signals.push(...result.signals);
|
|
@@ -1131,6 +1369,7 @@ export class BaseDocumentDriveServer {
|
|
|
1131
1369
|
if (!document || !isDocumentDrive(document)) {
|
|
1132
1370
|
throw error ?? new Error("Invalid Document Drive document");
|
|
1133
1371
|
}
|
|
1372
|
+
this.cache.setDocument(driveId, document).catch(this.logger.error);
|
|
1134
1373
|
this.cache.setDrive(driveId, document).catch(this.logger.error);
|
|
1135
1374
|
// update listener cache
|
|
1136
1375
|
const lastOperation = operationsApplied
|
|
@@ -1151,13 +1390,12 @@ export class BaseDocumentDriveServer {
|
|
|
1151
1390
|
: (options?.source ?? { type: "local" });
|
|
1152
1391
|
const operationSource = this.getOperationSource(source);
|
|
1153
1392
|
this.listenerManager
|
|
1154
|
-
.updateSynchronizationRevisions(
|
|
1393
|
+
.updateSynchronizationRevisions([
|
|
1155
1394
|
{
|
|
1156
|
-
|
|
1157
|
-
|
|
1395
|
+
documentId: driveId,
|
|
1396
|
+
documentType: document.header.documentType,
|
|
1158
1397
|
scope: "global",
|
|
1159
1398
|
branch: "main",
|
|
1160
|
-
documentType: "powerhouse/document-drive",
|
|
1161
1399
|
lastUpdated: lastOperation.timestamp,
|
|
1162
1400
|
revision: lastOperation.index,
|
|
1163
1401
|
},
|
|
@@ -1181,10 +1419,10 @@ export class BaseDocumentDriveServer {
|
|
|
1181
1419
|
});
|
|
1182
1420
|
}
|
|
1183
1421
|
if (this.shouldSyncRemoteDrive(document)) {
|
|
1184
|
-
this.startSyncRemoteDrive(driveId);
|
|
1422
|
+
this.startSyncRemoteDrive(driveId).catch(this.logger.error);
|
|
1185
1423
|
}
|
|
1186
1424
|
else {
|
|
1187
|
-
this.stopSyncRemoteDrive(driveId);
|
|
1425
|
+
this.stopSyncRemoteDrive(driveId).catch(this.logger.error);
|
|
1188
1426
|
}
|
|
1189
1427
|
// after applying all the valid operations,throws
|
|
1190
1428
|
// an error if there was an invalid operation
|
|
@@ -1192,7 +1430,7 @@ export class BaseDocumentDriveServer {
|
|
|
1192
1430
|
throw error;
|
|
1193
1431
|
}
|
|
1194
1432
|
this.eventEmitter.emit("driveOperationsAdded", driveId, operations);
|
|
1195
|
-
this.eventEmitter.emit("operationsAdded", driveId,
|
|
1433
|
+
this.eventEmitter.emit("operationsAdded", driveId, operations);
|
|
1196
1434
|
return {
|
|
1197
1435
|
status: "SUCCESS",
|
|
1198
1436
|
document,
|
|
@@ -1226,20 +1464,81 @@ export class BaseDocumentDriveServer {
|
|
|
1226
1464
|
}
|
|
1227
1465
|
return operations;
|
|
1228
1466
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1467
|
+
addAction(driveIdOrDocumentId, documentIdOrAction, actionOrOptions, maybeOptions) {
|
|
1468
|
+
let documentId;
|
|
1469
|
+
let action;
|
|
1470
|
+
let options;
|
|
1471
|
+
if (typeof documentIdOrAction === "string") {
|
|
1472
|
+
// Deprecated overload: (driveId, documentId, action, options)
|
|
1473
|
+
documentId = documentIdOrAction;
|
|
1474
|
+
action = actionOrOptions;
|
|
1475
|
+
options = maybeOptions;
|
|
1476
|
+
}
|
|
1477
|
+
else {
|
|
1478
|
+
// Standard overload: (documentId, action, options)
|
|
1479
|
+
documentId = driveIdOrDocumentId;
|
|
1480
|
+
action = documentIdOrAction;
|
|
1481
|
+
options = actionOrOptions;
|
|
1482
|
+
}
|
|
1483
|
+
return this._addAction(documentId, action, options);
|
|
1484
|
+
}
|
|
1485
|
+
async _addAction(documentId, action, options) {
|
|
1486
|
+
return this.addActions(documentId, [action], options);
|
|
1487
|
+
}
|
|
1488
|
+
addActions(driveIdOrDocumentId, documentIdOrActions, actionsOrOptions, maybeOptions) {
|
|
1489
|
+
let documentId;
|
|
1490
|
+
let actions;
|
|
1491
|
+
let options;
|
|
1492
|
+
if (typeof documentIdOrActions === "string") {
|
|
1493
|
+
// Deprecated overload: (driveId, documentId, actions, options)
|
|
1494
|
+
documentId = documentIdOrActions;
|
|
1495
|
+
actions = actionsOrOptions;
|
|
1496
|
+
options = maybeOptions;
|
|
1497
|
+
}
|
|
1498
|
+
else {
|
|
1499
|
+
// Standard overload: (documentId, actions, options)
|
|
1500
|
+
documentId = driveIdOrDocumentId;
|
|
1501
|
+
actions = documentIdOrActions;
|
|
1502
|
+
options = actionsOrOptions;
|
|
1503
|
+
}
|
|
1504
|
+
return this._queueActions(documentId, actions, options);
|
|
1234
1505
|
}
|
|
1235
|
-
async processActions(
|
|
1236
|
-
const document = await this.getDocument(
|
|
1506
|
+
async processActions(documentId, actions, options) {
|
|
1507
|
+
const document = await this.getDocument(documentId);
|
|
1237
1508
|
const operations = this._buildOperations(document, actions);
|
|
1238
|
-
return this.processOperations(
|
|
1509
|
+
return this.processOperations(documentId, operations, options);
|
|
1239
1510
|
}
|
|
1511
|
+
/**
|
|
1512
|
+
* @deprecated Use the {@link addAction} method instead.
|
|
1513
|
+
*/
|
|
1240
1514
|
async addDriveAction(driveId, action, options) {
|
|
1515
|
+
if ("synchronizationUnits" in action.input) {
|
|
1516
|
+
return this._legacyAddFileAction(driveId, action, options);
|
|
1517
|
+
}
|
|
1241
1518
|
return this.addDriveActions(driveId, [action], options);
|
|
1242
1519
|
}
|
|
1520
|
+
async _legacyAddFileAction(driveId, action, options) {
|
|
1521
|
+
// create document before adding it to the drive
|
|
1522
|
+
const document = this.getDocumentModelModule(action.input.documentType).utils.createDocument({ ...action.input.document });
|
|
1523
|
+
document.header.id = action.input.id;
|
|
1524
|
+
document.header.name = action.input.name;
|
|
1525
|
+
document.header.documentType = action.input.documentType;
|
|
1526
|
+
await this.queueDocument({ document }, { source: options?.source || { type: "local" } });
|
|
1527
|
+
// create updated version of the ADD_FILE action
|
|
1528
|
+
const newAction = {
|
|
1529
|
+
...action,
|
|
1530
|
+
input: {
|
|
1531
|
+
id: action.input.id,
|
|
1532
|
+
documentType: document.header.documentType,
|
|
1533
|
+
name: action.input.name,
|
|
1534
|
+
parentFolder: action.input.parentFolder,
|
|
1535
|
+
},
|
|
1536
|
+
};
|
|
1537
|
+
return (await this.addAction(driveId, newAction, options));
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* @deprecated Use the {@link addActions} method instead.
|
|
1541
|
+
*/
|
|
1243
1542
|
async addDriveActions(driveId, actions, options) {
|
|
1244
1543
|
return this.queueDriveActions(driveId, actions, options);
|
|
1245
1544
|
}
|
|
@@ -1260,8 +1559,8 @@ export class BaseDocumentDriveServer {
|
|
|
1260
1559
|
}
|
|
1261
1560
|
await this.addDriveAction(driveId, setSharingType({ type: "LOCAL" }));
|
|
1262
1561
|
}
|
|
1263
|
-
getSyncStatus(
|
|
1264
|
-
return this.synchronizationManager.getSyncStatus(
|
|
1562
|
+
getSyncStatus(documentId, scope, branch) {
|
|
1563
|
+
return this.synchronizationManager.getSyncStatus(documentId, scope, branch);
|
|
1265
1564
|
}
|
|
1266
1565
|
on(event, cb) {
|
|
1267
1566
|
return this.eventEmitter.on(event, cb);
|
|
@@ -1269,9 +1568,6 @@ export class BaseDocumentDriveServer {
|
|
|
1269
1568
|
emit(event, ...args) {
|
|
1270
1569
|
return this.eventEmitter.emit(event, ...args);
|
|
1271
1570
|
}
|
|
1272
|
-
getSynchronizationUnit(driveId, syncId) {
|
|
1273
|
-
return this.synchronizationManager.getSynchronizationUnit(driveId, syncId);
|
|
1274
|
-
}
|
|
1275
1571
|
// Add delegated methods to properly implement ISynchronizationManager
|
|
1276
1572
|
updateSyncStatus(syncUnitId, status, error) {
|
|
1277
1573
|
this.synchronizationManager.updateSyncStatus(syncUnitId, status, error);
|
|
@@ -1284,15 +1580,24 @@ export class BaseDocumentDriveServer {
|
|
|
1284
1580
|
}
|
|
1285
1581
|
// Add back the saveStrand method that was accidentally removed
|
|
1286
1582
|
async saveStrand(strand, source) {
|
|
1583
|
+
const isNewDocument = !(await this.documentStorage.exists(strand.documentId));
|
|
1584
|
+
let result = undefined;
|
|
1585
|
+
if (isNewDocument) {
|
|
1586
|
+
result = await this.queueDocument({
|
|
1587
|
+
id: strand.documentId,
|
|
1588
|
+
documentType: strand.documentType,
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1287
1591
|
const operations = strand.operations.map((op) => ({
|
|
1288
1592
|
...op,
|
|
1289
1593
|
scope: strand.scope,
|
|
1290
1594
|
branch: strand.branch,
|
|
1291
1595
|
}));
|
|
1292
|
-
|
|
1293
|
-
|
|
1596
|
+
// if document already existed or queueDocument
|
|
1597
|
+
// was successful, queues the operations
|
|
1598
|
+
if ((!isNewDocument || result?.status === "SUCCESS") && operations.length) {
|
|
1294
1599
|
try {
|
|
1295
|
-
result = await this.queueOperations(strand.
|
|
1600
|
+
result = await this.queueOperations(strand.documentId, operations, {
|
|
1296
1601
|
source,
|
|
1297
1602
|
});
|
|
1298
1603
|
}
|
|
@@ -1301,25 +1606,22 @@ export class BaseDocumentDriveServer {
|
|
|
1301
1606
|
throw error;
|
|
1302
1607
|
}
|
|
1303
1608
|
}
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
throw error;
|
|
1313
|
-
}
|
|
1609
|
+
if (!result) {
|
|
1610
|
+
this.logger.debug(`Document ${strand.documentId} already exists`);
|
|
1611
|
+
return {
|
|
1612
|
+
status: "SUCCESS",
|
|
1613
|
+
document: await this.getDocument(strand.documentId),
|
|
1614
|
+
operations: [],
|
|
1615
|
+
signals: [],
|
|
1616
|
+
};
|
|
1314
1617
|
}
|
|
1315
1618
|
if (result.status === "ERROR") {
|
|
1316
|
-
const syncUnits = strand.documentId !== ""
|
|
1317
|
-
? (await this.getSynchronizationUnitsIds(strand.driveId, [strand.documentId], [strand.scope], [strand.branch])).map((s) => s.syncId)
|
|
1318
|
-
: [strand.driveId];
|
|
1319
1619
|
const operationSource = this.getOperationSource(source);
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1620
|
+
this.synchronizationManager.updateSyncStatus({
|
|
1621
|
+
documentId: strand.documentId || strand.driveId,
|
|
1622
|
+
scope: strand.scope,
|
|
1623
|
+
branch: strand.branch,
|
|
1624
|
+
}, { [operationSource]: result.status }, result.error);
|
|
1323
1625
|
}
|
|
1324
1626
|
this.eventEmitter.emit("strandUpdate", strand);
|
|
1325
1627
|
return result;
|