@powerhousedao/reactor 6.0.0-dev.69 → 6.0.0-dev.77

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 (240) hide show
  1. package/dist/src/index.d.ts +1 -1
  2. package/dist/src/index.d.ts.map +1 -1
  3. package/dist/src/index.js +19998 -62
  4. package/dist/src/sync/index.d.ts +1 -1
  5. package/dist/src/sync/index.d.ts.map +1 -1
  6. package/dist/src/sync/sync-manager.d.ts.map +1 -1
  7. package/dist/src/sync/types.d.ts +11 -0
  8. package/dist/src/sync/types.d.ts.map +1 -1
  9. package/package.json +17 -10
  10. package/dist/src/actions/index.js +0 -76
  11. package/dist/src/actions/index.js.map +0 -1
  12. package/dist/src/cache/buffer/ring-buffer.js +0 -69
  13. package/dist/src/cache/buffer/ring-buffer.js.map +0 -1
  14. package/dist/src/cache/collection-membership-cache.js +0 -33
  15. package/dist/src/cache/collection-membership-cache.js.map +0 -1
  16. package/dist/src/cache/document-meta-cache-types.js +0 -2
  17. package/dist/src/cache/document-meta-cache-types.js.map +0 -1
  18. package/dist/src/cache/document-meta-cache.js +0 -129
  19. package/dist/src/cache/document-meta-cache.js.map +0 -1
  20. package/dist/src/cache/index.js +0 -2
  21. package/dist/src/cache/index.js.map +0 -1
  22. package/dist/src/cache/kysely-operation-index.js +0 -345
  23. package/dist/src/cache/kysely-operation-index.js.map +0 -1
  24. package/dist/src/cache/kysely-write-cache.js +0 -411
  25. package/dist/src/cache/kysely-write-cache.js.map +0 -1
  26. package/dist/src/cache/lru/lru-tracker.js +0 -96
  27. package/dist/src/cache/lru/lru-tracker.js.map +0 -1
  28. package/dist/src/cache/operation-index-types.js +0 -4
  29. package/dist/src/cache/operation-index-types.js.map +0 -1
  30. package/dist/src/cache/write/interfaces.js +0 -2
  31. package/dist/src/cache/write/interfaces.js.map +0 -1
  32. package/dist/src/cache/write-cache-types.js +0 -2
  33. package/dist/src/cache/write-cache-types.js.map +0 -1
  34. package/dist/src/client/reactor-client.js +0 -497
  35. package/dist/src/client/reactor-client.js.map +0 -1
  36. package/dist/src/client/types.js +0 -14
  37. package/dist/src/client/types.js.map +0 -1
  38. package/dist/src/core/reactor-builder.js +0 -306
  39. package/dist/src/core/reactor-builder.js.map +0 -1
  40. package/dist/src/core/reactor-client-builder.js +0 -132
  41. package/dist/src/core/reactor-client-builder.js.map +0 -1
  42. package/dist/src/core/reactor.js +0 -640
  43. package/dist/src/core/reactor.js.map +0 -1
  44. package/dist/src/core/types.js +0 -2
  45. package/dist/src/core/types.js.map +0 -1
  46. package/dist/src/core/utils.js +0 -225
  47. package/dist/src/core/utils.js.map +0 -1
  48. package/dist/src/events/event-bus.js +0 -53
  49. package/dist/src/events/event-bus.js.map +0 -1
  50. package/dist/src/events/interfaces.js +0 -2
  51. package/dist/src/events/interfaces.js.map +0 -1
  52. package/dist/src/events/types.js +0 -30
  53. package/dist/src/events/types.js.map +0 -1
  54. package/dist/src/executor/document-action-handler.js +0 -356
  55. package/dist/src/executor/document-action-handler.js.map +0 -1
  56. package/dist/src/executor/interfaces.js +0 -2
  57. package/dist/src/executor/interfaces.js.map +0 -1
  58. package/dist/src/executor/signature-verifier.js +0 -70
  59. package/dist/src/executor/signature-verifier.js.map +0 -1
  60. package/dist/src/executor/simple-job-executor-manager.js +0 -345
  61. package/dist/src/executor/simple-job-executor-manager.js.map +0 -1
  62. package/dist/src/executor/simple-job-executor.js +0 -423
  63. package/dist/src/executor/simple-job-executor.js.map +0 -1
  64. package/dist/src/executor/types.js +0 -11
  65. package/dist/src/executor/types.js.map +0 -1
  66. package/dist/src/executor/util.js +0 -230
  67. package/dist/src/executor/util.js.map +0 -1
  68. package/dist/src/index.js.map +0 -1
  69. package/dist/src/job-tracker/in-memory-job-tracker.js +0 -114
  70. package/dist/src/job-tracker/in-memory-job-tracker.js.map +0 -1
  71. package/dist/src/job-tracker/index.js +0 -2
  72. package/dist/src/job-tracker/index.js.map +0 -1
  73. package/dist/src/job-tracker/interfaces.js +0 -2
  74. package/dist/src/job-tracker/interfaces.js.map +0 -1
  75. package/dist/src/logging/console.js +0 -2
  76. package/dist/src/logging/console.js.map +0 -1
  77. package/dist/src/logging/types.js +0 -2
  78. package/dist/src/logging/types.js.map +0 -1
  79. package/dist/src/processors/index.js +0 -2
  80. package/dist/src/processors/index.js.map +0 -1
  81. package/dist/src/processors/processor-manager.js +0 -165
  82. package/dist/src/processors/processor-manager.js.map +0 -1
  83. package/dist/src/processors/relational/types.js +0 -2
  84. package/dist/src/processors/relational/types.js.map +0 -1
  85. package/dist/src/processors/relational/utils.js +0 -2
  86. package/dist/src/processors/relational/utils.js.map +0 -1
  87. package/dist/src/processors/utils.js +0 -59
  88. package/dist/src/processors/utils.js.map +0 -1
  89. package/dist/src/queue/interfaces.js +0 -2
  90. package/dist/src/queue/interfaces.js.map +0 -1
  91. package/dist/src/queue/job-execution-handle.js +0 -71
  92. package/dist/src/queue/job-execution-handle.js.map +0 -1
  93. package/dist/src/queue/queue.js +0 -493
  94. package/dist/src/queue/queue.js.map +0 -1
  95. package/dist/src/queue/types.js +0 -19
  96. package/dist/src/queue/types.js.map +0 -1
  97. package/dist/src/read-models/base-read-model.js +0 -143
  98. package/dist/src/read-models/base-read-model.js.map +0 -1
  99. package/dist/src/read-models/coordinator.js +0 -72
  100. package/dist/src/read-models/coordinator.js.map +0 -1
  101. package/dist/src/read-models/document-view.js +0 -457
  102. package/dist/src/read-models/document-view.js.map +0 -1
  103. package/dist/src/read-models/interfaces.js +0 -2
  104. package/dist/src/read-models/interfaces.js.map +0 -1
  105. package/dist/src/read-models/types.js +0 -2
  106. package/dist/src/read-models/types.js.map +0 -1
  107. package/dist/src/registry/document-model-resolver.js +0 -81
  108. package/dist/src/registry/document-model-resolver.js.map +0 -1
  109. package/dist/src/registry/implementation.js +0 -226
  110. package/dist/src/registry/implementation.js.map +0 -1
  111. package/dist/src/registry/index.js +0 -3
  112. package/dist/src/registry/index.js.map +0 -1
  113. package/dist/src/registry/interfaces.js +0 -2
  114. package/dist/src/registry/interfaces.js.map +0 -1
  115. package/dist/src/shared/awaiter.js +0 -123
  116. package/dist/src/shared/awaiter.js.map +0 -1
  117. package/dist/src/shared/collect-all-pages.js +0 -17
  118. package/dist/src/shared/collect-all-pages.js.map +0 -1
  119. package/dist/src/shared/consistency-tracker.js +0 -123
  120. package/dist/src/shared/consistency-tracker.js.map +0 -1
  121. package/dist/src/shared/drive-url.js +0 -17
  122. package/dist/src/shared/drive-url.js.map +0 -1
  123. package/dist/src/shared/errors.js +0 -93
  124. package/dist/src/shared/errors.js.map +0 -1
  125. package/dist/src/shared/factories.js +0 -41
  126. package/dist/src/shared/factories.js.map +0 -1
  127. package/dist/src/shared/types.js +0 -38
  128. package/dist/src/shared/types.js.map +0 -1
  129. package/dist/src/shared/utils.js +0 -8
  130. package/dist/src/shared/utils.js.map +0 -1
  131. package/dist/src/signer/passthrough-signer.js +0 -17
  132. package/dist/src/signer/passthrough-signer.js.map +0 -1
  133. package/dist/src/signer/types.js +0 -2
  134. package/dist/src/signer/types.js.map +0 -1
  135. package/dist/src/storage/index.js +0 -3
  136. package/dist/src/storage/index.js.map +0 -1
  137. package/dist/src/storage/interfaces.js +0 -29
  138. package/dist/src/storage/interfaces.js.map +0 -1
  139. package/dist/src/storage/kysely/document-indexer.js +0 -421
  140. package/dist/src/storage/kysely/document-indexer.js.map +0 -1
  141. package/dist/src/storage/kysely/keyframe-store.js +0 -64
  142. package/dist/src/storage/kysely/keyframe-store.js.map +0 -1
  143. package/dist/src/storage/kysely/store.js +0 -264
  144. package/dist/src/storage/kysely/store.js.map +0 -1
  145. package/dist/src/storage/kysely/sync-cursor-storage.js +0 -97
  146. package/dist/src/storage/kysely/sync-cursor-storage.js.map +0 -1
  147. package/dist/src/storage/kysely/sync-dead-letter-storage.js +0 -110
  148. package/dist/src/storage/kysely/sync-dead-letter-storage.js.map +0 -1
  149. package/dist/src/storage/kysely/sync-remote-storage.js +0 -133
  150. package/dist/src/storage/kysely/sync-remote-storage.js.map +0 -1
  151. package/dist/src/storage/kysely/types.js +0 -2
  152. package/dist/src/storage/kysely/types.js.map +0 -1
  153. package/dist/src/storage/migrations/001_create_operation_table.js +0 -41
  154. package/dist/src/storage/migrations/001_create_operation_table.js.map +0 -1
  155. package/dist/src/storage/migrations/002_create_keyframe_table.js +0 -27
  156. package/dist/src/storage/migrations/002_create_keyframe_table.js.map +0 -1
  157. package/dist/src/storage/migrations/003_create_document_table.js +0 -10
  158. package/dist/src/storage/migrations/003_create_document_table.js.map +0 -1
  159. package/dist/src/storage/migrations/004_create_document_relationship_table.js +0 -35
  160. package/dist/src/storage/migrations/004_create_document_relationship_table.js.map +0 -1
  161. package/dist/src/storage/migrations/005_create_indexer_state_table.js +0 -10
  162. package/dist/src/storage/migrations/005_create_indexer_state_table.js.map +0 -1
  163. package/dist/src/storage/migrations/006_create_document_snapshot_table.js +0 -49
  164. package/dist/src/storage/migrations/006_create_document_snapshot_table.js.map +0 -1
  165. package/dist/src/storage/migrations/007_create_slug_mapping_table.js +0 -24
  166. package/dist/src/storage/migrations/007_create_slug_mapping_table.js.map +0 -1
  167. package/dist/src/storage/migrations/008_create_view_state_table.js +0 -10
  168. package/dist/src/storage/migrations/008_create_view_state_table.js.map +0 -1
  169. package/dist/src/storage/migrations/009_create_operation_index_tables.js +0 -50
  170. package/dist/src/storage/migrations/009_create_operation_index_tables.js.map +0 -1
  171. package/dist/src/storage/migrations/010_create_sync_tables.js +0 -43
  172. package/dist/src/storage/migrations/010_create_sync_tables.js.map +0 -1
  173. package/dist/src/storage/migrations/011_add_cursor_type_column.js +0 -29
  174. package/dist/src/storage/migrations/011_add_cursor_type_column.js.map +0 -1
  175. package/dist/src/storage/migrations/012_add_source_remote_column.js +0 -7
  176. package/dist/src/storage/migrations/012_add_source_remote_column.js.map +0 -1
  177. package/dist/src/storage/migrations/013_create_sync_dead_letters_table.js +0 -24
  178. package/dist/src/storage/migrations/013_create_sync_dead_letters_table.js.map +0 -1
  179. package/dist/src/storage/migrations/index.js +0 -3
  180. package/dist/src/storage/migrations/index.js.map +0 -1
  181. package/dist/src/storage/migrations/migrator.js +0 -84
  182. package/dist/src/storage/migrations/migrator.js.map +0 -1
  183. package/dist/src/storage/migrations/run-migrations.js +0 -58
  184. package/dist/src/storage/migrations/run-migrations.js.map +0 -1
  185. package/dist/src/storage/migrations/types.js +0 -2
  186. package/dist/src/storage/migrations/types.js.map +0 -1
  187. package/dist/src/storage/txn.js +0 -42
  188. package/dist/src/storage/txn.js.map +0 -1
  189. package/dist/src/subs/default-error-handler.js +0 -27
  190. package/dist/src/subs/default-error-handler.js.map +0 -1
  191. package/dist/src/subs/react-subscription-manager.js +0 -185
  192. package/dist/src/subs/react-subscription-manager.js.map +0 -1
  193. package/dist/src/subs/subscription-notification-read-model.js +0 -62
  194. package/dist/src/subs/subscription-notification-read-model.js.map +0 -1
  195. package/dist/src/subs/types.js +0 -2
  196. package/dist/src/subs/types.js.map +0 -1
  197. package/dist/src/sync/batch-aggregator.js +0 -94
  198. package/dist/src/sync/batch-aggregator.js.map +0 -1
  199. package/dist/src/sync/buffered-mailbox.js +0 -164
  200. package/dist/src/sync/buffered-mailbox.js.map +0 -1
  201. package/dist/src/sync/channels/gql-req-channel.js +0 -548
  202. package/dist/src/sync/channels/gql-req-channel.js.map +0 -1
  203. package/dist/src/sync/channels/gql-request-channel-factory.js +0 -105
  204. package/dist/src/sync/channels/gql-request-channel-factory.js.map +0 -1
  205. package/dist/src/sync/channels/gql-res-channel.js +0 -79
  206. package/dist/src/sync/channels/gql-res-channel.js.map +0 -1
  207. package/dist/src/sync/channels/gql-response-channel-factory.js +0 -14
  208. package/dist/src/sync/channels/gql-response-channel-factory.js.map +0 -1
  209. package/dist/src/sync/channels/index.js +0 -8
  210. package/dist/src/sync/channels/index.js.map +0 -1
  211. package/dist/src/sync/channels/interval-poll-timer.js +0 -123
  212. package/dist/src/sync/channels/interval-poll-timer.js.map +0 -1
  213. package/dist/src/sync/channels/poll-timer.js +0 -2
  214. package/dist/src/sync/channels/poll-timer.js.map +0 -1
  215. package/dist/src/sync/channels/utils.js +0 -161
  216. package/dist/src/sync/channels/utils.js.map +0 -1
  217. package/dist/src/sync/errors.js +0 -17
  218. package/dist/src/sync/errors.js.map +0 -1
  219. package/dist/src/sync/index.js +0 -11
  220. package/dist/src/sync/index.js.map +0 -1
  221. package/dist/src/sync/interfaces.js +0 -2
  222. package/dist/src/sync/interfaces.js.map +0 -1
  223. package/dist/src/sync/mailbox.js +0 -142
  224. package/dist/src/sync/mailbox.js.map +0 -1
  225. package/dist/src/sync/sync-awaiter.js +0 -124
  226. package/dist/src/sync/sync-awaiter.js.map +0 -1
  227. package/dist/src/sync/sync-builder.js +0 -52
  228. package/dist/src/sync/sync-builder.js.map +0 -1
  229. package/dist/src/sync/sync-manager.js +0 -447
  230. package/dist/src/sync/sync-manager.js.map +0 -1
  231. package/dist/src/sync/sync-operation.js +0 -70
  232. package/dist/src/sync/sync-operation.js.map +0 -1
  233. package/dist/src/sync/sync-status-tracker.js +0 -137
  234. package/dist/src/sync/sync-status-tracker.js.map +0 -1
  235. package/dist/src/sync/types.js +0 -31
  236. package/dist/src/sync/types.js.map +0 -1
  237. package/dist/src/sync/utils.js +0 -283
  238. package/dist/src/sync/utils.js.map +0 -1
  239. package/dist/src/utils/reshuffle.js +0 -91
  240. package/dist/src/utils/reshuffle.js.map +0 -1
