@powerhousedao/reactor-api 6.0.0-dev.4 → 6.0.0-dev.41

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 (155) hide show
  1. package/dist/codegen.js +1 -1
  2. package/dist/codegen.js.map +1 -1
  3. package/dist/src/config.d.ts +1 -2
  4. package/dist/src/config.d.ts.map +1 -1
  5. package/dist/src/config.js +1 -5
  6. package/dist/src/config.js.map +1 -1
  7. package/dist/src/graphql/auth/subgraph.js +1 -1
  8. package/dist/src/graphql/auth/subgraph.js.map +1 -1
  9. package/dist/src/graphql/base-subgraph.d.ts +2 -2
  10. package/dist/src/graphql/base-subgraph.d.ts.map +1 -1
  11. package/dist/src/graphql/base-subgraph.js.map +1 -1
  12. package/dist/src/graphql/document-model-subgraph.d.ts +95 -43
  13. package/dist/src/graphql/document-model-subgraph.d.ts.map +1 -1
  14. package/dist/src/graphql/document-model-subgraph.js +570 -79
  15. package/dist/src/graphql/document-model-subgraph.js.map +1 -1
  16. package/dist/src/graphql/drive-subgraph.d.ts.map +1 -1
  17. package/dist/src/graphql/drive-subgraph.js +1 -0
  18. package/dist/src/graphql/drive-subgraph.js.map +1 -1
  19. package/dist/src/graphql/graphql-manager.d.ts +7 -2
  20. package/dist/src/graphql/graphql-manager.d.ts.map +1 -1
  21. package/dist/src/graphql/graphql-manager.js +128 -42
  22. package/dist/src/graphql/graphql-manager.js.map +1 -1
  23. package/dist/src/graphql/index.d.ts +1 -0
  24. package/dist/src/graphql/index.d.ts.map +1 -1
  25. package/dist/src/graphql/index.js +1 -0
  26. package/dist/src/graphql/index.js.map +1 -1
  27. package/dist/src/graphql/reactor/adapters.d.ts +10 -2
  28. package/dist/src/graphql/reactor/adapters.d.ts.map +1 -1
  29. package/dist/src/graphql/reactor/adapters.js +35 -1
  30. package/dist/src/graphql/reactor/adapters.js.map +1 -1
  31. package/dist/src/graphql/reactor/factory.d.ts +1 -1
  32. package/dist/src/graphql/reactor/factory.d.ts.map +1 -1
  33. package/dist/src/graphql/reactor/factory.js +1 -1
  34. package/dist/src/graphql/reactor/factory.js.map +1 -1
  35. package/dist/src/graphql/reactor/gen/graphql.d.ts +84 -24
  36. package/dist/src/graphql/reactor/gen/graphql.d.ts.map +1 -1
  37. package/dist/src/graphql/reactor/gen/graphql.js +33 -9
  38. package/dist/src/graphql/reactor/gen/graphql.js.map +1 -1
  39. package/dist/src/graphql/reactor/index.d.ts +1 -1
  40. package/dist/src/graphql/reactor/index.d.ts.map +1 -1
  41. package/dist/src/graphql/reactor/index.js +1 -1
  42. package/dist/src/graphql/reactor/index.js.map +1 -1
  43. package/dist/src/graphql/reactor/requester.with-zod.d.ts.map +1 -1
  44. package/dist/src/graphql/reactor/requester.with-zod.js +100 -38
  45. package/dist/src/graphql/reactor/requester.with-zod.js.map +1 -1
  46. package/dist/src/graphql/reactor/resolvers.d.ts +41 -21
  47. package/dist/src/graphql/reactor/resolvers.d.ts.map +1 -1
  48. package/dist/src/graphql/reactor/resolvers.js +118 -64
  49. package/dist/src/graphql/reactor/resolvers.js.map +1 -1
  50. package/dist/src/graphql/reactor/schema.graphql +51 -31
  51. package/dist/src/graphql/reactor/subgraph.d.ts.map +1 -1
  52. package/dist/src/graphql/reactor/subgraph.js +91 -108
  53. package/dist/src/graphql/reactor/subgraph.js.map +1 -1
  54. package/dist/src/graphql/reactor/validation.d.ts +24 -0
  55. package/dist/src/graphql/reactor/validation.d.ts.map +1 -1
  56. package/dist/src/graphql/reactor/validation.js +15 -0
  57. package/dist/src/graphql/reactor/validation.js.map +1 -1
  58. package/dist/src/graphql/system/index.d.ts +0 -1
  59. package/dist/src/graphql/system/index.d.ts.map +1 -1
  60. package/dist/src/graphql/system/index.js +0 -1
  61. package/dist/src/graphql/system/index.js.map +1 -1
  62. package/dist/src/graphql/types.d.ts +3 -3
  63. package/dist/src/graphql/types.d.ts.map +1 -1
  64. package/dist/src/graphql/utils.d.ts.map +1 -1
  65. package/dist/src/graphql/utils.js +7 -3
  66. package/dist/src/graphql/utils.js.map +1 -1
  67. package/dist/src/packages/import-loader.d.ts +5 -3
  68. package/dist/src/packages/import-loader.d.ts.map +1 -1
  69. package/dist/src/packages/import-loader.js +19 -10
  70. package/dist/src/packages/import-loader.js.map +1 -1
  71. package/dist/src/packages/package-manager.d.ts +2 -2
  72. package/dist/src/packages/package-manager.d.ts.map +1 -1
  73. package/dist/src/packages/package-manager.js.map +1 -1
  74. package/dist/src/packages/types.d.ts +8 -4
  75. package/dist/src/packages/types.d.ts.map +1 -1
  76. package/dist/src/packages/util.d.ts +3 -2
  77. package/dist/src/packages/util.d.ts.map +1 -1
  78. package/dist/src/packages/util.js +1 -1
  79. package/dist/src/packages/util.js.map +1 -1
  80. package/dist/src/packages/vite-loader.d.ts +7 -6
  81. package/dist/src/packages/vite-loader.d.ts.map +1 -1
  82. package/dist/src/packages/vite-loader.js +21 -8
  83. package/dist/src/packages/vite-loader.js.map +1 -1
  84. package/dist/src/server.d.ts +16 -7
  85. package/dist/src/server.d.ts.map +1 -1
  86. package/dist/src/server.js +117 -59
  87. package/dist/src/server.js.map +1 -1
  88. package/dist/src/services/auth.service.d.ts.map +1 -1
  89. package/dist/src/services/auth.service.js +11 -20
  90. package/dist/src/services/auth.service.js.map +1 -1
  91. package/dist/src/tracing.js +1 -1
  92. package/dist/src/tracing.js.map +1 -1
  93. package/dist/src/types.d.ts +5 -5
  94. package/dist/src/types.d.ts.map +1 -1
  95. package/dist/src/utils/create-schema.d.ts +21 -1
  96. package/dist/src/utils/create-schema.d.ts.map +1 -1
  97. package/dist/src/utils/create-schema.js +290 -16
  98. package/dist/src/utils/create-schema.js.map +1 -1
  99. package/dist/src/utils/drive-url.d.ts +2 -0
  100. package/dist/src/utils/drive-url.d.ts.map +1 -0
  101. package/dist/src/utils/drive-url.js +3 -0
  102. package/dist/src/utils/drive-url.js.map +1 -0
  103. package/dist/src/utils/index.d.ts +1 -0
  104. package/dist/src/utils/index.d.ts.map +1 -1
  105. package/dist/src/utils/index.js +1 -0
  106. package/dist/src/utils/index.js.map +1 -1
  107. package/dist/test/document-drive-subgraph.test.d.ts +2 -0
  108. package/dist/test/document-drive-subgraph.test.d.ts.map +1 -0
  109. package/dist/test/document-drive-subgraph.test.js +186 -0
  110. package/dist/test/document-drive-subgraph.test.js.map +1 -0
  111. package/dist/test/document-model-subgraph-legacy-permissions.test.d.ts +2 -0
  112. package/dist/test/document-model-subgraph-legacy-permissions.test.d.ts.map +1 -0
  113. package/dist/test/document-model-subgraph-legacy-permissions.test.js +518 -0
  114. package/dist/test/document-model-subgraph-legacy-permissions.test.js.map +1 -0
  115. package/dist/test/document-model-subgraph-permissions.test.d.ts +2 -0
  116. package/dist/test/document-model-subgraph-permissions.test.d.ts.map +1 -0
  117. package/dist/test/document-model-subgraph-permissions.test.js +635 -0
  118. package/dist/test/document-model-subgraph-permissions.test.js.map +1 -0
  119. package/dist/test/document-model-subgraph.test.d.ts +2 -0
  120. package/dist/test/document-model-subgraph.test.d.ts.map +1 -0
  121. package/dist/test/document-model-subgraph.test.js +441 -0
  122. package/dist/test/document-model-subgraph.test.js.map +1 -0
  123. package/dist/test/permissions-integration.test.js +4 -4
  124. package/dist/test/permissions-integration.test.js.map +1 -1
  125. package/dist/test/reactor-client.test.js +4 -4
  126. package/dist/test/reactor-client.test.js.map +1 -1
  127. package/dist/test/reactor-resolvers.test.js +4 -8
  128. package/dist/test/reactor-resolvers.test.js.map +1 -1
  129. package/dist/test/reactor-subgraph-permissions.test.js +4 -7
  130. package/dist/test/reactor-subgraph-permissions.test.js.map +1 -1
  131. package/dist/test/subscriptions.test.js +2 -0
  132. package/dist/test/subscriptions.test.js.map +1 -1
  133. package/dist/test/two-reactor-gql-catchup-duplicate.test.d.ts +2 -0
  134. package/dist/test/two-reactor-gql-catchup-duplicate.test.d.ts.map +1 -0
  135. package/dist/test/two-reactor-gql-catchup-duplicate.test.js +264 -0
  136. package/dist/test/two-reactor-gql-catchup-duplicate.test.js.map +1 -0
  137. package/dist/test/two-reactor-gql-sync.test.js +86 -99
  138. package/dist/test/two-reactor-gql-sync.test.js.map +1 -1
  139. package/dist/test/utils/gql-resolver-bridge.d.ts.map +1 -1
  140. package/dist/test/utils/gql-resolver-bridge.js +4 -4
  141. package/dist/test/utils/gql-resolver-bridge.js.map +1 -1
  142. package/dist/tsconfig.tsbuildinfo +1 -1
  143. package/package.json +35 -25
  144. package/dist/src/graphql/system/system-subgraph.d.ts +0 -49
  145. package/dist/src/graphql/system/system-subgraph.d.ts.map +0 -1
  146. package/dist/src/graphql/system/system-subgraph.js +0 -130
  147. package/dist/src/graphql/system/system-subgraph.js.map +0 -1
  148. package/dist/test/system.test.d.ts +0 -2
  149. package/dist/test/system.test.d.ts.map +0 -1
  150. package/dist/test/system.test.js +0 -211
  151. package/dist/test/system.test.js.map +0 -1
  152. package/dist/test/three-reactor-gql-sync.test.d.ts +0 -2
  153. package/dist/test/three-reactor-gql-sync.test.d.ts.map +0 -1
  154. package/dist/test/three-reactor-gql-sync.test.js +0 -368
  155. package/dist/test/three-reactor-gql-sync.test.js.map +0 -1
