document-drive 1.19.1 → 1.20.0

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
@@ -0,0 +1,401 @@
1
+ import { DefaultListenerManagerOptions, } from "#server/types";
2
+ import { childLogger } from "document-drive";
3
+ import { OperationError } from "#server/error";
4
+ import { debounce } from "./util.js";
5
+ const ENABLE_SYNC_DEBUG = false;
6
+ export class ListenerManager {
7
+ static LISTENER_UPDATE_DELAY = 250;
8
+ logger = childLogger([
9
+ "ListenerManager",
10
+ Math.floor(Math.random() * 999).toString(),
11
+ ]);
12
+ syncManager;
13
+ options;
14
+ // driveId -> listenerId -> listenerState
15
+ listenerStateByDriveId = new Map();
16
+ constructor(syncManager, options = DefaultListenerManagerOptions) {
17
+ this.syncManager = syncManager;
18
+ this.options = { ...DefaultListenerManagerOptions, ...options };
19
+ this.logger.verbose(`constructor(...)`);
20
+ }
21
+ async initialize(handler) {
22
+ this.logger.verbose("initialize(...)");
23
+ // if network connect comes back online
24
+ // then triggers the listeners update
25
+ if (typeof window !== "undefined") {
26
+ window.addEventListener("online", () => {
27
+ this.triggerUpdate(false, { type: "local" }, handler).catch((error) => {
28
+ this.logger.error("Non handled error updating listeners", error);
29
+ });
30
+ });
31
+ }
32
+ }
33
+ driveHasListeners(driveId) {
34
+ return this.listenerStateByDriveId.has(driveId);
35
+ }
36
+ async setListener(driveId, listener) {
37
+ this.logger.verbose(`setListener(drive: ${driveId}, listener: ${listener.listenerId})`);
38
+ // slight code smell -- drive id may not need to be on listener or not passed in
39
+ if (driveId !== listener.driveId) {
40
+ throw new Error("Drive ID mismatch");
41
+ }
42
+ let existingState;
43
+ try {
44
+ existingState = this.getListenerState(driveId, listener.listenerId);
45
+ }
46
+ catch {
47
+ existingState = {};
48
+ }
49
+ // keep existing state if it exists
50
+ this.setListenerState(driveId, listener.listenerId, {
51
+ ...existingState,
52
+ block: listener.block,
53
+ driveId: listener.driveId,
54
+ pendingTimeout: "0",
55
+ listener,
56
+ listenerStatus: "CREATED",
57
+ syncUnits: new Map(),
58
+ });
59
+ this.triggerUpdate(true, { type: "local" });
60
+ }
61
+ async removeListener(driveId, listenerId) {
62
+ this.logger.verbose("setListener()");
63
+ const driveMap = this.listenerStateByDriveId.get(driveId);
64
+ if (!driveMap) {
65
+ return false;
66
+ }
67
+ return Promise.resolve(driveMap.delete(listenerId));
68
+ }
69
+ async removeSyncUnits(driveId, syncUnits) {
70
+ const listeners = this.listenerStateByDriveId.get(driveId);
71
+ if (!listeners) {
72
+ return;
73
+ }
74
+ for (const [, listener] of listeners) {
75
+ for (const syncUnit of syncUnits) {
76
+ listener.syncUnits.delete(syncUnit.syncId);
77
+ }
78
+ }
79
+ return Promise.resolve();
80
+ }
81
+ async updateSynchronizationRevisions(driveId, syncUnits, source, willUpdate, onError, forceSync = false) {
82
+ const listenerIdToListenerState = this.listenerStateByDriveId.get(driveId);
83
+ if (!listenerIdToListenerState) {
84
+ return [];
85
+ }
86
+ const outdatedListeners = [];
87
+ for (const [, listenerState] of listenerIdToListenerState) {
88
+ if (outdatedListeners.find((l) => l.listenerId === listenerState.listener.listenerId)) {
89
+ continue;
90
+ }
91
+ const transmitter = listenerState.listener.transmitter;
92
+ if (!transmitter?.transmit) {
93
+ continue;
94
+ }
95
+ for (const syncUnit of syncUnits) {
96
+ if (!this._checkFilter(listenerState.listener.filter, syncUnit)) {
97
+ continue;
98
+ }
99
+ const listenerRev = listenerState.syncUnits.get(syncUnit.syncId);
100
+ if (!listenerRev || listenerRev.listenerRev < syncUnit.revision) {
101
+ outdatedListeners.push(listenerState.listener);
102
+ break;
103
+ }
104
+ }
105
+ }
106
+ if (outdatedListeners.length) {
107
+ willUpdate?.(outdatedListeners);
108
+ return this.triggerUpdate(forceSync, source, onError);
109
+ }
110
+ return [];
111
+ }
112
+ async updateListenerRevision(listenerId, driveId, syncId, listenerRev) {
113
+ const drive = this.listenerStateByDriveId.get(driveId);
114
+ if (!drive) {
115
+ return;
116
+ }
117
+ const listener = drive.get(listenerId);
118
+ if (!listener) {
119
+ return;
120
+ }
121
+ const lastUpdated = new Date().toISOString();
122
+ const entry = listener.syncUnits.get(syncId);
123
+ if (entry) {
124
+ entry.listenerRev = listenerRev;
125
+ entry.lastUpdated = lastUpdated;
126
+ }
127
+ else {
128
+ listener.syncUnits.set(syncId, { listenerRev, lastUpdated });
129
+ }
130
+ return Promise.resolve();
131
+ }
132
+ triggerUpdate = debounce(this._triggerUpdate.bind(this), ListenerManager.LISTENER_UPDATE_DELAY);
133
+ async _triggerUpdate(source, onError, maxContinues = 500) {
134
+ this.logger.verbose(`_triggerUpdate(source: ${source.type}, maxContinues: ${maxContinues})`, this.listenerStateByDriveId);
135
+ if (maxContinues < 0) {
136
+ throw new Error("Maximum retries exhausted.");
137
+ }
138
+ const listenerUpdates = [];
139
+ for (const [driveId, drive] of this.listenerStateByDriveId) {
140
+ for (const [listenerId, listenerState] of drive) {
141
+ const transmitter = listenerState.listener.transmitter;
142
+ if (!transmitter?.transmit) {
143
+ this.logger.verbose(`Transmitter not set on listener: ${listenerId}`);
144
+ continue;
145
+ }
146
+ const syncUnits = await this.getListenerSyncUnits(driveId, listenerId);
147
+ const strandUpdates = [];
148
+ this.logger.verbose("syncUnits", syncUnits);
149
+ // TODO change to push one after the other, reusing operation data
150
+ const tasks = syncUnits.map((syncUnit) => async () => {
151
+ const unitState = listenerState.syncUnits.get(syncUnit.syncId);
152
+ if (unitState && unitState.listenerRev >= syncUnit.revision) {
153
+ this.logger.verbose(`Abandoning push for sync unit ${syncUnit.syncId}: already up-to-date (${unitState.listenerRev} >= ${syncUnit.revision})`);
154
+ return;
155
+ }
156
+ else {
157
+ this.logger.verbose(`Listener out-of-date for sync unit ${syncUnit.syncId}: ${unitState?.listenerRev} < ${syncUnit.revision}`);
158
+ }
159
+ const opData = [];
160
+ try {
161
+ const data = await this.syncManager.getOperationData(
162
+ // TODO - join queries, DEAL WITH INVALID SYNC ID ERROR
163
+ driveId, syncUnit.syncId, {
164
+ fromRevision: unitState?.listenerRev,
165
+ });
166
+ opData.push(...data);
167
+ }
168
+ catch (e) {
169
+ this.logger.error(e);
170
+ }
171
+ if (!opData.length) {
172
+ this.logger.verbose(`Abandoning push for ${syncUnit.syncId}: no operations found`);
173
+ return;
174
+ }
175
+ strandUpdates.push({
176
+ driveId,
177
+ documentId: syncUnit.documentId,
178
+ branch: syncUnit.branch,
179
+ operations: opData,
180
+ scope: syncUnit.scope,
181
+ });
182
+ });
183
+ if (this.options.sequentialUpdates) {
184
+ this.logger.verbose(`Collecting ${tasks.length} syncUnit strandUpdates in sequence`);
185
+ for (const task of tasks) {
186
+ await task();
187
+ }
188
+ }
189
+ else {
190
+ this.logger.verbose(`Collecting ${tasks.length} syncUnit strandUpdates in parallel`);
191
+ await Promise.all(tasks.map((task) => task()));
192
+ }
193
+ if (strandUpdates.length == 0) {
194
+ this.logger.verbose(`No strandUpdates needed for listener ${listenerId}`);
195
+ continue;
196
+ }
197
+ listenerState.pendingTimeout = new Date(new Date().getTime() / 1000 + 300).toISOString();
198
+ listenerState.listenerStatus = "PENDING";
199
+ // TODO update listeners in parallel, blocking for listeners with block=true
200
+ try {
201
+ this.logger.verbose(`_triggerUpdate(source: ${source.type}) > transmitter.transmit`);
202
+ const listenerRevisions = await transmitter.transmit(strandUpdates, source);
203
+ this.logger.verbose(`_triggerUpdate(source: ${source.type}) > transmission succeeded`, listenerRevisions);
204
+ listenerState.pendingTimeout = "0";
205
+ listenerState.listenerStatus = "PENDING";
206
+ const lastUpdated = new Date().toISOString();
207
+ let continuationNeeded = false;
208
+ for (const revision of listenerRevisions) {
209
+ const syncUnit = syncUnits.find((unit) => revision.documentId === unit.documentId &&
210
+ revision.scope === unit.scope &&
211
+ revision.branch === unit.branch);
212
+ if (syncUnit) {
213
+ listenerState.syncUnits.set(syncUnit.syncId, {
214
+ lastUpdated,
215
+ listenerRev: revision.revision,
216
+ });
217
+ // Check for revision status vv
218
+ const su = strandUpdates.find((su) => su.driveId === revision.driveId &&
219
+ su.documentId === revision.documentId &&
220
+ su.scope === revision.scope &&
221
+ su.branch === revision.branch);
222
+ if (su && su.operations.length > 0) {
223
+ const suIndex = su.operations.at(su.operations.length - 1)?.index;
224
+ if (suIndex !== revision.revision) {
225
+ this.logger.verbose(`Revision still out-of-date for ${su.documentId}:${su.scope}:${su.branch} ${suIndex} <> ${revision.revision}`);
226
+ continuationNeeded = true;
227
+ }
228
+ else {
229
+ this.logger.verbose(`Revision match for ${su.documentId}:${su.scope}:${su.branch} ${suIndex}`);
230
+ }
231
+ }
232
+ else {
233
+ this.logger.verbose(`Cannot find strand update for (${revision.documentId}:${revision.scope}:${revision.branch} in drive ${revision.driveId})`);
234
+ }
235
+ // Check for revision status ^^
236
+ }
237
+ else {
238
+ this.logger.warn(`Received revision for untracked unit for listener ${listenerState.listener.listenerId}`, revision);
239
+ }
240
+ }
241
+ for (const revision of listenerRevisions) {
242
+ const error = revision.status === "ERROR";
243
+ if (revision.error?.includes("Missing operations")) {
244
+ continuationNeeded = true;
245
+ }
246
+ else if (error) {
247
+ throw new OperationError(revision.status, undefined, revision.error, revision.error);
248
+ }
249
+ }
250
+ if (!continuationNeeded) {
251
+ listenerUpdates.push({
252
+ listenerId: listenerState.listener.listenerId,
253
+ listenerRevisions,
254
+ });
255
+ }
256
+ else {
257
+ const updates = await this._triggerUpdate(source, onError, maxContinues - 1);
258
+ listenerUpdates.push(...updates);
259
+ }
260
+ listenerState.listenerStatus = "SUCCESS";
261
+ }
262
+ catch (e) {
263
+ // TODO: Handle error based on listener params (blocking, retry, etc)
264
+ onError?.(e, driveId, listenerState);
265
+ listenerState.listenerStatus =
266
+ e instanceof OperationError ? e.status : "ERROR";
267
+ }
268
+ }
269
+ }
270
+ this.logger.verbose(`Returning listener updates (maxContinues: ${maxContinues})`, listenerUpdates);
271
+ return listenerUpdates;
272
+ }
273
+ _checkFilter(filter, syncUnit) {
274
+ const { branch, documentId, scope, documentType } = syncUnit;
275
+ // TODO: Needs to be optimized
276
+ if ((!filter.branch ||
277
+ filter.branch.includes(branch) ||
278
+ filter.branch.includes("*")) &&
279
+ (!filter.documentId ||
280
+ filter.documentId.includes(documentId) ||
281
+ filter.documentId.includes("*")) &&
282
+ (!filter.scope ||
283
+ filter.scope.includes(scope) ||
284
+ filter.scope.includes("*")) &&
285
+ (!filter.documentType ||
286
+ filter.documentType.includes(documentType) ||
287
+ filter.documentType.includes("*"))) {
288
+ return true;
289
+ }
290
+ return false;
291
+ }
292
+ getListenerSyncUnits(driveId, listenerId) {
293
+ const listener = this.listenerStateByDriveId.get(driveId)?.get(listenerId);
294
+ if (!listener) {
295
+ return [];
296
+ }
297
+ const filter = listener.listener.filter;
298
+ return this.syncManager.getSynchronizationUnits(driveId, filter.documentId ?? ["*"], filter.scope ?? ["*"], filter.branch ?? ["*"], filter.documentType ?? ["*"]);
299
+ }
300
+ getListenerSyncUnitIds(driveId, listenerId) {
301
+ const listener = this.listenerStateByDriveId.get(driveId)?.get(listenerId);
302
+ if (!listener) {
303
+ return Promise.resolve([]);
304
+ }
305
+ const filter = listener.listener.filter;
306
+ return this.syncManager.getSynchronizationUnitsIds(driveId, filter.documentId ?? ["*"], filter.scope ?? ["*"], filter.branch ?? ["*"], filter.documentType ?? ["*"]);
307
+ }
308
+ async removeDrive(driveId) {
309
+ const listenerIdToListenerState = this.listenerStateByDriveId.get(driveId);
310
+ if (!listenerIdToListenerState) {
311
+ return;
312
+ }
313
+ // delete first
314
+ this.listenerStateByDriveId.delete(driveId);
315
+ for (const [_, listenerState] of listenerIdToListenerState) {
316
+ // guarantee that all disconnects are called
317
+ try {
318
+ await listenerState.listener.transmitter?.disconnect?.();
319
+ }
320
+ catch (error) {
321
+ this.logger.error(error);
322
+ }
323
+ }
324
+ }
325
+ async getStrands(driveId, listenerId, options) {
326
+ // this will throw if listenerState is not found
327
+ const listenerState = this.getListenerState(driveId, listenerId);
328
+ // fetch operations from drive and prepare strands
329
+ const strands = [];
330
+ const syncUnits = await this.getListenerSyncUnits(driveId, listenerId);
331
+ const limit = options?.limit; // maximum number of operations to send across all sync units
332
+ let operationsCount = 0; // total amount of operations that have been retrieved
333
+ const tasks = syncUnits.map((syncUnit) => async () => {
334
+ if (limit && operationsCount >= limit) {
335
+ // break;
336
+ return;
337
+ }
338
+ if (syncUnit.revision < 0) {
339
+ return;
340
+ }
341
+ const entry = listenerState.syncUnits.get(syncUnit.syncId);
342
+ if (entry && entry.listenerRev >= syncUnit.revision) {
343
+ return;
344
+ }
345
+ const { documentId, driveId, scope, branch } = syncUnit;
346
+ try {
347
+ const operations = await this.syncManager.getOperationData(
348
+ // DEAL WITH INVALID SYNC ID ERROR
349
+ driveId, syncUnit.syncId, {
350
+ since: options?.since,
351
+ fromRevision: options?.fromRevision ?? entry?.listenerRev,
352
+ limit: limit ? limit - operationsCount : undefined,
353
+ });
354
+ if (!operations.length) {
355
+ return;
356
+ }
357
+ operationsCount += operations.length;
358
+ strands.push({
359
+ driveId,
360
+ documentId,
361
+ scope: scope,
362
+ branch,
363
+ operations,
364
+ });
365
+ }
366
+ catch (error) {
367
+ this.logger.error(error);
368
+ return;
369
+ }
370
+ });
371
+ if (this.options.sequentialUpdates) {
372
+ for (const task of tasks) {
373
+ await task();
374
+ }
375
+ }
376
+ else {
377
+ await Promise.all(tasks.map((task) => task()));
378
+ }
379
+ return strands;
380
+ }
381
+ getListenerState(driveId, listenerId) {
382
+ let listenerStateByListenerId = this.listenerStateByDriveId.get(driveId);
383
+ if (!listenerStateByListenerId) {
384
+ listenerStateByListenerId = new Map();
385
+ this.listenerStateByDriveId.set(driveId, listenerStateByListenerId);
386
+ }
387
+ const listenerState = listenerStateByListenerId.get(listenerId);
388
+ if (!listenerState) {
389
+ throw new Error("Listener not found");
390
+ }
391
+ return listenerState;
392
+ }
393
+ setListenerState(driveId, listenerId, listenerState) {
394
+ let listenerStateByListenerId = this.listenerStateByDriveId.get(driveId);
395
+ if (!listenerStateByListenerId) {
396
+ listenerStateByListenerId = new Map();
397
+ this.listenerStateByDriveId.set(driveId, listenerStateByListenerId);
398
+ }
399
+ listenerStateByListenerId.set(listenerId, listenerState);
400
+ }
401
+ }
@@ -0,0 +1,8 @@
1
+ import { IBaseDocumentDriveServer, IListenerManager, ITransmitterFactory, Listener } from "#server/types";
2
+ import { ITransmitter } from "./types.js";
3
+ export default class TransmitterFactory implements ITransmitterFactory {
4
+ private readonly listenerManager;
5
+ constructor(listenerManager: IListenerManager);
6
+ instance(transmitterType: string, listener: Listener, driveServer: IBaseDocumentDriveServer): ITransmitter;
7
+ }
8
+ //# sourceMappingURL=factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../../../src/server/listener/transmitter/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,gBAAgB,EAChB,mBAAmB,EACnB,QAAQ,EACT,MAAM,eAAe,CAAC;AAIvB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,CAAC,OAAO,OAAO,kBAAmB,YAAW,mBAAmB;IACpE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAmB;gBAEvC,eAAe,EAAE,gBAAgB;IAI7C,QAAQ,CACN,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,wBAAwB,GACpC,YAAY;CAiBhB"}
@@ -0,0 +1,25 @@
1
+ import { InternalTransmitter } from "./internal.js";
2
+ import { PullResponderTransmitter } from "./pull-responder.js";
3
+ import { SwitchboardPushTransmitter } from "./switchboard-push.js";
4
+ export default class TransmitterFactory {
5
+ listenerManager;
6
+ constructor(listenerManager) {
7
+ this.listenerManager = listenerManager;
8
+ }
9
+ instance(transmitterType, listener, driveServer) {
10
+ switch (transmitterType) {
11
+ case "SwitchboardPush": {
12
+ if (!listener.callInfo?.data) {
13
+ throw new Error("No call info data: " + JSON.stringify(listener));
14
+ }
15
+ return new SwitchboardPushTransmitter(listener.callInfo.data);
16
+ }
17
+ case "Internal": {
18
+ return new InternalTransmitter(listener, driveServer);
19
+ }
20
+ default: {
21
+ return new PullResponderTransmitter(listener, this.listenerManager);
22
+ }
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,34 @@
1
+ import { GlobalStateFromDocument, LocalStateFromDocument, OperationFromDocument, OperationScope, PHDocument } from "document-model";
2
+ import { IBaseDocumentDriveServer, Listener, ListenerRevision, StrandUpdate } from "#server/types";
3
+ import { ITransmitter, StrandUpdateSource } from "./types.js";
4
+ export interface IReceiver {
5
+ onStrands: <TDocument extends PHDocument>(strands: InternalTransmitterUpdate<TDocument>[]) => Promise<void>;
6
+ onDisconnect: () => Promise<void>;
7
+ }
8
+ export type InternalOperationUpdate<TDocument extends PHDocument> = Omit<OperationFromDocument<TDocument>, "scope"> & {
9
+ state: GlobalStateFromDocument<TDocument> | LocalStateFromDocument<TDocument>;
10
+ previousState: GlobalStateFromDocument<TDocument> | LocalStateFromDocument<TDocument>;
11
+ };
12
+ export type InternalTransmitterUpdate<TDocument extends PHDocument> = {
13
+ driveId: string;
14
+ documentId: string;
15
+ scope: OperationScope;
16
+ branch: string;
17
+ operations: InternalOperationUpdate<TDocument>[];
18
+ state: GlobalStateFromDocument<TDocument> | LocalStateFromDocument<TDocument>;
19
+ };
20
+ export interface IInternalTransmitter extends ITransmitter {
21
+ setReceiver(receiver: IReceiver): void;
22
+ }
23
+ export declare class InternalTransmitter implements ITransmitter {
24
+ #private;
25
+ protected drive: IBaseDocumentDriveServer;
26
+ protected listener: Listener;
27
+ protected receiver: IReceiver | undefined;
28
+ constructor(listener: Listener, drive: IBaseDocumentDriveServer);
29
+ transmit(strands: StrandUpdate[], _source: StrandUpdateSource): Promise<ListenerRevision[]>;
30
+ setReceiver(receiver: IReceiver): void;
31
+ disconnect(): Promise<void>;
32
+ getListener(): Listener;
33
+ }
34
+ //# sourceMappingURL=internal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../../../../../src/server/listener/transmitter/internal.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,qBAAqB,EACrB,cAAc,EACd,UAAU,EACX,MAAM,gBAAgB,CAAC;AAMxB,OAAO,EAEL,wBAAwB,EACxB,QAAQ,EACR,gBAAgB,EAChB,YAAY,EACb,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE9D,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,CAAC,SAAS,SAAS,UAAU,EACtC,OAAO,EAAE,yBAAyB,CAAC,SAAS,CAAC,EAAE,KAC5C,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AAED,MAAM,MAAM,uBAAuB,CAAC,SAAS,SAAS,UAAU,IAAI,IAAI,CACtE,qBAAqB,CAAC,SAAS,CAAC,EAChC,OAAO,CACR,GAAG;IACF,KAAK,EAAE,uBAAuB,CAAC,SAAS,CAAC,GAAG,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAC9E,aAAa,EACT,uBAAuB,CAAC,SAAS,CAAC,GAClC,sBAAsB,CAAC,SAAS,CAAC,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,yBAAyB,CAAC,SAAS,SAAS,UAAU,IAAI;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,uBAAuB,CAAC,SAAS,CAAC,EAAE,CAAC;IACjD,KAAK,EAAE,uBAAuB,CAAC,SAAS,CAAC,GAAG,sBAAsB,CAAC,SAAS,CAAC,CAAC;CAC/E,CAAC;AAEF,MAAM,WAAW,oBAAqB,SAAQ,YAAY;IACxD,WAAW,CAAC,QAAQ,EAAE,SAAS,GAAG,IAAI,CAAC;CACxC;AAED,qBAAa,mBAAoB,YAAW,YAAY;;IACtD,SAAS,CAAC,KAAK,EAAE,wBAAwB,CAAC;IAC1C,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC7B,SAAS,CAAC,QAAQ,EAAE,SAAS,GAAG,SAAS,CAAC;gBAE9B,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,wBAAwB;IAkDzD,QAAQ,CACZ,OAAO,EAAE,YAAY,EAAE,EAEvB,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAiC9B,WAAW,CAAC,QAAQ,EAAE,SAAS;IAIzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC,WAAW,IAAI,QAAQ;CAGxB"}
@@ -0,0 +1,87 @@
1
+ import { logger } from "#utils/logger";
2
+ export class InternalTransmitter {
3
+ drive;
4
+ listener;
5
+ receiver;
6
+ constructor(listener, drive) {
7
+ this.listener = listener;
8
+ this.drive = drive;
9
+ }
10
+ async #buildInternalOperationUpdate(strand) {
11
+ const operations = [];
12
+ const stateByIndex = new Map();
13
+ const getStateByIndex = async (index) => {
14
+ const state = stateByIndex.get(index);
15
+ if (state) {
16
+ return state;
17
+ }
18
+ const getDocumentOptions = {
19
+ revisions: {
20
+ [strand.scope]: index,
21
+ },
22
+ checkHashes: false,
23
+ };
24
+ const document = await (strand.documentId
25
+ ? this.drive.getDocument(strand.driveId, strand.documentId, getDocumentOptions)
26
+ : this.drive.getDrive(strand.driveId, getDocumentOptions));
27
+ if (index < 0) {
28
+ stateByIndex.set(index, document.initialState.state[strand.scope]);
29
+ }
30
+ else {
31
+ stateByIndex.set(index, document.state[strand.scope]);
32
+ }
33
+ return stateByIndex.get(index);
34
+ };
35
+ for (const operation of strand.operations) {
36
+ operations.push({
37
+ ...operation,
38
+ state: await getStateByIndex(operation.index),
39
+ previousState: await getStateByIndex(operation.index - 1),
40
+ });
41
+ }
42
+ return operations;
43
+ }
44
+ async transmit(strands,
45
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
46
+ _source) {
47
+ if (!this.receiver) {
48
+ return [];
49
+ }
50
+ const updates = [];
51
+ for (const strand of strands) {
52
+ const operations = await this.#buildInternalOperationUpdate(strand);
53
+ const state = operations.at(-1)?.state ?? {};
54
+ updates.push({
55
+ ...strand,
56
+ operations,
57
+ state,
58
+ });
59
+ }
60
+ try {
61
+ await this.receiver.onStrands(updates);
62
+ return strands.map(({ operations, ...s }) => ({
63
+ ...s,
64
+ status: "SUCCESS",
65
+ revision: operations.at(operations.length - 1)?.index ?? -1,
66
+ }));
67
+ }
68
+ catch (error) {
69
+ logger.error(error);
70
+ // TODO check which strand caused an error
71
+ return strands.map(({ operations, ...s }) => ({
72
+ ...s,
73
+ status: "ERROR",
74
+ revision: (operations.at(0)?.index ?? 0) - 1,
75
+ }));
76
+ }
77
+ }
78
+ setReceiver(receiver) {
79
+ this.receiver = receiver;
80
+ }
81
+ async disconnect() {
82
+ await this.receiver?.onDisconnect();
83
+ }
84
+ getListener() {
85
+ return this.listener;
86
+ }
87
+ }
@@ -0,0 +1,38 @@
1
+ import { GetStrandsOptions, IListenerManager, IOperationResult, Listener, ListenerRevision, ListenerRevisionWithError, OperationUpdate, RemoteDriveOptions, StrandUpdate } from "#server/types";
2
+ import { ListenerFilter, Trigger } from "document-drive";
3
+ import { ITransmitter, PullResponderTrigger, StrandUpdateSource } from "./types.js";
4
+ export type OperationUpdateGraphQL = Omit<OperationUpdate, "input"> & {
5
+ input: string;
6
+ };
7
+ export type PullStrandsGraphQL = {
8
+ system: {
9
+ sync: {
10
+ strands: StrandUpdateGraphQL[];
11
+ };
12
+ };
13
+ };
14
+ export type CancelPullLoop = () => void;
15
+ export type StrandUpdateGraphQL = Omit<StrandUpdate, "operations"> & {
16
+ operations: OperationUpdateGraphQL[];
17
+ };
18
+ export interface IPullResponderTransmitter extends ITransmitter {
19
+ getStrands(options?: GetStrandsOptions): Promise<StrandUpdate[]>;
20
+ }
21
+ export declare class PullResponderTransmitter implements IPullResponderTransmitter {
22
+ private static staticLogger;
23
+ private logger;
24
+ private listener;
25
+ private manager;
26
+ constructor(listener: Listener, manager: IListenerManager);
27
+ getStrands(options?: GetStrandsOptions): Promise<StrandUpdate[]>;
28
+ disconnect(): Promise<void>;
29
+ processAcknowledge(driveId: string, listenerId: string, revisions: ListenerRevision[]): Promise<boolean>;
30
+ static registerPullResponder(driveId: string, url: string, filter: ListenerFilter): Promise<Listener["listenerId"]>;
31
+ static pullStrands(driveId: string, url: string, listenerId: string, options?: GetStrandsOptions): Promise<StrandUpdate[]>;
32
+ static acknowledgeStrands(driveId: string, url: string, listenerId: string, revisions: ListenerRevision[]): Promise<boolean>;
33
+ private static executePull;
34
+ static setupPull(driveId: string, trigger: PullResponderTrigger, onStrandUpdate: (strand: StrandUpdate, source: StrandUpdateSource) => Promise<IOperationResult>, onError: (error: Error) => void, onRevisions?: (revisions: ListenerRevisionWithError[]) => void, onAcknowledge?: (success: boolean) => void): CancelPullLoop;
35
+ static createPullResponderTrigger(driveId: string, url: string, options: Pick<RemoteDriveOptions, "pullInterval" | "pullFilter">): Promise<PullResponderTrigger>;
36
+ static isPullResponderTrigger(trigger: Trigger): trigger is PullResponderTrigger;
37
+ }
38
+ //# sourceMappingURL=pull-responder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull-responder.d.ts","sourceRoot":"","sources":["../../../../../src/server/listener/transmitter/pull-responder.ts"],"names":[],"mappings":"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"}