@@ -1,124 +0,0 @@
1
- import { SyncEventTypes, } from "./types.js";
2
- /**
3
- * Provides a promise-based interface for waiting on sync completion.
4
- * Subscribes to sync events at construction and tracks completed sync results
5
- * to provide a fast path for jobs that have already synced.
6
- */
7
- export class SyncAwaiter {
8
- eventBus;
9
- completedResults = new Map();
10
- pendingWaiters = new Map();
11
- unsubscribers = [];
12
- isShutdown = false;
13
- constructor(eventBus) {
14
- this.eventBus = eventBus;
15
- this.subscribeToEvents();
16
- }
17
- /**
18
- * Waits for sync operations for a job to complete.
19
- * Resolves when SYNC_SUCCEEDED is emitted.
20
- * Rejects when SYNC_FAILED is emitted.
21
- *
22
- * @param jobId - The job id to wait for
23
- * @param signal - Optional abort signal
24
- * @returns The sync result
25
- */
26
- waitForSync(jobId, signal) {
27
- if (signal?.aborted) {
28
- return Promise.reject(new Error("Operation aborted"));
29
- }
30
- if (this.isShutdown) {
31
- return Promise.reject(new Error("SyncAwaiter is shutdown"));
32
- }
33
- const completedResult = this.completedResults.get(jobId);
34
- if (completedResult) {
35
- return Promise.resolve(completedResult);
36
- }
37
- return new Promise((resolve, reject) => {
38
- const waiter = { resolve, reject, signal };
39
- const existingWaiters = this.pendingWaiters.get(jobId) || [];
40
- existingWaiters.push(waiter);
41
- this.pendingWaiters.set(jobId, existingWaiters);
42
- if (signal) {
43
- const abortHandler = () => {
44
- const waiters = this.pendingWaiters.get(jobId);
45
- if (waiters) {
46
- const index = waiters.indexOf(waiter);
47
- if (index !== -1) {
48
- waiters.splice(index, 1);
49
- if (waiters.length === 0) {
50
- this.pendingWaiters.delete(jobId);
51
- }
52
- waiter.reject(new Error("Operation aborted"));
53
- }
54
- }
55
- };
56
- signal.addEventListener("abort", abortHandler, { once: true });
57
- }
58
- });
59
- }
60
- /**
61
- * Shuts down the sync awaiter. This will synchronously reject all pending waiters.
62
- */
63
- shutdown() {
64
- this.isShutdown = true;
65
- for (const unsubscribe of this.unsubscribers) {
66
- unsubscribe();
67
- }
68
- this.unsubscribers.length = 0;
69
- for (const [, waiters] of this.pendingWaiters) {
70
- for (const waiter of waiters) {
71
- waiter.reject(new Error("SyncAwaiter shutdown"));
72
- }
73
- }
74
- this.pendingWaiters.clear();
75
- }
76
- subscribeToEvents() {
77
- this.unsubscribers.push(this.eventBus.subscribe(SyncEventTypes.SYNC_SUCCEEDED, (_type, event) => {
78
- this.handleSyncSucceeded(event);
79
- }));
80
- this.unsubscribers.push(this.eventBus.subscribe(SyncEventTypes.SYNC_FAILED, (_type, event) => {
81
- this.handleSyncFailed(event);
82
- }));
83
- }
84
- handleSyncSucceeded(event) {
85
- const result = {
86
- jobId: event.jobId,
87
- status: "succeeded",
88
- syncOperationCount: event.syncOperationCount,
89
- successCount: event.syncOperationCount,
90
- failureCount: 0,
91
- errors: [],
92
- };
93
- this.completedResults.set(event.jobId, result);
94
- this.resolveWaiters(event.jobId, result);
95
- }
96
- handleSyncFailed(event) {
97
- const result = {
98
- jobId: event.jobId,
99
- status: "failed",
100
- syncOperationCount: event.successCount + event.failureCount,
101
- successCount: event.successCount,
102
- failureCount: event.failureCount,
103
- errors: event.errors,
104
- };
105
- this.completedResults.set(event.jobId, result);
106
- this.resolveWaiters(event.jobId, result);
107
- }
108
- resolveWaiters(jobId, result) {
109
- const waiters = this.pendingWaiters.get(jobId);
110
- if (!waiters || waiters.length === 0) {
111
- return;
112
- }
113
- this.pendingWaiters.delete(jobId);
114
- for (const waiter of waiters) {
115
- if (waiter.signal?.aborted) {
116
- waiter.reject(new Error("Operation aborted"));
117
- }
118
- else {
119
- waiter.resolve(result);
120
- }
121
- }
122
- }
123
- }
124
- //# sourceMappingURL=sync-awaiter.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sync-awaiter.js","sourceRoot":"","sources":["../../../src/sync/sync-awaiter.ts"],"names":[],"mappings":"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"}
@@ -1,52 +0,0 @@
1
- import { KyselySyncCursorStorage } from "../storage/kysely/sync-cursor-storage.js";
2
- import { KyselySyncDeadLetterStorage } from "../storage/kysely/sync-dead-letter-storage.js";
3
- import { KyselySyncRemoteStorage } from "../storage/kysely/sync-remote-storage.js";
4
- import { SyncManager } from "./sync-manager.js";
5
- export class SyncBuilder {
6
- channelFactory;
7
- remoteStorage;
8
- cursorStorage;
9
- deadLetterStorage;
10
- maxDeadLettersPerRemote = 100;
11
- withChannelFactory(factory) {
12
- this.channelFactory = factory;
13
- return this;
14
- }
15
- withRemoteStorage(storage) {
16
- this.remoteStorage = storage;
17
- return this;
18
- }
19
- withCursorStorage(storage) {
20
- this.cursorStorage = storage;
21
- return this;
22
- }
23
- withDeadLetterStorage(storage) {
24
- this.deadLetterStorage = storage;
25
- return this;
26
- }
27
- withMaxDeadLettersPerRemote(limit) {
28
- this.maxDeadLettersPerRemote = limit;
29
- return this;
30
- }
31
- build(reactor, logger, operationIndex, eventBus, db) {
32
- const module = this.buildModule(reactor, logger, operationIndex, eventBus, db);
33
- return module.syncManager;
34
- }
35
- buildModule(reactor, logger, operationIndex, eventBus, db) {
36
- if (!this.channelFactory) {
37
- throw new Error("Channel factory is required");
38
- }
39
- const remoteStorage = this.remoteStorage ?? new KyselySyncRemoteStorage(db);
40
- const cursorStorage = this.cursorStorage ?? new KyselySyncCursorStorage(db);
41
- const deadLetterStorage = this.deadLetterStorage ?? new KyselySyncDeadLetterStorage(db);
42
- const syncManager = new SyncManager(logger, remoteStorage, cursorStorage, deadLetterStorage, this.channelFactory, operationIndex, reactor, eventBus, this.maxDeadLettersPerRemote);
43
- return {
44
- remoteStorage,
45
- cursorStorage,
46
- deadLetterStorage,
47
- channelFactory: this.channelFactory,
48
- syncManager,
49
- };
50
- }
51
- }
52
- //# sourceMappingURL=sync-builder.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sync-builder.js","sourceRoot":"","sources":["../../../src/sync/sync-builder.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AACnF,OAAO,EAAE,2BAA2B,EAAE,MAAM,+CAA+C,CAAC;AAC5F,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AAGnF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,OAAO,WAAW;IACd,cAAc,CAAmB;IACjC,aAAa,CAAsB;IACnC,aAAa,CAAsB;IACnC,iBAAiB,CAA0B;IAC3C,uBAAuB,GAAW,GAAG,CAAC;IAE9C,kBAAkB,CAAC,OAAwB;QACzC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,iBAAiB,CAAC,OAA2B;QAC3C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,iBAAiB,CAAC,OAA2B;QAC3C,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,qBAAqB,CAAC,OAA+B;QACnD,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,2BAA2B,CAAC,KAAa;QACvC,IAAI,CAAC,uBAAuB,GAAG,KAAK,CAAC;QACrC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CACH,OAAiB,EACjB,MAAe,EACf,cAA+B,EAC/B,QAAmB,EACnB,EAAoB;QAEpB,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAC7B,OAAO,EACP,MAAM,EACN,cAAc,EACd,QAAQ,EACR,EAAE,CACH,CAAC;QACF,OAAO,MAAM,CAAC,WAAW,CAAC;IAC5B,CAAC;IAED,WAAW,CACT,OAAiB,EACjB,MAAe,EACf,cAA+B,EAC/B,QAAmB,EACnB,EAAoB;QAEpB,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,uBAAuB,CAAC,EAAE,CAAC,CAAC;QAC5E,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,uBAAuB,CAAC,EAAE,CAAC,CAAC;QAC5E,MAAM,iBAAiB,GACrB,IAAI,CAAC,iBAAiB,IAAI,IAAI,2BAA2B,CAAC,EAAE,CAAC,CAAC;QAEhE,MAAM,WAAW,GAAG,IAAI,WAAW,CACjC,MAAM,EACN,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,IAAI,CAAC,cAAc,EACnB,cAAc,EACd,OAAO,EACP,QAAQ,EACR,IAAI,CAAC,uBAAuB,CAC7B,CAAC;QAEF,OAAO;YACL,aAAa;YACb,aAAa;YACb,iBAAiB;YACjB,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,WAAW;SACZ,CAAC;IACJ,CAAC;CACF"}
@@ -1,447 +0,0 @@
1
- import { ReactorEventTypes, } from "../events/types.js";
2
- import { JobAwaiter } from "../shared/awaiter.js";
3
- import { JobStatus, } from "../shared/types.js";
4
- import { BatchAggregator } from "./batch-aggregator.js";
5
- import { ChannelError } from "./errors.js";
6
- import { SyncAwaiter } from "./sync-awaiter.js";
7
- import { SyncOperation } from "./sync-operation.js";
8
- import { SyncStatusTracker, } from "./sync-status-tracker.js";
9
- import { ChannelErrorSource } from "./types.js";
10
- import { batchOperationsByDocument, createIdleHealth, filterOperations, toOperationWithContext, trimMailboxFromBatch, } from "./utils.js";
11
- export class SyncManager {
12
- logger;
13
- remoteStorage;
14
- cursorStorage;
15
- deadLetterStorage;
16
- channelFactory;
17
- operationIndex;
18
- reactor;
19
- eventBus;
20
- remotes;
21
- awaiter;
22
- syncAwaiter;
23
- isShutdown;
24
- eventUnsubscribe;
25
- failedEventUnsubscribe;
26
- batchAggregator;
27
- syncStatusTracker;
28
- maxDeadLettersPerRemote;
29
- loadJobs = new Map();
30
- constructor(logger, remoteStorage, cursorStorage, deadLetterStorage, channelFactory, operationIndex, reactor, eventBus, maxDeadLettersPerRemote = 100) {
31
- this.logger = logger;
32
- this.remoteStorage = remoteStorage;
33
- this.cursorStorage = cursorStorage;
34
- this.deadLetterStorage = deadLetterStorage;
35
- this.channelFactory = channelFactory;
36
- this.operationIndex = operationIndex;
37
- this.reactor = reactor;
38
- this.eventBus = eventBus;
39
- this.maxDeadLettersPerRemote = maxDeadLettersPerRemote;
40
- this.remotes = new Map();
41
- this.awaiter = new JobAwaiter(eventBus, (jobId, signal) => reactor.getJobStatus(jobId, signal));
42
- this.syncAwaiter = new SyncAwaiter(eventBus);
43
- this.isShutdown = false;
44
- this.batchAggregator = new BatchAggregator(logger, (batch) => this.processCompleteBatch(batch));
45
- this.syncStatusTracker = new SyncStatusTracker();
46
- }
47
- async startup() {
48
- if (this.isShutdown) {
49
- throw new Error("SyncManager is already shutdown and cannot be started");
50
- }
51
- const remoteRecords = await this.remoteStorage.list();
52
- for (const record of remoteRecords) {
53
- const channel = this.channelFactory.instance(record.id, record.name, record.channelConfig, this.cursorStorage, record.collectionId, record.filter, this.operationIndex);
54
- const remote = {
55
- id: record.id,
56
- name: record.name,
57
- collectionId: record.collectionId,
58
- filter: record.filter,
59
- options: record.options,
60
- channel,
61
- };
62
- this.remotes.set(record.name, remote);
63
- await this.loadDeadLetters(remote);
64
- this.wireChannelCallbacks(remote);
65
- try {
66
- await channel.init();
67
- }
68
- catch (error) {
69
- this.logger.error("Error initializing channel for remote (@name, @error)", record.name, error instanceof Error ? error.message : String(error));
70
- this.remotes.delete(record.name);
71
- continue;
72
- }
73
- // backfill channels
74
- const outboxAckOrdinal = remote.channel.outbox.ackOrdinal;
75
- if (outboxAckOrdinal > 0) {
76
- await this.updateOutbox(remote, outboxAckOrdinal);
77
- }
78
- }
79
- this.eventUnsubscribe = this.eventBus.subscribe(ReactorEventTypes.JOB_WRITE_READY, async (_type, event) => this.batchAggregator.enqueueWriteReady(event));
80
- this.failedEventUnsubscribe = this.eventBus.subscribe(ReactorEventTypes.JOB_FAILED, async (_type, event) => this.batchAggregator.handleJobFailed(event));
81
- }
82
- shutdown() {
83
- this.isShutdown = true;
84
- this.batchAggregator.clear();
85
- if (this.eventUnsubscribe) {
86
- this.eventUnsubscribe();
87
- this.eventUnsubscribe = undefined;
88
- }
89
- if (this.failedEventUnsubscribe) {
90
- this.failedEventUnsubscribe();
91
- this.failedEventUnsubscribe = undefined;
92
- }
93
- this.awaiter.shutdown();
94
- this.syncAwaiter.shutdown();
95
- this.syncStatusTracker.clear();
96
- const promises = [];
97
- for (const remote of this.remotes.values()) {
98
- promises.push(remote.channel.shutdown());
99
- }
100
- this.remotes.clear();
101
- return {
102
- isShutdown: true,
103
- completed: Promise.all(promises).then(() => undefined),
104
- };
105
- }
106
- getByName(name) {
107
- const remote = this.remotes.get(name);
108
- if (!remote) {
109
- throw new Error(`Remote with name '${name}' does not exist`);
110
- }
111
- return remote;
112
- }
113
- getById(id) {
114
- for (const remote of this.remotes.values()) {
115
- if (remote.id === id) {
116
- return remote;
117
- }
118
- }
119
- throw new Error(`Remote with id '${id}' does not exist`);
120
- }
121
- async add(name, collectionId, channelConfig, filter = { documentId: [], scope: [], branch: "" }, options = { sinceTimestampUtcMs: "0" }, id) {
122
- if (this.isShutdown) {
123
- throw new Error("SyncManager is shutdown and cannot add remotes");
124
- }
125
- if (this.remotes.has(name)) {
126
- throw new Error(`Remote with name '${name}' already exists`);
127
- }
128
- this.logger.debug("Adding remote (@name, @collectionId, @channelConfig, @filter, @options, @id)", name, collectionId, channelConfig, filter, options, id);
129
- const remoteId = id ?? crypto.randomUUID();
130
- const status = {
131
- push: createIdleHealth(),
132
- pull: createIdleHealth(),
133
- };
134
- const remoteRecord = {
135
- id: remoteId,
136
- name,
137
- collectionId,
138
- channelConfig,
139
- filter,
140
- options,
141
- status,
142
- };
143
- await this.remoteStorage.upsert(remoteRecord);
144
- const channel = this.channelFactory.instance(remoteId, name, channelConfig, this.cursorStorage, collectionId, filter, this.operationIndex);
145
- const remote = {
146
- id: remoteId,
147
- name,
148
- collectionId,
149
- filter,
150
- options,
151
- channel,
152
- };
153
- this.remotes.set(name, remote);
154
- await this.loadDeadLetters(remote);
155
- this.wireChannelCallbacks(remote);
156
- try {
157
- await channel.init();
158
- }
159
- catch (error) {
160
- this.remotes.delete(name);
161
- await this.remoteStorage.remove(name);
162
- throw error;
163
- }
164
- // backfill
165
- await this.updateOutbox(remote, 0);
166
- return remote;
167
- }
168
- async remove(name) {
169
- const remote = this.remotes.get(name);
170
- if (!remote) {
171
- throw new Error(`Remote with name '${name}' does not exist`);
172
- }
173
- // shutdown the channel
174
- await remote.channel.shutdown();
175
- // delete the remote's data
176
- await this.remoteStorage.remove(name);
177
- await this.cursorStorage.remove(name);
178
- this.syncStatusTracker.untrackRemote(name);
179
- this.remotes.delete(name);
180
- }
181
- list() {
182
- return Array.from(this.remotes.values());
183
- }
184
- waitForSync(jobId, signal) {
185
- return this.syncAwaiter.waitForSync(jobId, signal);
186
- }
187
- getSyncStatus(documentId) {
188
- return this.syncStatusTracker.getStatus(documentId);
189
- }
190
- onSyncStatusChange(callback) {
191
- return this.syncStatusTracker.onChange(callback);
192
- }
193
- wireChannelCallbacks(remote) {
194
- remote.channel.inbox.onAdded((syncOps) => this.handleInboxAdded(remote, syncOps));
195
- this.syncStatusTracker.trackRemote(remote.name, remote.channel);
196
- remote.channel.deadLetter.onAdded((syncOps) => {
197
- for (const syncOp of syncOps) {
198
- this.logger.error("Dead letter (@remote, @documentId, @jobId, @error, @dependencies)", remote.name, syncOp.documentId, syncOp.jobId, syncOp.error?.message ?? "unknown", syncOp.jobDependencies);
199
- const record = {
200
- id: syncOp.id,
201
- jobId: syncOp.jobId,
202
- jobDependencies: syncOp.jobDependencies,
203
- remoteName: syncOp.remoteName,
204
- documentId: syncOp.documentId,
205
- scopes: syncOp.scopes,
206
- branch: syncOp.branch,
207
- operations: syncOp.operations,
208
- errorSource: syncOp.error?.source ?? ChannelErrorSource.None,
209
- errorMessage: syncOp.error?.error.message ?? "unknown",
210
- };
211
- void this.deadLetterStorage.add(record).catch((err) => {
212
- this.logger.error("Failed to persist dead letter (@id, @error)", record.id, err instanceof Error ? err.message : String(err));
213
- });
214
- }
215
- // Evict oldest dead letters from mailbox if over capacity
216
- const items = remote.channel.deadLetter.items;
217
- if (items.length > this.maxDeadLettersPerRemote) {
218
- const excessCount = items.length - this.maxDeadLettersPerRemote;
219
- const toEvict = items.slice(0, excessCount);
220
- remote.channel.deadLetter.remove(...toEvict);
221
- }
222
- });
223
- }
224
- async loadDeadLetters(remote) {
225
- let records;
226
- try {
227
- const page = await this.deadLetterStorage.list(remote.name, {
228
- cursor: "0",
229
- limit: this.maxDeadLettersPerRemote,
230
- });
231
- records = page.results;
232
- }
233
- catch (error) {
234
- this.logger.error("Failed to load dead letters for remote (@name, @error)", remote.name, error instanceof Error ? error.message : String(error));
235
- return;
236
- }
237
- if (records.length === 0) {
238
- return;
239
- }
240
- // Records come in ordinal DESC order (newest first).
241
- // Reverse so the Map maintains chronological insertion order (oldest first),
242
- // which makes eviction (slice from the front) straightforward.
243
- records.reverse();
244
- const syncOps = [];
245
- for (const record of records) {
246
- const syncOp = new SyncOperation(record.id, record.jobId, record.jobDependencies, record.remoteName, record.documentId, record.scopes, record.branch, record.operations);
247
- syncOp.failed(new ChannelError(record.errorSource, new Error(record.errorMessage)));
248
- syncOps.push(syncOp);
249
- }
250
- remote.channel.deadLetter.add(...syncOps);
251
- this.logger.debug("Loaded @count persisted dead letters for remote @name", records.length, remote.name);
252
- }
253
- getRemotesForCollection(collectionId) {
254
- return Array.from(this.remotes.values()).filter((remote) => remote.collectionId === collectionId);
255
- }
256
- async processCompleteBatch(batch) {
257
- // get the unique set of collection ids
258
- const collectionIds = [
259
- ...new Set(Object.values(batch.collectionMemberships).flatMap((collections) => collections)),
260
- ];
261
- // get the unique set of affected remotes
262
- const affectedRemotes = [];
263
- for (const collectionId of collectionIds) {
264
- const remotes = this.getRemotesForCollection(collectionId);
265
- for (const remote of remotes) {
266
- if (!affectedRemotes.includes(remote)) {
267
- affectedRemotes.push(remote);
268
- }
269
- }
270
- }
271
- // ack matching inbox items
272
- for (const remote of affectedRemotes) {
273
- trimMailboxFromBatch(remote.channel.inbox, batch);
274
- }
275
- // finally, work through the affected remotes and backfill based on the last operation in the outbox
276
- for (const remote of affectedRemotes) {
277
- await this.updateOutbox(remote, remote.channel.outbox.latestOrdinal);
278
- }
279
- }
280
- handleInboxAdded(remote, syncOps) {
281
- if (this.isShutdown) {
282
- return;
283
- }
284
- const keyed = [];
285
- const nonKeyed = [];
286
- for (const syncOp of syncOps) {
287
- if (syncOp.jobId) {
288
- keyed.push(syncOp);
289
- }
290
- else {
291
- nonKeyed.push(syncOp);
292
- }
293
- }
294
- for (const syncOp of nonKeyed) {
295
- void this.applyInboxJob(remote, syncOp);
296
- }
297
- if (keyed.length > 0) {
298
- void this.applyInboxBatch(keyed.map((syncOp) => ({ remote, syncOp })));
299
- }
300
- }
301
- async applyInboxJob(remote, syncOp) {
302
- const operations = syncOp.operations.map((op) => op.operation);
303
- let jobInfo;
304
- try {
305
- jobInfo = await this.reactor.load(syncOp.documentId, syncOp.branch, operations, undefined, { sourceRemote: remote.name });
306
- }
307
- catch (error) {
308
- const err = error instanceof Error ? error : new Error(String(error));
309
- this.logger.error("Failed to load operations from inbox (@remote, @documentId, @error)", remote.name, syncOp.documentId, err.message);
310
- const channelError = new ChannelError(ChannelErrorSource.Inbox, err);
311
- syncOp.failed(channelError);
312
- remote.channel.deadLetter.add(syncOp);
313
- remote.channel.inbox.remove(syncOp);
314
- return;
315
- }
316
- let completedJobInfo;
317
- try {
318
- completedJobInfo = await this.awaiter.waitForJob(jobInfo.id);
319
- }
320
- catch (error) {
321
- const err = error instanceof Error ? error : new Error(String(error));
322
- this.logger.error("Failed to wait for job completion (@remote, @documentId, @jobId, @error)", remote.name, syncOp.documentId, jobInfo.id, err.message);
323
- const channelError = new ChannelError(ChannelErrorSource.Inbox, err);
324
- syncOp.failed(channelError);
325
- remote.channel.deadLetter.add(syncOp);
326
- remote.channel.inbox.remove(syncOp);
327
- return;
328
- }
329
- const jobKey = `${syncOp.documentId}:${syncOp.branch}`;
330
- this.loadJobs.set(jobKey, completedJobInfo);
331
- if (completedJobInfo.status === JobStatus.FAILED) {
332
- const errorMessage = completedJobInfo.error?.message || "Unknown error";
333
- this.logger.error("Failed to apply operations from inbox (@remote, @documentId, @jobId, @error)", remote.name, syncOp.documentId, completedJobInfo.id, errorMessage);
334
- const error = new ChannelError(ChannelErrorSource.Inbox, new Error(`Failed to apply operations: ${errorMessage}`));
335
- syncOp.failed(error);
336
- remote.channel.deadLetter.add(syncOp);
337
- }
338
- else {
339
- syncOp.executed();
340
- }
341
- remote.channel.inbox.remove(syncOp);
342
- }
343
- async applyInboxBatch(items) {
344
- const sourceRemote = items[0].remote.name;
345
- const jobs = items.map(({ syncOp }) => ({
346
- key: syncOp.jobId,
347
- documentId: syncOp.documentId,
348
- scope: syncOp.scopes[0],
349
- branch: syncOp.branch,
350
- operations: syncOp.operations.map((op) => op.operation),
351
- dependsOn: syncOp.jobDependencies.filter(Boolean),
352
- }));
353
- const request = { jobs };
354
- let result;
355
- try {
356
- result = await this.reactor.loadBatch(request, undefined, {
357
- sourceRemote,
358
- });
359
- }
360
- catch (error) {
361
- for (const { remote, syncOp } of items) {
362
- const err = error instanceof Error ? error : new Error(String(error));
363
- syncOp.failed(new ChannelError(ChannelErrorSource.Inbox, err));
364
- remote.channel.deadLetter.add(syncOp);
365
- remote.channel.inbox.remove(syncOp);
366
- }
367
- return;
368
- }
369
- for (const { remote, syncOp } of items) {
370
- if (!(syncOp.jobId in result.jobs)) {
371
- this.logger.error("Job key missing from batch load result (@remote, @documentId, @jobId)", remote.name, syncOp.documentId, syncOp.jobId);
372
- const error = new ChannelError(ChannelErrorSource.Inbox, new Error(`Job key '${syncOp.jobId}' missing from batch load result`));
373
- syncOp.failed(error);
374
- remote.channel.deadLetter.add(syncOp);
375
- remote.channel.inbox.remove(syncOp);
376
- continue;
377
- }
378
- const jobInfo = result.jobs[syncOp.jobId];
379
- let completedJobInfo;
380
- try {
381
- completedJobInfo = await this.awaiter.waitForJob(jobInfo.id);
382
- }
383
- catch (error) {
384
- const err = error instanceof Error ? error : new Error(String(error));
385
- syncOp.failed(new ChannelError(ChannelErrorSource.Inbox, err));
386
- remote.channel.deadLetter.add(syncOp);
387
- remote.channel.inbox.remove(syncOp);
388
- continue;
389
- }
390
- const jobKey = `${syncOp.documentId}:${syncOp.branch}`;
391
- this.loadJobs.set(jobKey, completedJobInfo);
392
- if (completedJobInfo.status === JobStatus.FAILED) {
393
- const errorMessage = completedJobInfo.error?.message || "Unknown error";
394
- const channelError = new ChannelError(ChannelErrorSource.Inbox, new Error(`Failed to apply operations: ${errorMessage}`));
395
- syncOp.failed(channelError);
396
- remote.channel.deadLetter.add(syncOp);
397
- }
398
- else {
399
- syncOp.executed();
400
- }
401
- remote.channel.inbox.remove(syncOp);
402
- }
403
- }
404
- async updateOutbox(remote, ackOrdinal) {
405
- const operations = await this.getOperationsForRemote(remote, ackOrdinal);
406
- if (operations.length === 0) {
407
- return;
408
- }
409
- // sort by (documentId, scope, ordinal) so batchOperationsByDocument
410
- // groups all operations for the same document together
411
- operations.sort((a, b) => {
412
- if (a.context.documentId !== b.context.documentId) {
413
- return a.context.documentId < b.context.documentId ? -1 : 1;
414
- }
415
- if (a.context.scope !== b.context.scope) {
416
- return a.context.scope < b.context.scope ? -1 : 1;
417
- }
418
- return a.context.ordinal - b.context.ordinal;
419
- });
420
- const batches = batchOperationsByDocument(operations);
421
- // per-document dependency chain: each batch depends on the previous
422
- // batch for the same documentId only, allowing independent documents
423
- // to be processed in parallel
424
- const lastJobByDoc = new Map();
425
- const syncOps = [];
426
- for (const batch of batches) {
427
- const jobId = crypto.randomUUID();
428
- const prevJobId = lastJobByDoc.get(batch.documentId);
429
- const syncOp = new SyncOperation(crypto.randomUUID(), jobId, prevJobId ? [prevJobId] : [], remote.name, batch.documentId, [batch.scope], batch.branch, batch.operations);
430
- syncOps.push(syncOp);
431
- lastJobByDoc.set(batch.documentId, jobId);
432
- }
433
- remote.channel.outbox.add(...syncOps);
434
- }
435
- async getOperationsForRemote(remote, ackOrdinal) {
436
- const results = await this.operationIndex.find(remote.collectionId, ackOrdinal, { excludeSourceRemote: remote.name });
437
- let operations = results.results.map((entry) => toOperationWithContext(entry));
438
- // apply the sinceTimestampUtcMs filter
439
- const sinceTimestamp = remote.options.sinceTimestampUtcMs;
440
- if (sinceTimestamp && sinceTimestamp !== "0") {
441
- operations = operations.filter((op) => op.operation.timestampUtcMs >= sinceTimestamp);
442
- }
443
- // apply the remote filter
444
- return filterOperations(operations, remote.filter);
445
- }
446
- }
447
- //# sourceMappingURL=sync-manager.js.map