document-drive 4.0.1 → 4.1.0-dev.2

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