@@ -0,0 +1,264 @@
1
+ import { CompositeChannelFactory, ConsoleLogger, driveCollectionId, JobStatus, ReactorEventTypes, ReactorBuilder, SyncBuilder, } from "@powerhousedao/reactor";
2
+ import { driveDocumentModelModule } from "document-drive";
3
+ import { documentModelDocumentModelModule, } from "document-model";
4
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+ import { createResolverBridge } from "./utils/gql-resolver-bridge.js";
6
+ async function waitForJobCompletion(reactor, jobId, timeoutMs = 5000) {
7
+ const startTime = Date.now();
8
+ while (Date.now() - startTime < timeoutMs) {
9
+ const status = await reactor.getJobStatus(jobId);
10
+ if (status.status === JobStatus.READ_READY) {
11
+ return;
12
+ }
13
+ if (status.status === JobStatus.FAILED) {
14
+ throw new Error(`Job failed: ${status.error?.message || "Unknown"}`);
15
+ }
16
+ await new Promise((resolve) => setTimeout(resolve, 50));
17
+ }
18
+ throw new Error(`Job did not complete within ${timeoutMs}ms`);
19
+ }
20
+ async function waitForOperationsReady(eventBus, documentId, timeoutMs = 5000) {
21
+ return new Promise((resolve, reject) => {
22
+ const timeout = setTimeout(() => {
23
+ unsubscribe();
24
+ reject(new Error(`OPERATIONS_READY event for document ${documentId} not received within ${timeoutMs}ms`));
25
+ }, timeoutMs);
26
+ const unsubscribe = eventBus.subscribe(ReactorEventTypes.JOB_READ_READY, (type, event) => {
27
+ const hasDocument = event.operations.some((op) => op.context.documentId === documentId);
28
+ if (hasDocument) {
29
+ clearTimeout(timeout);
30
+ unsubscribe();
31
+ resolve();
32
+ }
33
+ });
34
+ });
35
+ }
36
+ /**
37
+ * Creates a unique key for an operation using id + index + skip.
38
+ * Per deriveOperationId, the same operation ID can appear with different index values.
39
+ * A true duplicate is identified by the combination of id + index + skip.
40
+ */
41
+ function createOperationKey(op) {
42
+ return `${op.operation.id}:${op.operation.index}:${op.operation.skip}`;
43
+ }
44
+ /**
45
+ * Creates a collector that subscribes to OPERATIONS_READY events and tracks
46
+ * operations for a specific document, detecting duplicates.
47
+ */
48
+ function collectOperationsReady(eventBus, documentId, timeoutMs = 10000) {
49
+ const operationKeys = new Set();
50
+ const allOperations = [];
51
+ const duplicates = [];
52
+ let waitResolve = null;
53
+ let waitCount = 0;
54
+ const unsubscribe = eventBus.subscribe(ReactorEventTypes.JOB_READ_READY, (type, event) => {
55
+ for (const op of event.operations) {
56
+ if (op.context.documentId !== documentId) {
57
+ continue;
58
+ }
59
+ const key = createOperationKey(op);
60
+ allOperations.push(op);
61
+ if (operationKeys.has(key)) {
62
+ duplicates.push(op);
63
+ }
64
+ else {
65
+ operationKeys.add(key);
66
+ }
67
+ }
68
+ if (waitResolve && allOperations.length >= waitCount) {
69
+ waitResolve();
70
+ waitResolve = null;
71
+ }
72
+ });
73
+ return {
74
+ operationKeys,
75
+ allOperations,
76
+ duplicates,
77
+ waitForCount: (count) => {
78
+ if (allOperations.length >= count) {
79
+ return Promise.resolve();
80
+ }
81
+ waitCount = count;
82
+ return new Promise((resolve, reject) => {
83
+ waitResolve = resolve;
84
+ setTimeout(() => {
85
+ if (waitResolve) {
86
+ waitResolve = null;
87
+ reject(new Error(`Expected ${count} operations but received ${allOperations.length} within ${timeoutMs}ms`));
88
+ }
89
+ }, timeoutMs);
90
+ });
91
+ },
92
+ stop: () => {
93
+ unsubscribe();
94
+ },
95
+ };
96
+ }
97
+ /**
98
+ * Creates a collector that subscribes to JOB_FAILED events and tracks failures
99
+ * that indicate duplicate operations (document already exists errors).
100
+ */
101
+ function collectDuplicateFailures(eventBus) {
102
+ const failures = [];
103
+ const unsubscribe = eventBus.subscribe(ReactorEventTypes.JOB_FAILED, (type, event) => {
104
+ if (event.error.message.includes("already exists")) {
105
+ failures.push(event);
106
+ }
107
+ });
108
+ return {
109
+ failures,
110
+ stop: () => {
111
+ unsubscribe();
112
+ },
113
+ };
114
+ }
115
+ async function setupTwoReactors() {
116
+ const syncManagerRegistry = new Map();
117
+ const resolverBridge = createResolverBridge(syncManagerRegistry);
118
+ const logger = new ConsoleLogger(["test"]);
119
+ const channelFactoryA = new CompositeChannelFactory(logger);
120
+ const channelFactoryB = new CompositeChannelFactory(logger);
121
+ const models = [
122
+ driveDocumentModelModule,
123
+ documentModelDocumentModelModule,
124
+ ];
125
+ const reactorAModule = await new ReactorBuilder()
126
+ .withDocumentModels(models)
127
+ .withSync(new SyncBuilder().withChannelFactory(channelFactoryA))
128
+ .buildModule();
129
+ const reactorA = reactorAModule.reactor;
130
+ const eventBusA = reactorAModule.eventBus;
131
+ const syncManagerA = reactorAModule.syncModule.syncManager;
132
+ const reactorBModule = await new ReactorBuilder()
133
+ .withDocumentModels(models)
134
+ .withSync(new SyncBuilder().withChannelFactory(channelFactoryB))
135
+ .buildModule();
136
+ const reactorB = reactorBModule.reactor;
137
+ const eventBusB = reactorBModule.eventBus;
138
+ const syncManagerB = reactorBModule.syncModule.syncManager;
139
+ syncManagerRegistry.set("reactora", syncManagerA);
140
+ syncManagerRegistry.set("reactorb", syncManagerB);
141
+ return {
142
+ reactorA,
143
+ reactorB,
144
+ eventBusA,
145
+ eventBusB,
146
+ syncManagerA,
147
+ syncManagerB,
148
+ resolverBridge,
149
+ };
150
+ }
151
+ describe("Two-Reactor GQL Catchup Duplicate Operations Bug", () => {
152
+ let reactorA;
153
+ let reactorB;
154
+ let eventBusA;
155
+ let eventBusB;
156
+ let syncManagerA;
157
+ let resolverBridge;
158
+ beforeEach(async () => {
159
+ const setup = await setupTwoReactors();
160
+ reactorA = setup.reactorA;
161
+ reactorB = setup.reactorB;
162
+ eventBusA = setup.eventBusA;
163
+ eventBusB = setup.eventBusB;
164
+ syncManagerA = setup.syncManagerA;
165
+ resolverBridge = setup.resolverBridge;
166
+ });
167
+ afterEach(() => {
168
+ reactorA.kill();
169
+ reactorB.kill();
170
+ });
171
+ it("should not produce duplicate operations when remote is removed and re-added", async () => {
172
+ const driveDocument = driveDocumentModelModule.utils.createDocument();
173
+ const collectionId = driveCollectionId("main", driveDocument.header.id);
174
+ const jobInfo = await reactorA.create(driveDocument);
175
+ await waitForJobCompletion(reactorA, jobInfo.id);
176
+ const gqlParamsToB = {
177
+ url: "http://reactorB/graphql",
178
+ pollIntervalMs: 100,
179
+ maxFailures: 10,
180
+ retryBaseDelayMs: 50,
181
+ fetchFn: resolverBridge,
182
+ };
183
+ const filter = {
184
+ documentId: [],
185
+ scope: [],
186
+ branch: "main",
187
+ };
188
+ const readyPromiseB = waitForOperationsReady(eventBusB, driveDocument.header.id);
189
+ await syncManagerA.add("remoteB", collectionId, { type: "gql", parameters: gqlParamsToB }, filter);
190
+ await readyPromiseB;
191
+ const resultA = await reactorA.getOperations(driveDocument.header.id, {
192
+ branch: "main",
193
+ });
194
+ const opsA = Object.values(resultA).flatMap((scope) => scope.results);
195
+ expect(opsA.length).toBeGreaterThan(0);
196
+ const resultB = await reactorB.getOperations(driveDocument.header.id, {
197
+ branch: "main",
198
+ });
199
+ const opsB = Object.values(resultB).flatMap((scope) => scope.results);
200
+ expect(opsB.length).toBe(opsA.length);
201
+ const operationCollector = collectOperationsReady(eventBusA, driveDocument.header.id, 15000);
202
+ const failureCollector = collectDuplicateFailures(eventBusA);
203
+ try {
204
+ await syncManagerA.remove("remoteB");
205
+ await new Promise((resolve) => setTimeout(resolve, 200));
206
+ await syncManagerA.add("remoteB", collectionId, { type: "gql", parameters: gqlParamsToB }, filter);
207
+ await new Promise((resolve) => setTimeout(resolve, 2000));
208
+ expect(operationCollector.duplicates.length, "Duplicate operations were received via OPERATIONS_READY events").toBe(0);
209
+ expect(failureCollector.failures.length, "Duplicate operations caused 'document already exists' failures - this indicates the catchup backfill bug").toBe(0);
210
+ }
211
+ finally {
212
+ operationCollector.stop();
213
+ failureCollector.stop();
214
+ }
215
+ }, 20000);
216
+ it("should not produce duplicate operations after multiple remove/re-add cycles", async () => {
217
+ const driveDocument = driveDocumentModelModule.utils.createDocument();
218
+ const collectionId = driveCollectionId("main", driveDocument.header.id);
219
+ const jobInfo = await reactorA.create(driveDocument);
220
+ await waitForJobCompletion(reactorA, jobInfo.id);
221
+ await reactorA.execute(driveDocument.header.id, "main", [
222
+ driveDocumentModelModule.actions.setDriveName({ name: "Test Drive" }),
223
+ driveDocumentModelModule.actions.addFolder({
224
+ id: "folder-1",
225
+ name: "Folder 1",
226
+ parentFolder: null,
227
+ }),
228
+ ]);
229
+ const gqlParamsToB = {
230
+ url: "http://reactorB/graphql",
231
+ pollIntervalMs: 100,
232
+ maxFailures: 10,
233
+ retryBaseDelayMs: 50,
234
+ fetchFn: resolverBridge,
235
+ };
236
+ const filter = {
237
+ documentId: [],
238
+ scope: [],
239
+ branch: "main",
240
+ };
241
+ const readyPromiseB = waitForOperationsReady(eventBusB, driveDocument.header.id);
242
+ await syncManagerA.add("remoteB", collectionId, { type: "gql", parameters: gqlParamsToB }, filter);
243
+ await readyPromiseB;
244
+ await new Promise((resolve) => setTimeout(resolve, 500));
245
+ const operationCollector = collectOperationsReady(eventBusA, driveDocument.header.id, 30000);
246
+ const failureCollector = collectDuplicateFailures(eventBusA);
247
+ try {
248
+ for (let cycle = 0; cycle < 3; cycle++) {
249
+ await syncManagerA.remove("remoteB");
250
+ await new Promise((resolve) => setTimeout(resolve, 200));
251
+ await syncManagerA.add("remoteB", collectionId, { type: "gql", parameters: gqlParamsToB }, filter);
252
+ await new Promise((resolve) => setTimeout(resolve, 1000));
253
+ }
254
+ await new Promise((resolve) => setTimeout(resolve, 2000));
255
+ expect(operationCollector.duplicates.length, "Duplicate operations were received via OPERATIONS_READY events").toBe(0);
256
+ expect(failureCollector.failures.length, "Duplicate operations caused 'document already exists' failures after 3 remove/re-add cycles - this indicates the catchup backfill bug").toBe(0);
257
+ }
258
+ finally {
259
+ operationCollector.stop();
260
+ failureCollector.stop();
261
+ }
262
+ }, 45000);
263
+ });
264
+ //# sourceMappingURL=two-reactor-gql-catchup-duplicate.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"two-reactor-gql-catchup-duplicate.test.js","sourceRoot":"","sources":["../../test/two-reactor-gql-catchup-duplicate.test.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { CompositeChannelFactory, ConsoleLogger, JobStatus, OperationEventTypes, ReactorBuilder, SyncBuilder, } from "@powerhousedao/reactor";
1
+ import { CompositeChannelFactory, ConsoleLogger, driveCollectionId, JobStatus, ReactorBuilder, ReactorEventTypes, SyncBuilder, } from "@powerhousedao/reactor";
2
2
  import { driveDocumentModelModule } from "document-drive";
