document-drive 1.19.1 → 1.20.1

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 (252) hide show
  1. package/README.md +4 -0
  2. package/dist/index.d.ts +28 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +18 -0
  5. package/dist/src/cache/memory.d.ts +10 -0
  6. package/dist/src/cache/memory.d.ts.map +1 -0
  7. package/dist/src/cache/memory.js +26 -0
  8. package/dist/src/cache/redis.d.ts +14 -0
  9. package/dist/src/cache/redis.d.ts.map +1 -0
  10. package/dist/src/cache/redis.js +40 -0
  11. package/dist/src/cache/types.d.ts +7 -0
  12. package/dist/src/cache/types.d.ts.map +1 -0
  13. package/dist/src/cache/types.js +1 -0
  14. package/dist/src/drive-document-model/constants.d.ts +2 -0
  15. package/dist/src/drive-document-model/constants.d.ts.map +1 -0
  16. package/dist/src/drive-document-model/constants.js +1 -0
  17. package/dist/src/drive-document-model/gen/actions.d.ts +7 -0
  18. package/dist/src/drive-document-model/gen/actions.d.ts.map +1 -0
  19. package/dist/src/drive-document-model/gen/actions.js +2 -0
  20. package/dist/src/drive-document-model/gen/constants.d.ts +7 -0
  21. package/dist/src/drive-document-model/gen/constants.d.ts.map +1 -0
  22. package/dist/src/drive-document-model/gen/constants.js +16 -0
  23. package/dist/src/drive-document-model/gen/creators.d.ts +3 -0
  24. package/dist/src/drive-document-model/gen/creators.d.ts.map +1 -0
  25. package/dist/src/drive-document-model/gen/creators.js +2 -0
  26. package/dist/src/drive-document-model/gen/document-model.d.ts +3 -0
  27. package/dist/src/drive-document-model/gen/document-model.d.ts.map +1 -0
  28. package/dist/src/drive-document-model/gen/document-model.js +210 -0
  29. package/dist/src/drive-document-model/gen/drive/actions.d.ts +12 -0
  30. package/dist/src/drive-document-model/gen/drive/actions.d.ts.map +1 -0
  31. package/dist/src/drive-document-model/gen/drive/actions.js +1 -0
  32. package/dist/src/drive-document-model/gen/drive/creators.d.ts +11 -0
  33. package/dist/src/drive-document-model/gen/drive/creators.d.ts.map +1 -0
  34. package/dist/src/drive-document-model/gen/drive/creators.js +10 -0
  35. package/dist/src/drive-document-model/gen/drive/error.d.ts +2 -0
  36. package/dist/src/drive-document-model/gen/drive/error.d.ts.map +1 -0
  37. package/dist/src/drive-document-model/gen/drive/error.js +1 -0
  38. package/dist/src/drive-document-model/gen/drive/object.d.ts +14 -0
  39. package/dist/src/drive-document-model/gen/drive/object.d.ts.map +1 -0
  40. package/dist/src/drive-document-model/gen/drive/object.js +28 -0
  41. package/dist/src/drive-document-model/gen/drive/operations.d.ts +14 -0
  42. package/dist/src/drive-document-model/gen/drive/operations.d.ts.map +1 -0
  43. package/dist/src/drive-document-model/gen/drive/operations.js +1 -0
  44. package/dist/src/drive-document-model/gen/node/actions.d.ts +11 -0
  45. package/dist/src/drive-document-model/gen/node/actions.d.ts.map +1 -0
  46. package/dist/src/drive-document-model/gen/node/actions.js +1 -0
  47. package/dist/src/drive-document-model/gen/node/creators.d.ts +10 -0
  48. package/dist/src/drive-document-model/gen/node/creators.d.ts.map +1 -0
  49. package/dist/src/drive-document-model/gen/node/creators.js +9 -0
  50. package/dist/src/drive-document-model/gen/node/error.d.ts +2 -0
  51. package/dist/src/drive-document-model/gen/node/error.d.ts.map +1 -0
  52. package/dist/src/drive-document-model/gen/node/error.js +1 -0
  53. package/dist/src/drive-document-model/gen/node/object.d.ts +13 -0
  54. package/dist/src/drive-document-model/gen/node/object.d.ts.map +1 -0
  55. package/dist/src/drive-document-model/gen/node/object.js +25 -0
  56. package/dist/src/drive-document-model/gen/node/operations.d.ts +13 -0
  57. package/dist/src/drive-document-model/gen/node/operations.d.ts.map +1 -0
  58. package/dist/src/drive-document-model/gen/node/operations.js +1 -0
  59. package/dist/src/drive-document-model/gen/object.d.ts +21 -0
  60. package/dist/src/drive-document-model/gen/object.d.ts.map +1 -0
  61. package/dist/src/drive-document-model/gen/object.js +28 -0
  62. package/dist/src/drive-document-model/gen/reducer.d.ts +4 -0
  63. package/dist/src/drive-document-model/gen/reducer.d.ts.map +1 -0
  64. package/dist/src/drive-document-model/gen/reducer.js +74 -0
  65. package/dist/src/drive-document-model/gen/schema/types.d.ts +176 -0
  66. package/dist/src/drive-document-model/gen/schema/types.d.ts.map +1 -0
  67. package/dist/src/drive-document-model/gen/schema/types.js +1 -0
  68. package/dist/src/drive-document-model/gen/schema/zod.d.ts +87 -0
  69. package/dist/src/drive-document-model/gen/schema/zod.d.ts.map +1 -0
  70. package/dist/src/drive-document-model/gen/schema/zod.js +203 -0
  71. package/dist/src/drive-document-model/gen/types.d.ts +9 -0
  72. package/dist/src/drive-document-model/gen/types.d.ts.map +1 -0
  73. package/dist/src/drive-document-model/gen/types.js +1 -0
  74. package/dist/src/drive-document-model/gen/utils.d.ts +10 -0
  75. package/dist/src/drive-document-model/gen/utils.d.ts.map +1 -0
  76. package/dist/src/drive-document-model/gen/utils.js +27 -0
  77. package/dist/src/drive-document-model/index.d.ts +2 -0
  78. package/dist/src/drive-document-model/index.d.ts.map +1 -0
  79. package/dist/src/drive-document-model/index.js +1 -0
  80. package/dist/src/drive-document-model/module.d.ts +3 -0
  81. package/dist/src/drive-document-model/module.d.ts.map +1 -0
  82. package/dist/src/drive-document-model/module.js +12 -0
  83. package/dist/src/drive-document-model/src/reducers/drive.d.ts +8 -0
  84. package/dist/src/drive-document-model/src/reducers/drive.d.ts.map +1 -0
  85. package/dist/src/drive-document-model/src/reducers/drive.js +37 -0
  86. package/dist/src/drive-document-model/src/reducers/node.d.ts +8 -0
  87. package/dist/src/drive-document-model/src/reducers/node.d.ts.map +1 -0
  88. package/dist/src/drive-document-model/src/reducers/node.js +185 -0
  89. package/dist/src/drive-document-model/src/utils.d.ts +34 -0
  90. package/dist/src/drive-document-model/src/utils.d.ts.map +1 -0
  91. package/dist/src/drive-document-model/src/utils.js +146 -0
  92. package/dist/src/queue/base.d.ts +43 -0
  93. package/dist/src/queue/base.d.ts.map +1 -0
  94. package/dist/src/queue/base.js +241 -0
  95. package/dist/src/queue/redis.d.ts +28 -0
  96. package/dist/src/queue/redis.d.ts.map +1 -0
  97. package/dist/src/queue/redis.js +110 -0
  98. package/dist/src/queue/types.d.ts +55 -0
  99. package/dist/src/queue/types.d.ts.map +1 -0
  100. package/dist/src/queue/types.js +6 -0
  101. package/dist/src/read-mode/errors.d.ts +12 -0
  102. package/dist/src/read-mode/errors.d.ts.map +1 -0
  103. package/dist/src/read-mode/errors.js +17 -0
  104. package/dist/src/read-mode/server.d.ts +4 -0
  105. package/dist/src/read-mode/server.d.ts.map +1 -0
  106. package/dist/src/read-mode/server.js +78 -0
  107. package/dist/src/read-mode/service.d.ts +18 -0
  108. package/dist/src/read-mode/service.d.ts.map +1 -0
  109. package/dist/src/read-mode/service.js +112 -0
  110. package/dist/src/read-mode/types.d.ts +35 -0
  111. package/dist/src/read-mode/types.d.ts.map +1 -0
  112. package/dist/src/read-mode/types.js +1 -0
  113. package/dist/src/server/base-server.d.ts +112 -0
  114. package/dist/src/server/base-server.d.ts.map +1 -0
  115. package/dist/src/server/base-server.js +1280 -0
  116. package/dist/src/server/builder.d.ts +30 -0
  117. package/dist/src/server/builder.d.ts.map +1 -0
  118. package/dist/src/server/builder.js +89 -0
  119. package/dist/src/server/constants.d.ts +2 -0
  120. package/dist/src/server/constants.d.ts.map +1 -0
  121. package/dist/src/server/constants.js +1 -0
  122. package/dist/src/server/error.d.ts +30 -0
  123. package/dist/src/server/error.d.ts.map +1 -0
  124. package/dist/src/server/error.js +47 -0
  125. package/dist/src/server/event-emitter.d.ts +8 -0
  126. package/dist/src/server/event-emitter.d.ts.map +1 -0
  127. package/dist/src/server/event-emitter.js +10 -0
  128. package/dist/src/server/listener/index.d.ts +2 -0
  129. package/dist/src/server/listener/index.d.ts.map +1 -0
  130. package/dist/src/server/listener/index.js +1 -0
  131. package/dist/src/server/listener/listener-manager.d.ts +27 -0
  132. package/dist/src/server/listener/listener-manager.d.ts.map +1 -0
  133. package/dist/src/server/listener/listener-manager.js +401 -0
  134. package/dist/src/server/listener/transmitter/factory.d.ts +8 -0
  135. package/dist/src/server/listener/transmitter/factory.d.ts.map +1 -0
  136. package/dist/src/server/listener/transmitter/factory.js +25 -0
  137. package/dist/src/server/listener/transmitter/internal.d.ts +34 -0
  138. package/dist/src/server/listener/transmitter/internal.d.ts.map +1 -0
  139. package/dist/src/server/listener/transmitter/internal.js +87 -0
  140. package/dist/src/server/listener/transmitter/pull-responder.d.ts +38 -0
  141. package/dist/src/server/listener/transmitter/pull-responder.d.ts.map +1 -0
  142. package/dist/src/server/listener/transmitter/pull-responder.js +256 -0
  143. package/dist/src/server/listener/transmitter/switchboard-push.d.ts +9 -0
  144. package/dist/src/server/listener/transmitter/switchboard-push.d.ts.map +1 -0
  145. package/dist/src/server/listener/transmitter/switchboard-push.js +77 -0
  146. package/dist/src/server/listener/transmitter/types.d.ts +20 -0
  147. package/dist/src/server/listener/transmitter/types.d.ts.map +1 -0
  148. package/dist/src/server/listener/transmitter/types.js +1 -0
  149. package/dist/src/server/listener/util.d.ts +2 -0
  150. package/dist/src/server/listener/util.d.ts.map +1 -0
  151. package/dist/src/server/listener/util.js +22 -0
  152. package/dist/src/server/sync-manager.d.ts +30 -0
  153. package/dist/src/server/sync-manager.d.ts.map +1 -0
  154. package/dist/src/server/sync-manager.js +287 -0
  155. package/dist/src/server/types.d.ts +308 -0
  156. package/dist/src/server/types.d.ts.map +1 -0
  157. package/dist/src/server/types.js +12 -0
  158. package/dist/src/server/utils.d.ts +8 -0
  159. package/dist/src/server/utils.d.ts.map +1 -0
  160. package/dist/src/server/utils.js +47 -0
  161. package/dist/src/storage/base.d.ts +36 -0
  162. package/dist/src/storage/base.d.ts.map +1 -0
  163. package/dist/src/storage/base.js +4 -0
  164. package/dist/src/storage/browser.d.ts +36 -0
  165. package/dist/src/storage/browser.d.ts.map +1 -0
  166. package/dist/src/storage/browser.js +155 -0
  167. package/dist/src/storage/filesystem.d.ts +33 -0
  168. package/dist/src/storage/filesystem.d.ts.map +1 -0
  169. package/dist/src/storage/filesystem.js +197 -0
  170. package/dist/src/storage/memory.d.ts +33 -0
  171. package/dist/src/storage/memory.d.ts.map +1 -0
  172. package/dist/src/storage/memory.js +139 -0
  173. package/dist/src/storage/prisma.d.ts +67 -0
  174. package/dist/src/storage/prisma.d.ts.map +1 -0
  175. package/dist/src/storage/prisma.js +445 -0
  176. package/dist/src/storage/sequelize.d.ts +32 -0
  177. package/dist/src/storage/sequelize.d.ts.map +1 -0
  178. package/dist/src/storage/sequelize.js +373 -0
  179. package/dist/src/storage/types.d.ts +43 -0
  180. package/dist/src/storage/types.d.ts.map +1 -0
  181. package/dist/src/storage/types.js +1 -0
  182. package/dist/src/utils/default-drives-manager.d.ts +29 -0
  183. package/dist/src/utils/default-drives-manager.d.ts.map +1 -0
  184. package/dist/src/utils/default-drives-manager.js +208 -0
  185. package/dist/src/utils/graphql.d.ts +34 -0
  186. package/dist/src/utils/graphql.d.ts.map +1 -0
  187. package/dist/src/utils/graphql.js +183 -0
  188. package/dist/src/utils/logger.d.ts +27 -0
  189. package/dist/src/utils/logger.d.ts.map +1 -0
  190. package/dist/src/utils/logger.js +105 -0
  191. package/dist/src/utils/migrations.d.ts +4 -0
  192. package/dist/src/utils/migrations.d.ts.map +1 -0
  193. package/dist/src/utils/migrations.js +41 -0
  194. package/dist/src/utils/misc.d.ts +11 -0
  195. package/dist/src/utils/misc.d.ts.map +1 -0
  196. package/dist/src/utils/misc.js +43 -0
  197. package/dist/src/utils/run-asap.d.ts +12 -0
  198. package/dist/src/utils/run-asap.d.ts.map +1 -0
  199. package/dist/src/utils/run-asap.js +131 -0
  200. package/dist/test/document-helpers/utils.d.ts +8 -0
  201. package/dist/test/document-helpers/utils.d.ts.map +1 -0
  202. package/dist/test/document-helpers/utils.js +21 -0
  203. package/dist/test/utils.d.ts +48 -0
  204. package/dist/test/utils.d.ts.map +1 -0
  205. package/dist/test/utils.js +132 -0
  206. package/dist/test/vitest-setup.d.ts +2 -0
  207. package/dist/test/vitest-setup.d.ts.map +1 -0
  208. package/dist/test/vitest-setup.js +4 -0
  209. package/dist/tsconfig.tsbuildinfo +1 -0
  210. package/dist/vitest.config.d.ts +3 -0
  211. package/dist/vitest.config.d.ts.map +1 -0
  212. package/dist/vitest.config.js +20 -0
  213. package/package.json +20 -38
  214. package/src/cache/index.ts +0 -2
  215. package/src/cache/memory.ts +0 -33
  216. package/src/cache/redis.ts +0 -56
  217. package/src/cache/types.ts +0 -9
  218. package/src/index.ts +0 -4
  219. package/src/queue/base.ts +0 -320
  220. package/src/queue/index.ts +0 -2
  221. package/src/queue/redis.ts +0 -144
  222. package/src/queue/types.ts +0 -79
  223. package/src/read-mode/errors.ts +0 -19
  224. package/src/read-mode/index.ts +0 -125
  225. package/src/read-mode/service.ts +0 -207
  226. package/src/read-mode/types.ts +0 -108
  227. package/src/server/error.ts +0 -70
  228. package/src/server/index.ts +0 -2444
  229. package/src/server/listener/index.ts +0 -2
  230. package/src/server/listener/manager.ts +0 -652
  231. package/src/server/listener/transmitter/index.ts +0 -4
  232. package/src/server/listener/transmitter/internal.ts +0 -143
  233. package/src/server/listener/transmitter/pull-responder.ts +0 -462
  234. package/src/server/listener/transmitter/switchboard-push.ts +0 -125
  235. package/src/server/listener/transmitter/types.ts +0 -27
  236. package/src/server/types.ts +0 -596
  237. package/src/server/utils.ts +0 -82
  238. package/src/storage/base.ts +0 -81
  239. package/src/storage/browser.ts +0 -238
  240. package/src/storage/filesystem.ts +0 -297
  241. package/src/storage/index.ts +0 -2
  242. package/src/storage/memory.ts +0 -211
  243. package/src/storage/prisma.ts +0 -653
  244. package/src/storage/sequelize.ts +0 -498
  245. package/src/storage/types.ts +0 -97
  246. package/src/utils/default-drives-manager.ts +0 -341
  247. package/src/utils/document-helpers.ts +0 -21
  248. package/src/utils/graphql.ts +0 -301
  249. package/src/utils/index.ts +0 -90
  250. package/src/utils/logger.ts +0 -38
  251. package/src/utils/migrations.ts +0 -58
  252. package/src/utils/run-asap.ts +0 -156