3
3
  import { documentModelDocumentModelModule, } from "document-model";
4
4
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
@@ -7,7 +7,7 @@ async function waitForJobCompletion(reactor, jobId, timeoutMs = 5000) {
7
7
  const startTime = Date.now();
8
8
  while (Date.now() - startTime < timeoutMs) {
9
9
  const status = await reactor.getJobStatus(jobId);
10
- if (status.status === JobStatus.READ_MODELS_READY) {
10
+ if (status.status === JobStatus.READ_READY) {
11
11
  return;
12
12
  }
13
13
  if (status.status === JobStatus.FAILED) {
@@ -23,7 +23,7 @@ async function waitForOperationsReady(eventBus, documentId, timeoutMs = 5000) {
23
23
  unsubscribe();
24
24
  reject(new Error(`OPERATIONS_READY event for document ${documentId} not received within ${timeoutMs}ms`));
25
25
  }, timeoutMs);
26
- const unsubscribe = eventBus.subscribe(OperationEventTypes.OPERATIONS_READY, (type, event) => {
26
+ const unsubscribe = eventBus.subscribe(ReactorEventTypes.JOB_READ_READY, (type, event) => {
27
27
  const hasDocument = event.operations.some((op) => op.context.documentId === documentId);
28
28
  if (hasDocument) {
29
29
  clearTimeout(timeout);
@@ -33,24 +33,7 @@ async function waitForOperationsReady(eventBus, documentId, timeoutMs = 5000) {
33
33
  });
34
34
  });
35
35
  }
36
- async function waitForMultipleOperationsReady(eventBus, expectedCount, timeoutMs = 10000) {
37
- return new Promise((resolve, reject) => {
38
- let count = 0;
39
- const timeout = setTimeout(() => {
40
- unsubscribe();
41
- reject(new Error(`Expected ${expectedCount} OPERATIONS_READY events but received ${count} within ${timeoutMs}ms`));
42
- }, timeoutMs);
43
- const unsubscribe = eventBus.subscribe(OperationEventTypes.OPERATIONS_READY, () => {
44
- count++;
45
- if (count >= expectedCount) {
46
- clearTimeout(timeout);
47
- unsubscribe();
48
- resolve();
49
- }
50
- });
51
- });
52
- }
53
- async function setupTwoReactorsWithGqlChannel() {
36
+ async function setupTwoReactors() {
54
37
  const syncManagerRegistry = new Map();
55
38
  const resolverBridge = createResolverBridge(syncManagerRegistry);
56
39
  const logger = new ConsoleLogger(["test"]);
@@ -76,6 +59,18 @@ async function setupTwoReactorsWithGqlChannel() {
76
59
  const syncManagerB = reactorBModule.syncModule.syncManager;
77
60
  syncManagerRegistry.set("reactora", syncManagerA);
78
61
  syncManagerRegistry.set("reactorb", syncManagerB);
62
+ return {
63
+ reactorA,
64
+ reactorB,
65
+ eventBusA,
66
+ eventBusB,
67
+ syncManagerA,
68
+ syncManagerB,
69
+ resolverBridge,
70
+ };
71
+ }
72
+ async function setupSyncForDrive(syncManagerA, driveId, resolverBridge) {
73
+ const collectionId = driveCollectionId("main", driveId);
79
74
  const filter = {
80
75
  documentId: [],
81
76
  scope: [],
@@ -88,22 +83,23 @@ async function setupTwoReactorsWithGqlChannel() {
88
83
  retryBaseDelayMs: 50,
89
84
  fetchFn: resolverBridge,
90
85
  };
91
- // ReactorA adds remote pointing to B
92
- // touchChannel automatically creates receiving channel on B
93
- await syncManagerA.add("remoteB", "collection1", { type: "gql", parameters: gqlParamsToB }, filter);
94
- return { reactorA, reactorB, eventBusA, eventBusB };
86
+ await syncManagerA.add(`remoteB-${driveId}`, collectionId, { type: "gql", parameters: gqlParamsToB }, filter);
95
87
  }
96
88
  describe("Two-Reactor Sync with GqlChannel", () => {
97
89
  let reactorA;
98
90
  let reactorB;
99
91
  let eventBusA;
100
92
  let eventBusB;
93
+ let syncManagerA;
94
+ let resolverBridge;
101
95
  beforeEach(async () => {
102
- const setup = await setupTwoReactorsWithGqlChannel();
96
+ const setup = await setupTwoReactors();
103
97
  reactorA = setup.reactorA;
104
98
  reactorB = setup.reactorB;
105
99
  eventBusA = setup.eventBusA;
106
100
  eventBusB = setup.eventBusB;
101
+ syncManagerA = setup.syncManagerA;
102
+ resolverBridge = setup.resolverBridge;
107
103
  });
108
104
  afterEach(() => {
109
105
  reactorA.kill();
@@ -111,6 +107,7 @@ describe("Two-Reactor Sync with GqlChannel", () => {
111
107
  });
112
108
  it("should sync operation from ReactorA to ReactorB via GqlChannel", async () => {
113
109
  const document = driveDocumentModelModule.utils.createDocument();
110
+ await setupSyncForDrive(syncManagerA, document.header.id, resolverBridge);
114
111
  const readyPromise = waitForOperationsReady(eventBusB, document.header.id);
115
112
  const jobInfo = await reactorA.create(document);
116
113
  await waitForJobCompletion(reactorA, jobInfo.id);
@@ -130,10 +127,11 @@ describe("Two-Reactor Sync with GqlChannel", () => {
130
127
  }
131
128
  const docA = await reactorA.get(document.header.id, { branch: "main" });
132
129
  const docB = await reactorB.get(document.header.id, { branch: "main" });
133
- expect(docA.document).toEqual(docB.document);
130
+ expect(docA).toEqual(docB);
134
131
  });
135
132
  it("should sync operation from ReactorB to ReactorA via GqlChannel", async () => {
136
133
  const document = driveDocumentModelModule.utils.createDocument();
134
+ await setupSyncForDrive(syncManagerA, document.header.id, resolverBridge);
137
135
  const readyPromise = waitForOperationsReady(eventBusA, document.header.id);
138
136
  const jobInfo = await reactorB.create(document);
139
137
  await waitForJobCompletion(reactorB, jobInfo.id);
@@ -153,10 +151,9 @@ describe("Two-Reactor Sync with GqlChannel", () => {
153
151
  }
154
152
  const docA = await reactorA.get(document.header.id, { branch: "main" });
155
153
  const docB = await reactorB.get(document.header.id, { branch: "main" });
156
- expect(docA.document).toEqual(docB.document);
154
+ expect(docA).toEqual(docB);
157
155
  });
158
156
  it("should sync multiple documents with concurrent operations from both reactors", async () => {
159
- // Create 4 documents (2 for each reactor)
160
157
  const docA1 = driveDocumentModelModule.utils.createDocument();
161
158
  const docA2 = driveDocumentModelModule.utils.createDocument();
162
159
  const docB1 = driveDocumentModelModule.utils.createDocument();
@@ -167,12 +164,16 @@ describe("Two-Reactor Sync with GqlChannel", () => {
167
164
  docB1.header.id,
168
165
  docB2.header.id,
169
166
  ];
170
- // Set up listeners for docs to sync to the other reactor
167
+ await Promise.all([
168
+ setupSyncForDrive(syncManagerA, docA1.header.id, resolverBridge),
169
+ setupSyncForDrive(syncManagerA, docA2.header.id, resolverBridge),
170
+ setupSyncForDrive(syncManagerA, docB1.header.id, resolverBridge),
171
+ setupSyncForDrive(syncManagerA, docB2.header.id, resolverBridge),
172
+ ]);
171
173
  const readyOnB_A1 = waitForOperationsReady(eventBusB, docA1.header.id);
172
174
  const readyOnB_A2 = waitForOperationsReady(eventBusB, docA2.header.id);
173
175
  const readyOnA_B1 = waitForOperationsReady(eventBusA, docB1.header.id);
174
176
  const readyOnA_B2 = waitForOperationsReady(eventBusA, docB2.header.id);
175
- // Create documents on their respective reactors
176
177
  const [jobA1, jobA2] = await Promise.all([
177
178
  reactorA.create(docA1),
178
179
  reactorA.create(docA2),
@@ -181,16 +182,13 @@ describe("Two-Reactor Sync with GqlChannel", () => {
181
182
  reactorB.create(docB1),
182
183
  reactorB.create(docB2),
183
184
  ]);
184
- // Wait for all creates to complete on source reactors
185
185
  await Promise.all([
186
186
  waitForJobCompletion(reactorA, jobA1.id),
187
187
  waitForJobCompletion(reactorA, jobA2.id),
188
188
  waitForJobCompletion(reactorB, jobB1.id),
189
189
  waitForJobCompletion(reactorB, jobB2.id),
190
190
  ]);
191
- // Wait for all docs to sync to the other reactor
192
191
  await Promise.all([readyOnB_A1, readyOnB_A2, readyOnA_B1, readyOnA_B2]);
193
- // Now fire concurrent modify operations on each doc
194
192
  void reactorA.execute(docA1.header.id, "main", [
195
193
  driveDocumentModelModule.actions.setDriveName({ name: "Drive A1" }),
196
194
  driveDocumentModelModule.actions.addFolder({
@@ -223,7 +221,6 @@ describe("Two-Reactor Sync with GqlChannel", () => {
223
221
  parentFolder: null,
224
222
  }),
225
223
  ]);
226
- // Poll until all 4 documents are synced on both reactors
227
224
  const startTime = Date.now();
228
225
  const timeout = 25000;
229
226
  let synced = false;
@@ -239,8 +236,6 @@ describe("Two-Reactor Sync with GqlChannel", () => {
239
236
  branch: "main",
240
237
  });
241
238
  const opsB = Object.values(resultB).flatMap((scope) => scope.results);
242
- // Each doc should have at least 2 ops (create + execute with actions)
243
- // and both reactors should have same count
244
239
  if (opsA.length < 2 ||
245
240
  opsB.length < 2 ||
246
241
  opsA.length !== opsB.length) {
@@ -249,7 +244,6 @@ describe("Two-Reactor Sync with GqlChannel", () => {
249
244
  }
250
245
  }
251
246
  catch {
252
- // Document may not exist on one reactor yet
253
247
  allDocsSynced = false;
254
248
  break;
255
249
  }
@@ -261,7 +255,6 @@ describe("Two-Reactor Sync with GqlChannel", () => {
261
255
  await new Promise((resolve) => setTimeout(resolve, 100));
262
256
  }
263
257
  expect(synced).toBe(true);
264
- // Verify operation equality for all 4 documents
265
258
  for (const docId of allDocIds) {
266
259
  const resultA = await reactorA.getOperations(docId, { branch: "main" });
267
260
  const opsA = Object.values(resultA).flatMap((scope) => scope.results);
@@ -273,76 +266,70 @@ describe("Two-Reactor Sync with GqlChannel", () => {
273
266
  expect(opsB[i]).toEqual(opsA[i]);
274
267
  }
275
268
  }
276
- // Verify document state equality for all 4 documents
277
269
  for (const docId of allDocIds) {
278
270
  const docFromA = await reactorA.get(docId, { branch: "main" });
279
271
  const docFromB = await reactorB.get(docId, { branch: "main" });
280
- expect(docFromA.document).toEqual(docFromB.document);
272
+ expect(docFromA).toEqual(docFromB);
281
273
  }
282
274
  }, 30000);
283
- it("should handle concurrent modifications to the same document from both reactors", async () => {
284
- const doc = driveDocumentModelModule.utils.createDocument();
285
- const readyPromise = waitForOperationsReady(eventBusB, doc.header.id);
286
- const createJob = await reactorA.create(doc);
287
- await waitForJobCompletion(reactorA, createJob.id);
275
+ it("should include key/dependsOn in poll response for batch operations", async () => {
276
+ const driveDoc = driveDocumentModelModule.utils.createDocument();
277
+ await setupSyncForDrive(syncManagerA, driveDoc.header.id, resolverBridge);
278
+ const readyPromise = waitForOperationsReady(eventBusA, driveDoc.header.id);
279
+ const createJob = await reactorB.create(driveDoc);
280
+ await waitForJobCompletion(reactorB, createJob.id);
288
281
  await readyPromise;
289
- const docOnB = await reactorB.get(doc.header.id, { branch: "main" });
290
- expect(docOnB.document).toBeDefined();
291
- void reactorA.execute(doc.header.id, "main", [
292
- driveDocumentModelModule.actions.setDriveName({ name: "Name from A" }),
293
- ]);
294
- void reactorB.execute(doc.header.id, "main", [
295
- driveDocumentModelModule.actions.setDriveName({ name: "Name from B" }),
296
- ]);
297
- void reactorA.execute(doc.header.id, "main", [
298
- driveDocumentModelModule.actions.addFolder({
299
- id: "folder-a",
300
- name: "Folder from A",
301
- parentFolder: null,
302
- }),
303
- ]);
304
- void reactorB.execute(doc.header.id, "main", [
305
- driveDocumentModelModule.actions.addFolder({
306
- id: "folder-b",
307
- name: "Folder from B",
308
- parentFolder: null,
309
- }),
310
- ]);
282
+ const batchResult = await reactorB.executeBatch({
283
+ jobs: [
284
+ {
285
+ key: "rename",
286
+ documentId: driveDoc.header.id,
287
+ scope: "global",
288
+ branch: "main",
289
+ actions: [
290
+ driveDocumentModelModule.actions.setDriveName({
291
+ name: "Renamed Drive",
292
+ }),
293
+ ],
294
+ dependsOn: [],
295
+ },
296
+ {
297
+ key: "add-folder",
298
+ documentId: driveDoc.header.id,
299
+ scope: "global",
300
+ branch: "main",
301
+ actions: [
302
+ driveDocumentModelModule.actions.addFolder({
303
+ id: "folder-1",
304
+ name: "Folder 1",
305
+ parentFolder: null,
306
+ }),
307
+ ],
308
+ dependsOn: ["rename"],
309
+ },
310
+ ],
311
+ });
312
+ for (const jobInfo of Object.values(batchResult.jobs)) {
313
+ await waitForJobCompletion(reactorB, jobInfo.id);
314
+ }
311
315
  const startTime = Date.now();
312
- const timeout = 15000;
313
- let synced = false;
316
+ const timeout = 10000;
317
+ let statesSynced = false;
314
318
  while (Date.now() - startTime < timeout) {
315
- const resultA = await reactorA.getOperations(doc.header.id, {
316
- branch: "main",
317
- });
318
- const opsA = Object.values(resultA).flatMap((scope) => scope.results);
319
- const resultB = await reactorB.getOperations(doc.header.id, {
320
- branch: "main",
321
- });
322
- const opsB = Object.values(resultB).flatMap((scope) => scope.results);
323
- if (opsA.length > 1 && opsB.length > 1 && opsA.length === opsB.length) {
324
- synced = true;
319
+ const docA = await reactorA.get(driveDoc.header.id, { branch: "main" });
320
+ const docB = await reactorB.get(driveDoc.header.id, { branch: "main" });
321
+ if (docA &&
322
+ docB &&
323
+ JSON.stringify(docA.state) === JSON.stringify(docB.state)) {
324
+ statesSynced = true;
325
325
  break;
326
326
  }
327
- await new Promise((resolve) => setTimeout(resolve, 100));
328
- }
329
- expect(synced).toBe(true);
330
- const resultA = await reactorA.getOperations(doc.header.id, {
331
- branch: "main",
332
- });
333
- const opsA = Object.values(resultA).flatMap((scope) => scope.results);
334
- const resultB = await reactorB.getOperations(doc.header.id, {
335
- branch: "main",
336
- });
337
- const opsB = Object.values(resultB).flatMap((scope) => scope.results);
338
- expect(opsA.length).toBeGreaterThan(0);
339
- expect(opsB.length).toBe(opsA.length);
340
- for (let i = 0; i < opsA.length; i++) {
341
- expect(opsB[i]).toEqual(opsA[i]);
327
+ await new Promise((resolve) => setTimeout(resolve, 50));
342
328
  }
343
- const docFromA = await reactorA.get(doc.header.id, { branch: "main" });
344
- const docFromB = await reactorB.get(doc.header.id, { branch: "main" });
345
- expect(docFromA.document).toEqual(docFromB.document);
346
- }, 20000);
329
+ expect(statesSynced).toBe(true);
330
+ const docA = await reactorA.get(driveDoc.header.id, { branch: "main" });
331
+ const docB = await reactorB.get(driveDoc.header.id, { branch: "main" });
332
+ expect(docA.state).toEqual(docB.state);
333
+ }, 15000);
347
334
  });
348
335
  //# sourceMappingURL=two-reactor-gql-sync.test.js.map