@@ -1,2 +0,0 @@
1
- export * from "./manager";
2
- export * from "./transmitter";
@@ -1,652 +0,0 @@
1
- import {
2
- DocumentDriveDocument,
3
- ListenerFilter,
4
- } from "document-model-libs/document-drive";
5
- import { OperationScope } from "document-model/document";
6
- import { logger } from "../../utils/logger";
7
- import { OperationError } from "../error";
8
- import {
9
- DefaultListenerManagerOptions,
10
- DriveUpdateErrorHandler,
11
- ErrorStatus,
12
- GetStrandsOptions,
13
- IBaseDocumentDriveServer,
14
- IListenerManager,
15
- Listener,
16
- ListenerManagerOptions,
17
- ListenerState,
18
- ListenerUpdate,
19
- OperationUpdate,
20
- StrandUpdate,
21
- SynchronizationUnit,
22
- SynchronizationUnitQuery,
23
- } from "../types";
24
- import { StrandUpdateSource } from "./transmitter/types";
25
-
26
- const ENABLE_SYNC_DEBUG = false;
27
-
28
- function debounce<T extends unknown[], R>(
29
- func: (...args: T) => Promise<R>,
30
- delay = 250,
31
- ) {
32
- let timer: number;
33
- return (immediate: boolean, ...args: T) => {
34
- if (timer) {
35
- clearTimeout(timer);
36
- }
37
- return new Promise<R>((resolve, reject) => {
38
- if (immediate) {
39
- func(...args)
40
- .then(resolve)
41
- .catch(reject);
42
- } else {
43
- timer = setTimeout(() => {
44
- func(...args)
45
- .then(resolve)
46
- .catch(reject);
47
- }, delay) as unknown as number;
48
- }
49
- });
50
- };
51
- }
52
-
53
- export class ListenerManager implements IListenerManager {
54
- static LISTENER_UPDATE_DELAY = 250;
55
- private debugID = `[LM #${Math.floor(Math.random() * 999)}]`;
56
- protected driveServer: IBaseDocumentDriveServer;
57
- protected options: ListenerManagerOptions;
58
-
59
- // driveId -> listenerId -> listenerState
60
- protected listenerStateByDriveId = new Map<
61
- string,
62
- Map<string, ListenerState>
63
- >();
64
-
65
- constructor(
66
- drive: IBaseDocumentDriveServer,
67
- listenerState = new Map<string, Map<string, ListenerState>>(),
68
- options: ListenerManagerOptions = DefaultListenerManagerOptions,
69
- ) {
70
- this.debugLog(`constructor(...)`);
71
- this.driveServer = drive;
72
- this.listenerStateByDriveId = listenerState;
73
- this.options = { ...DefaultListenerManagerOptions, ...options };
74
- }
75
-
76
- private debugLog(...data: any[]) {
77
- if (!ENABLE_SYNC_DEBUG) {
78
- return;
79
- }
80
-
81
- if (data.length > 0 && typeof data[0] === "string") {
82
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
83
- console.log(`${this.debugID} ${data[0]}`, ...data.slice(1));
84
- } else {
85
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
86
- console.log(this.debugID, ...data);
87
- }
88
- }
89
-
90
- async initialize(handler: DriveUpdateErrorHandler) {
91
- this.debugLog("initialize(...)");
92
- // if network connect comes back online
93
- // then triggers the listeners update
94
- if (typeof window !== "undefined") {
95
- window.addEventListener("online", () => {
96
- this.triggerUpdate(false, { type: "local" }, handler).catch((error) => {
97
- logger.error("Non handled error updating listeners", error);
98
- });
99
- });
100
- }
101
- }
102
-
103
- driveHasListeners(driveId: string) {
104
- return this.listenerStateByDriveId.has(driveId);
105
- }
106
-
107
- async setListener(driveId: string, listener: Listener) {
108
- this.debugLog(
109
- `setListener(drive: ${driveId}, listener: ${listener.listenerId})`,
110
- );
111
-
112
- // slight code smell -- drive id may not need to be on listener or not passed in
113
- if (driveId !== listener.driveId) {
114
- throw new Error("Drive ID mismatch");
115
- }
116
-
117
- let existingState;
118
- try {
119
- existingState = this.getListenerState(driveId, listener.listenerId);
120
- } catch {
121
- existingState = {};
122
- }
123
-
124
- // keep existing state if it exists
125
- this.setListenerState(driveId, listener.listenerId, {
126
- ...existingState,
127
- block: listener.block,
128
- driveId: listener.driveId,
129
- pendingTimeout: "0",
130
- listener,
131
- listenerStatus: "CREATED",
132
- syncUnits: new Map(),
133
- });
134
-
135
- this.triggerUpdate(true, { type: "local" });
136
- }
137
-
138
- async removeListener(driveId: string, listenerId: string) {
139
- this.debugLog("setListener()");
140
-
141
- const driveMap = this.listenerStateByDriveId.get(driveId);
142
- if (!driveMap) {
143
- return false;
144
- }
145
-
146
- return Promise.resolve(driveMap.delete(listenerId));
147
- }
148
-
149
- async removeSyncUnits(
150
- driveId: string,
151
- syncUnits: Pick<SynchronizationUnit, "syncId">[],
152
- ): Promise<void> {
153
- const listeners = this.listenerStateByDriveId.get(driveId);
154
- if (!listeners) {
155
- return;
156
- }
157
- for (const [, listener] of listeners) {
158
- for (const syncUnit of syncUnits) {
159
- listener.syncUnits.delete(syncUnit.syncId);
160
- }
161
- }
162
- return Promise.resolve();
163
- }
164
-
165
- async updateSynchronizationRevisions(
166
- driveId: string,
167
- syncUnits: SynchronizationUnit[],
168
- source: StrandUpdateSource,
169
- willUpdate?: (listeners: Listener[]) => void,
170
- onError?: (error: Error, driveId: string, listener: ListenerState) => void,
171
- forceSync = false,
172
- ) {
173
- const listenerIdToListenerState = this.listenerStateByDriveId.get(driveId);
174
- if (!listenerIdToListenerState) {
175
- return [];
176
- }
177
-
178
- const outdatedListeners: Listener[] = [];
179
- for (const [, listenerState] of listenerIdToListenerState) {
180
- if (
181
- outdatedListeners.find(
182
- (l) => l.listenerId === listenerState.listener.listenerId,
183
- )
184
- ) {
185
- continue;
186
- }
187
-
188
- const transmitter = listenerState.listener.transmitter;
189
- if (!transmitter?.transmit) {
190
- continue;
191
- }
192
-
193
- for (const syncUnit of syncUnits) {
194
- if (!this._checkFilter(listenerState.listener.filter, syncUnit)) {
195
- continue;
196
- }
197
-
198
- const listenerRev = listenerState.syncUnits.get(syncUnit.syncId);
199
-
200
- if (!listenerRev || listenerRev.listenerRev < syncUnit.revision) {
201
- outdatedListeners.push(listenerState.listener);
202
- break;
203
- }
204
- }
205
- }
206
-
207
- if (outdatedListeners.length) {
208
- willUpdate?.(outdatedListeners);
209
- return this.triggerUpdate(forceSync, source, onError);
210
- }
211
- return [];
212
- }
213
-
214
- async updateListenerRevision(
215
- listenerId: string,
216
- driveId: string,
217
- syncId: string,
218
- listenerRev: number,
219
- ): Promise<void> {
220
- const drive = this.listenerStateByDriveId.get(driveId);
221
- if (!drive) {
222
- return;
223
- }
224
-
225
- const listener = drive.get(listenerId);
226
- if (!listener) {
227
- return;
228
- }
229
-
230
- const lastUpdated = new Date().toISOString();
231
- const entry = listener.syncUnits.get(syncId);
232
- if (entry) {
233
- entry.listenerRev = listenerRev;
234
- entry.lastUpdated = lastUpdated;
235
- } else {
236
- listener.syncUnits.set(syncId, { listenerRev, lastUpdated });
237
- }
238
-
239
- return Promise.resolve();
240
- }
241
-
242
- triggerUpdate = debounce(
243
- this._triggerUpdate.bind(this),
244
- ListenerManager.LISTENER_UPDATE_DELAY,
245
- );
246
-
247
- private async _triggerUpdate(
248
- source: StrandUpdateSource,
249
- onError?: (error: Error, driveId: string, listener: ListenerState) => void,
250
- maxContinues = 500,
251
- ) {
252
- this.debugLog(
253
- `_triggerUpdate(source: ${source.type}, maxContinues: ${maxContinues})`,
254
- this.listenerStateByDriveId,
255
- );
256
-
257
- if (maxContinues < 0) {
258
- throw new Error("Maximum retries exhausted.");
259
- }
260
-
261
- const listenerUpdates: ListenerUpdate[] = [];
262
-
263
- for (const [driveId, drive] of this.listenerStateByDriveId) {
264
- for (const [listenerId, listenerState] of drive) {
265
- const transmitter = listenerState.listener.transmitter;
266
-
267
- if (!transmitter?.transmit) {
268
- this.debugLog(`Transmitter not set on listener: ${listenerId}`);
269
- continue;
270
- }
271
-
272
- const syncUnits = await this.getListenerSyncUnits(driveId, listenerId);
273
- const strandUpdates: StrandUpdate[] = [];
274
-
275
- this.debugLog("syncUnits", syncUnits);
276
-
277
- // TODO change to push one after the other, reusing operation data
278
- const tasks = syncUnits.map((syncUnit) => async () => {
279
- const unitState = listenerState.syncUnits.get(syncUnit.syncId);
280
-
281
- if (unitState && unitState.listenerRev >= syncUnit.revision) {
282
- this.debugLog(
283
- `Abandoning push for sync unit ${syncUnit.syncId}: already up-to-date (${unitState.listenerRev} >= ${syncUnit.revision})`,
284
- );
285
- return;
286
- } else {
287
- this.debugLog(
288
- `Listener out-of-date for sync unit ${syncUnit.syncId}: ${unitState?.listenerRev} < ${syncUnit.revision}`,
289
- );
290
- }
291
-
292
- const opData: OperationUpdate[] = [];
293
- try {
294
- const data = await this.driveServer.getOperationData(
295
- // TODO - join queries, DEAL WITH INVALID SYNC ID ERROR
296
- driveId,
297
- syncUnit.syncId,
298
- {
299
- fromRevision: unitState?.listenerRev,
300
- },
301
- );
302
- opData.push(...data);
303
- } catch (e) {
304
- logger.error(e);
305
- }
306
-
307
- if (!opData.length) {
308
- this.debugLog(
309
- `Abandoning push for ${syncUnit.syncId}: no operations found`,
310
- );
311
- return;
312
- }
313
-
314
- strandUpdates.push({
315
- driveId,
316
- documentId: syncUnit.documentId,
317
- branch: syncUnit.branch,
318
- operations: opData,
319
- scope: syncUnit.scope as OperationScope,
320
- });
321
- });
322
-
323
- if (this.options.sequentialUpdates) {
324
- this.debugLog(
325
- `Collecting ${tasks.length} syncUnit strandUpdates in sequence`,
326
- );
327
- for (const task of tasks) {
328
- await task();
329
- }
330
- } else {
331
- this.debugLog(
332
- `Collecting ${tasks.length} syncUnit strandUpdates in parallel`,
333
- );
334
- await Promise.all(tasks.map((task) => task()));
335
- }
336
-
337
- if (strandUpdates.length == 0) {
338
- this.debugLog(`No strandUpdates needed for listener ${listenerId}`);
339
- continue;
340
- }
341
-
342
- listenerState.pendingTimeout = new Date(
343
- new Date().getTime() / 1000 + 300,
344
- ).toISOString();
345
-
346
- listenerState.listenerStatus = "PENDING";
347
-
348
- // TODO update listeners in parallel, blocking for listeners with block=true
349
- try {
350
- this.debugLog(
351
- `_triggerUpdate(source: ${source.type}) > transmitter.transmit`,
352
- );
353
-
354
- const listenerRevisions = await transmitter.transmit(
355
- strandUpdates,
356
- source,
357
- );
358
-
359
- this.debugLog(
360
- `_triggerUpdate(source: ${source.type}) > transmission succeeded`,
361
- listenerRevisions,
362
- );
363
-
364
- listenerState.pendingTimeout = "0";
365
- listenerState.listenerStatus = "PENDING";
366
-
367
- const lastUpdated = new Date().toISOString();
368
- let continuationNeeded = false;
369
-
370
- for (const revision of listenerRevisions) {
371
- const syncUnit = syncUnits.find(
372
- (unit) =>
373
- revision.documentId === unit.documentId &&
374
- revision.scope === unit.scope &&
375
- revision.branch === unit.branch,
376
- );
377
-
378
- if (syncUnit) {
379
- listenerState.syncUnits.set(syncUnit.syncId, {
380
- lastUpdated,
381
- listenerRev: revision.revision,
382
- });
383
-
384
- // Check for revision status vv
385
- const su = strandUpdates.find(
386
- (su) =>
387
- su.driveId === revision.driveId &&
388
- su.documentId === revision.documentId &&
389
- su.scope === revision.scope &&
390
- su.branch === revision.branch,
391
- );
392
-
393
- if (su && su.operations.length > 0) {
394
- const suIndex = su.operations.at(
395
- su.operations.length - 1,
396
- )?.index;
397
- if (suIndex !== revision.revision) {
398
- this.debugLog(
399
- `Revision still out-of-date for ${su.documentId}:${su.scope}:${su.branch} ${suIndex} <> ${revision.revision}`,
400
- );
401
- continuationNeeded = true;
402
- } else {
403
- this.debugLog(
404
- `Revision match for ${su.documentId}:${su.scope}:${su.branch} ${suIndex}`,
405
- );
406
- }
407
- } else {
408
- this.debugLog(
409
- `Cannot find strand update for (${revision.documentId}:${revision.scope}:${revision.branch} in drive ${revision.driveId})`,
410
- );
411
- }
412
- // Check for revision status ^^
413
- } else {
414
- logger.warn(
415
- `Received revision for untracked unit for listener ${listenerState.listener.listenerId}`,
416
- revision,
417
- );
418
- }
419
- }
420
-
421
- for (const revision of listenerRevisions) {
422
- const error = revision.status === "ERROR";
423
- if (revision.error?.includes("Missing operations")) {
424
- continuationNeeded = true;
425
- } else if (error) {
426
- throw new OperationError(
427
- revision.status as ErrorStatus,
428
- undefined,
429
- revision.error,
430
- revision.error,
431
- );
432
- }
433
- }
434
-
435
- if (!continuationNeeded) {
436
- listenerUpdates.push({
437
- listenerId: listenerState.listener.listenerId,
438
- listenerRevisions,
439
- });
440
- } else {
441
- const updates = await this._triggerUpdate(
442
- source,
443
- onError,
444
- maxContinues - 1,
445
- );
446
- listenerUpdates.push(...updates);
447
- }
448
-
449
- listenerState.listenerStatus = "SUCCESS";
450
- } catch (e) {
451
- // TODO: Handle error based on listener params (blocking, retry, etc)
452
- onError?.(e as Error, driveId, listenerState);
453
- listenerState.listenerStatus =
454
- e instanceof OperationError ? e.status : "ERROR";
455
- }
456
- }
457
- }
458
-
459
- this.debugLog(
460
- `Returning listener updates (maxContinues: ${maxContinues})`,
461
- listenerUpdates,
462
- );
463
-
464
- return listenerUpdates;
465
- }
466
-
467
- private _checkFilter(filter: ListenerFilter, syncUnit: SynchronizationUnit) {
468
- const { branch, documentId, scope, documentType } = syncUnit;
469
- // TODO: Needs to be optimized
470
- if (
471
- (!filter.branch ||
472
- filter.branch.includes(branch) ||
473
- filter.branch.includes("*")) &&
474
- (!filter.documentId ||
475
- filter.documentId.includes(documentId) ||
476
- filter.documentId.includes("*")) &&
477
- (!filter.scope ||
478
- filter.scope.includes(scope) ||
479
- filter.scope.includes("*")) &&
480
- (!filter.documentType ||
481
- filter.documentType.includes(documentType) ||
482
- filter.documentType.includes("*"))
483
- ) {
484
- return true;
485
- }
486
- return false;
487
- }
488
-
489
- getListenerSyncUnits(
490
- driveId: string,
491
- listenerId: string,
492
- loadedDrive?: DocumentDriveDocument,
493
- ) {
494
- const listener = this.listenerStateByDriveId.get(driveId)?.get(listenerId);
495
- if (!listener) {
496
- return [];
497
- }
498
- const filter = listener.listener.filter;
499
- return this.driveServer.getSynchronizationUnits(
500
- driveId,
501
- filter.documentId ?? ["*"],
502
- filter.scope ?? ["*"],
503
- filter.branch ?? ["*"],
504
- filter.documentType ?? ["*"],
505
- loadedDrive,
506
- );
507
- }
508
-
509
- getListenerSyncUnitIds(
510
- driveId: string,
511
- listenerId: string,
512
- ): Promise<SynchronizationUnitQuery[]> {
513
- const listener = this.listenerStateByDriveId.get(driveId)?.get(listenerId);
514
- if (!listener) {
515
- return Promise.resolve([]);
516
- }
517
- const filter = listener.listener.filter;
518
- return this.driveServer.getSynchronizationUnitsIds(
519
- driveId,
520
- filter.documentId ?? ["*"],
521
- filter.scope ?? ["*"],
522
- filter.branch ?? ["*"],
523
- filter.documentType ?? ["*"],
524
- );
525
- }
526
-
527
- async removeDrive(driveId: string): Promise<void> {
528
- const listenerIdToListenerState = this.listenerStateByDriveId.get(driveId);
529
- if (!listenerIdToListenerState) {
530
- return;
531
- }
532
-
533
- // delete first
534
- this.listenerStateByDriveId.delete(driveId);
535
-
536
- for (const [_, listenerState] of listenerIdToListenerState) {
537
- // guarantee that all disconnects are called
538
- try {
539
- await listenerState.listener.transmitter?.disconnect?.();
540
- } catch (error) {
541
- logger.error(error);
542
- }
543
- }
544
- }
545
-
546
- async getStrands(
547
- driveId: string,
548
- listenerId: string,
549
- options?: GetStrandsOptions,
550
- ): Promise<StrandUpdate[]> {
551
- // this will throw if listenerState is not found
552
- const listenerState = this.getListenerState(driveId, listenerId);
553
-
554
- // fetch operations from drive and prepare strands
555
- const strands: StrandUpdate[] = [];
556
-
557
- const drive = await this.driveServer.getDrive(driveId);
558
- const syncUnits = await this.getListenerSyncUnits(
559
- driveId,
560
- listenerId,
561
- drive,
562
- );
563
-
564
- const limit = options?.limit; // maximum number of operations to send across all sync units
565
- let operationsCount = 0; // total amount of operations that have been retrieved
566
-
567
- const tasks = syncUnits.map((syncUnit) => async () => {
568
- if (limit && operationsCount >= limit) {
569
- // break;
570
- return;
571
- }
572
- if (syncUnit.revision < 0) {
573
- return;
574
- }
575
- const entry = listenerState.syncUnits.get(syncUnit.syncId);
576
- if (entry && entry.listenerRev >= syncUnit.revision) {
577
- return;
578
- }
579
-
580
- const { documentId, driveId, scope, branch } = syncUnit;
581
- try {
582
- const operations = await this.driveServer.getOperationData(
583
- // DEAL WITH INVALID SYNC ID ERROR
584
- driveId,
585
- syncUnit.syncId,
586
- {
587
- since: options?.since,
588
- fromRevision: options?.fromRevision ?? entry?.listenerRev,
589
- limit: limit ? limit - operationsCount : undefined,
590
- },
591
- drive,
592
- );
593
-
594
- if (!operations.length) {
595
- return;
596
- }
597
-
598
- operationsCount += operations.length;
599
-
600
- strands.push({
601
- driveId,
602
- documentId,
603
- scope: scope as OperationScope,
604
- branch,
605
- operations,
606
- });
607
- } catch (error) {
608
- logger.error(error);
609
- return;
610
- }
611
- });
612
-
613
- if (this.options.sequentialUpdates) {
614
- for (const task of tasks) {
615
- await task();
616
- }
617
- } else {
618
- await Promise.all(tasks.map((task) => task()));
619
- }
620
-
621
- return strands;
622
- }
623
-
624
- getListenerState(driveId: string, listenerId: string) {
625
- let listenerStateByListenerId = this.listenerStateByDriveId.get(driveId);
626
- if (!listenerStateByListenerId) {
627
- listenerStateByListenerId = new Map();
628
- this.listenerStateByDriveId.set(driveId, listenerStateByListenerId);
629
- }
630
-
631
- const listenerState = listenerStateByListenerId.get(listenerId);
632
- if (!listenerState) {
633
- throw new Error("Listener not found");
634
- }
635
-
636
- return listenerState;
637
- }
638
-
639
- setListenerState(
640
- driveId: string,
641
- listenerId: string,
642
- listenerState: ListenerState,
643
- ) {
644
- let listenerStateByListenerId = this.listenerStateByDriveId.get(driveId);
645
- if (!listenerStateByListenerId) {
646
- listenerStateByListenerId = new Map();
647
- this.listenerStateByDriveId.set(driveId, listenerStateByListenerId);
648
- }
649
-
650
- listenerStateByListenerId.set(listenerId, listenerState);
651
- }
652
- }
@@ -1,4 +0,0 @@
1
- export * from "./internal";
2
- export * from "./pull-responder";
3
- export * from "./switchboard-push";
4
- export * from "./types";