@powerhousedao/reactor-api 6.0.0-dev.23 → 6.0.0-dev.231

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 (326) hide show
  1. package/README.md +6 -0
  2. package/dist/index.d.mts +3039 -0
  3. package/dist/index.d.mts.map +1 -0
  4. package/dist/index.mjs +6691 -0
  5. package/dist/index.mjs.map +1 -0
  6. package/dist/src/packages/https-hooks.d.mts +39 -0
  7. package/dist/src/packages/https-hooks.d.mts.map +1 -0
  8. package/dist/src/packages/https-hooks.mjs +79 -0
  9. package/dist/src/packages/https-hooks.mjs.map +1 -0
  10. package/dist/src/packages/vite-loader.d.mts +29 -0
  11. package/dist/src/packages/vite-loader.d.mts.map +1 -0
  12. package/dist/src/packages/vite-loader.mjs +142 -0
  13. package/dist/src/packages/vite-loader.mjs.map +1 -0
  14. package/dist/types-Do4QTfT3.d.mts +37 -0
  15. package/dist/types-Do4QTfT3.d.mts.map +1 -0
  16. package/dist/utils-CHCRSWig.mjs +286 -0
  17. package/dist/utils-CHCRSWig.mjs.map +1 -0
  18. package/package.json +75 -64
  19. package/dist/codegen.d.ts +0 -4
  20. package/dist/codegen.d.ts.map +0 -1
  21. package/dist/codegen.js +0 -38
  22. package/dist/codegen.js.map +0 -1
  23. package/dist/index.d.ts +0 -14
  24. package/dist/index.d.ts.map +0 -1
  25. package/dist/index.js +0 -14
  26. package/dist/index.js.map +0 -1
  27. package/dist/src/config.d.ts +0 -7
  28. package/dist/src/config.d.ts.map +0 -1
  29. package/dist/src/config.js +0 -10
  30. package/dist/src/config.js.map +0 -1
  31. package/dist/src/graphql/analytics-subgraph.d.ts +0 -14
  32. package/dist/src/graphql/analytics-subgraph.d.ts.map +0 -1
  33. package/dist/src/graphql/analytics-subgraph.js +0 -26
  34. package/dist/src/graphql/analytics-subgraph.js.map +0 -1
  35. package/dist/src/graphql/auth/index.d.ts +0 -2
  36. package/dist/src/graphql/auth/index.d.ts.map +0 -1
  37. package/dist/src/graphql/auth/index.js +0 -2
  38. package/dist/src/graphql/auth/index.js.map +0 -1
  39. package/dist/src/graphql/auth/resolvers.d.ts +0 -149
  40. package/dist/src/graphql/auth/resolvers.d.ts.map +0 -1
  41. package/dist/src/graphql/auth/resolvers.js +0 -173
  42. package/dist/src/graphql/auth/resolvers.js.map +0 -1
  43. package/dist/src/graphql/auth/schema.graphql +0 -173
  44. package/dist/src/graphql/auth/subgraph.d.ts +0 -177
  45. package/dist/src/graphql/auth/subgraph.d.ts.map +0 -1
  46. package/dist/src/graphql/auth/subgraph.js +0 -340
  47. package/dist/src/graphql/auth/subgraph.js.map +0 -1
  48. package/dist/src/graphql/base-subgraph.d.ts +0 -20
  49. package/dist/src/graphql/base-subgraph.d.ts.map +0 -1
  50. package/dist/src/graphql/base-subgraph.js +0 -34
  51. package/dist/src/graphql/base-subgraph.js.map +0 -1
  52. package/dist/src/graphql/document-model-subgraph.d.ts +0 -51
  53. package/dist/src/graphql/document-model-subgraph.d.ts.map +0 -1
  54. package/dist/src/graphql/document-model-subgraph.js +0 -104
  55. package/dist/src/graphql/document-model-subgraph.js.map +0 -1
  56. package/dist/src/graphql/drive-subgraph.d.ts +0 -25
  57. package/dist/src/graphql/drive-subgraph.d.ts.map +0 -1
  58. package/dist/src/graphql/drive-subgraph.js +0 -487
  59. package/dist/src/graphql/drive-subgraph.js.map +0 -1
  60. package/dist/src/graphql/graphql-manager.d.ts +0 -47
  61. package/dist/src/graphql/graphql-manager.d.ts.map +0 -1
  62. package/dist/src/graphql/graphql-manager.js +0 -433
  63. package/dist/src/graphql/graphql-manager.js.map +0 -1
  64. package/dist/src/graphql/index.d.ts +0 -9
  65. package/dist/src/graphql/index.d.ts.map +0 -1
  66. package/dist/src/graphql/index.js +0 -9
  67. package/dist/src/graphql/index.js.map +0 -1
  68. package/dist/src/graphql/playground.d.ts +0 -2
  69. package/dist/src/graphql/playground.d.ts.map +0 -1
  70. package/dist/src/graphql/playground.js +0 -74
  71. package/dist/src/graphql/playground.js.map +0 -1
  72. package/dist/src/graphql/reactor/adapters.d.ts +0 -62
  73. package/dist/src/graphql/reactor/adapters.d.ts.map +0 -1
  74. package/dist/src/graphql/reactor/adapters.js +0 -270
  75. package/dist/src/graphql/reactor/adapters.js.map +0 -1
  76. package/dist/src/graphql/reactor/factory.d.ts +0 -84
  77. package/dist/src/graphql/reactor/factory.d.ts.map +0 -1
  78. package/dist/src/graphql/reactor/factory.js +0 -7
  79. package/dist/src/graphql/reactor/factory.js.map +0 -1
  80. package/dist/src/graphql/reactor/gen/graphql.d.ts +0 -1242
  81. package/dist/src/graphql/reactor/gen/graphql.d.ts.map +0 -1
  82. package/dist/src/graphql/reactor/gen/graphql.js +0 -496
  83. package/dist/src/graphql/reactor/gen/graphql.js.map +0 -1
  84. package/dist/src/graphql/reactor/index.d.ts +0 -4
  85. package/dist/src/graphql/reactor/index.d.ts.map +0 -1
  86. package/dist/src/graphql/reactor/index.js +0 -4
  87. package/dist/src/graphql/reactor/index.js.map +0 -1
  88. package/dist/src/graphql/reactor/operations.graphql +0 -253
  89. package/dist/src/graphql/reactor/pubsub.d.ts +0 -27
  90. package/dist/src/graphql/reactor/pubsub.d.ts.map +0 -1
  91. package/dist/src/graphql/reactor/pubsub.js +0 -93
  92. package/dist/src/graphql/reactor/pubsub.js.map +0 -1
  93. package/dist/src/graphql/reactor/requester.d.ts +0 -4
  94. package/dist/src/graphql/reactor/requester.d.ts.map +0 -1
  95. package/dist/src/graphql/reactor/requester.js +0 -22
  96. package/dist/src/graphql/reactor/requester.js.map +0 -1
  97. package/dist/src/graphql/reactor/requester.with-zod.d.ts +0 -4
  98. package/dist/src/graphql/reactor/requester.with-zod.d.ts.map +0 -1
  99. package/dist/src/graphql/reactor/requester.with-zod.js +0 -53
  100. package/dist/src/graphql/reactor/requester.with-zod.js.map +0 -1
  101. package/dist/src/graphql/reactor/resolvers.d.ts +0 -175
  102. package/dist/src/graphql/reactor/resolvers.d.ts.map +0 -1
  103. package/dist/src/graphql/reactor/resolvers.js +0 -526
  104. package/dist/src/graphql/reactor/resolvers.js.map +0 -1
  105. package/dist/src/graphql/reactor/schema.graphql +0 -432
  106. package/dist/src/graphql/reactor/subgraph.d.ts +0 -47
  107. package/dist/src/graphql/reactor/subgraph.d.ts.map +0 -1
  108. package/dist/src/graphql/reactor/subgraph.js +0 -555
  109. package/dist/src/graphql/reactor/subgraph.js.map +0 -1
  110. package/dist/src/graphql/reactor/validation.d.ts +0 -102
  111. package/dist/src/graphql/reactor/validation.d.ts.map +0 -1
  112. package/dist/src/graphql/reactor/validation.js +0 -73
  113. package/dist/src/graphql/reactor/validation.js.map +0 -1
  114. package/dist/src/graphql/system/env/getters.d.ts +0 -2
  115. package/dist/src/graphql/system/env/getters.d.ts.map +0 -1
  116. package/dist/src/graphql/system/env/getters.js +0 -4
  117. package/dist/src/graphql/system/env/getters.js.map +0 -1
  118. package/dist/src/graphql/system/env/index.d.ts +0 -2
  119. package/dist/src/graphql/system/env/index.d.ts.map +0 -1
  120. package/dist/src/graphql/system/env/index.js +0 -5
  121. package/dist/src/graphql/system/env/index.js.map +0 -1
  122. package/dist/src/graphql/system/index.d.ts +0 -3
  123. package/dist/src/graphql/system/index.d.ts.map +0 -1
  124. package/dist/src/graphql/system/index.js +0 -3
  125. package/dist/src/graphql/system/index.js.map +0 -1
  126. package/dist/src/graphql/system/system-subgraph.d.ts +0 -49
  127. package/dist/src/graphql/system/system-subgraph.d.ts.map +0 -1
  128. package/dist/src/graphql/system/system-subgraph.js +0 -130
  129. package/dist/src/graphql/system/system-subgraph.js.map +0 -1
  130. package/dist/src/graphql/system/types.d.ts +0 -2
  131. package/dist/src/graphql/system/types.d.ts.map +0 -1
  132. package/dist/src/graphql/system/types.js +0 -2
  133. package/dist/src/graphql/system/types.js.map +0 -1
  134. package/dist/src/graphql/temp-hack-rwa-type-defs.d.ts +0 -57
  135. package/dist/src/graphql/temp-hack-rwa-type-defs.d.ts.map +0 -1
  136. package/dist/src/graphql/temp-hack-rwa-type-defs.js +0 -2
  137. package/dist/src/graphql/temp-hack-rwa-type-defs.js.map +0 -1
  138. package/dist/src/graphql/types.d.ts +0 -103
  139. package/dist/src/graphql/types.d.ts.map +0 -1
  140. package/dist/src/graphql/types.js +0 -2
  141. package/dist/src/graphql/types.js.map +0 -1
  142. package/dist/src/graphql/utils.d.ts +0 -26
  143. package/dist/src/graphql/utils.d.ts.map +0 -1
  144. package/dist/src/graphql/utils.js +0 -100
  145. package/dist/src/graphql/utils.js.map +0 -1
  146. package/dist/src/graphql/websocket.d.ts +0 -3
  147. package/dist/src/graphql/websocket.d.ts.map +0 -1
  148. package/dist/src/graphql/websocket.js +0 -15
  149. package/dist/src/graphql/websocket.js.map +0 -1
  150. package/dist/src/migrations/001_create_document_permissions.d.ts +0 -4
  151. package/dist/src/migrations/001_create_document_permissions.d.ts.map +0 -1
  152. package/dist/src/migrations/001_create_document_permissions.js +0 -91
  153. package/dist/src/migrations/001_create_document_permissions.js.map +0 -1
  154. package/dist/src/migrations/index.d.ts +0 -10
  155. package/dist/src/migrations/index.d.ts.map +0 -1
  156. package/dist/src/migrations/index.js +0 -56
  157. package/dist/src/migrations/index.js.map +0 -1
  158. package/dist/src/packages/import-loader.d.ts +0 -16
  159. package/dist/src/packages/import-loader.d.ts.map +0 -1
  160. package/dist/src/packages/import-loader.js +0 -61
  161. package/dist/src/packages/import-loader.js.map +0 -1
  162. package/dist/src/packages/import-resolver.d.ts +0 -5
  163. package/dist/src/packages/import-resolver.d.ts.map +0 -1
  164. package/dist/src/packages/import-resolver.js +0 -127
  165. package/dist/src/packages/import-resolver.js.map +0 -1
  166. package/dist/src/packages/package-manager.d.ts +0 -34
  167. package/dist/src/packages/package-manager.d.ts.map +0 -1
  168. package/dist/src/packages/package-manager.js +0 -213
  169. package/dist/src/packages/package-manager.js.map +0 -1
  170. package/dist/src/packages/types.d.ts +0 -39
  171. package/dist/src/packages/types.d.ts.map +0 -1
  172. package/dist/src/packages/types.js +0 -2
  173. package/dist/src/packages/types.js.map +0 -1
  174. package/dist/src/packages/util.d.ts +0 -27
  175. package/dist/src/packages/util.d.ts.map +0 -1
  176. package/dist/src/packages/util.js +0 -97
  177. package/dist/src/packages/util.js.map +0 -1
  178. package/dist/src/packages/vite-loader.d.ts +0 -24
  179. package/dist/src/packages/vite-loader.d.ts.map +0 -1
  180. package/dist/src/packages/vite-loader.js +0 -172
  181. package/dist/src/packages/vite-loader.js.map +0 -1
  182. package/dist/src/server.d.ts +0 -73
  183. package/dist/src/server.d.ts.map +0 -1
  184. package/dist/src/server.js +0 -431
  185. package/dist/src/server.js.map +0 -1
  186. package/dist/src/services/auth.service.d.ts +0 -68
  187. package/dist/src/services/auth.service.d.ts.map +0 -1
  188. package/dist/src/services/auth.service.js +0 -199
  189. package/dist/src/services/auth.service.js.map +0 -1
  190. package/dist/src/services/document-permission.service.d.ts +0 -201
  191. package/dist/src/services/document-permission.service.d.ts.map +0 -1
  192. package/dist/src/services/document-permission.service.js +0 -636
  193. package/dist/src/services/document-permission.service.js.map +0 -1
  194. package/dist/src/sync/types.d.ts +0 -10
  195. package/dist/src/sync/types.d.ts.map +0 -1
  196. package/dist/src/sync/types.js +0 -2
  197. package/dist/src/sync/types.js.map +0 -1
  198. package/dist/src/sync/utils.d.ts +0 -7
  199. package/dist/src/sync/utils.d.ts.map +0 -1
  200. package/dist/src/sync/utils.js +0 -78
  201. package/dist/src/sync/utils.js.map +0 -1
  202. package/dist/src/tracing.d.ts +0 -4
  203. package/dist/src/tracing.d.ts.map +0 -1
  204. package/dist/src/tracing.js +0 -122
  205. package/dist/src/tracing.js.map +0 -1
  206. package/dist/src/types.d.ts +0 -18
  207. package/dist/src/types.d.ts.map +0 -1
  208. package/dist/src/types.js +0 -2
  209. package/dist/src/types.js.map +0 -1
  210. package/dist/src/utils/auth.d.ts +0 -3
  211. package/dist/src/utils/auth.d.ts.map +0 -1
  212. package/dist/src/utils/auth.js +0 -19
  213. package/dist/src/utils/auth.js.map +0 -1
  214. package/dist/src/utils/create-schema.d.ts +0 -11
  215. package/dist/src/utils/create-schema.d.ts.map +0 -1
  216. package/dist/src/utils/create-schema.js +0 -322
  217. package/dist/src/utils/create-schema.js.map +0 -1
  218. package/dist/src/utils/db.d.ts +0 -74
  219. package/dist/src/utils/db.d.ts.map +0 -1
  220. package/dist/src/utils/db.js +0 -101
  221. package/dist/src/utils/db.js.map +0 -1
  222. package/dist/src/utils/drive-url.d.ts +0 -2
  223. package/dist/src/utils/drive-url.d.ts.map +0 -1
  224. package/dist/src/utils/drive-url.js +0 -3
  225. package/dist/src/utils/drive-url.js.map +0 -1
  226. package/dist/src/utils/index.d.ts +0 -4
  227. package/dist/src/utils/index.d.ts.map +0 -1
  228. package/dist/src/utils/index.js +0 -4
  229. package/dist/src/utils/index.js.map +0 -1
  230. package/dist/test/benchmarks/load.bench.d.ts +0 -2
  231. package/dist/test/benchmarks/load.bench.d.ts.map +0 -1
  232. package/dist/test/benchmarks/load.bench.js +0 -73
  233. package/dist/test/benchmarks/load.bench.js.map +0 -1
  234. package/dist/test/benchmarks/sync.bench.d.ts +0 -2
  235. package/dist/test/benchmarks/sync.bench.d.ts.map +0 -1
  236. package/dist/test/benchmarks/sync.bench.js +0 -119
  237. package/dist/test/benchmarks/sync.bench.js.map +0 -1
  238. package/dist/test/document-permission.service.test.d.ts +0 -2
  239. package/dist/test/document-permission.service.test.d.ts.map +0 -1
  240. package/dist/test/document-permission.service.test.js +0 -480
  241. package/dist/test/document-permission.service.test.js.map +0 -1
  242. package/dist/test/drive-handlers.d.ts +0 -4
  243. package/dist/test/drive-handlers.d.ts.map +0 -1
  244. package/dist/test/drive-handlers.js +0 -39
  245. package/dist/test/drive-handlers.js.map +0 -1
  246. package/dist/test/drive-subgraph-permissions.test.d.ts +0 -2
  247. package/dist/test/drive-subgraph-permissions.test.d.ts.map +0 -1
  248. package/dist/test/drive-subgraph-permissions.test.js +0 -195
  249. package/dist/test/drive-subgraph-permissions.test.js.map +0 -1
  250. package/dist/test/drive.test.d.ts +0 -2
  251. package/dist/test/drive.test.d.ts.map +0 -1
  252. package/dist/test/drive.test.js +0 -142
  253. package/dist/test/drive.test.js.map +0 -1
  254. package/dist/test/identity-integration.test.d.ts +0 -2
  255. package/dist/test/identity-integration.test.d.ts.map +0 -1
  256. package/dist/test/identity-integration.test.js +0 -349
  257. package/dist/test/identity-integration.test.js.map +0 -1
  258. package/dist/test/index.d.ts +0 -3
  259. package/dist/test/index.d.ts.map +0 -1
  260. package/dist/test/index.js +0 -3
  261. package/dist/test/index.js.map +0 -1
  262. package/dist/test/permissions-integration.test.d.ts +0 -2
  263. package/dist/test/permissions-integration.test.d.ts.map +0 -1
  264. package/dist/test/permissions-integration.test.js +0 -421
  265. package/dist/test/permissions-integration.test.js.map +0 -1
  266. package/dist/test/pull-responder-transmitter.test.d.ts +0 -2
  267. package/dist/test/pull-responder-transmitter.test.d.ts.map +0 -1
  268. package/dist/test/pull-responder-transmitter.test.js +0 -220
  269. package/dist/test/pull-responder-transmitter.test.js.map +0 -1
  270. package/dist/test/push-transmitter.test.d.ts +0 -2
  271. package/dist/test/push-transmitter.test.d.ts.map +0 -1
  272. package/dist/test/push-transmitter.test.js +0 -179
  273. package/dist/test/push-transmitter.test.js.map +0 -1
  274. package/dist/test/reactor-adapters.test.d.ts +0 -2
  275. package/dist/test/reactor-adapters.test.d.ts.map +0 -1
  276. package/dist/test/reactor-adapters.test.js +0 -379
  277. package/dist/test/reactor-adapters.test.js.map +0 -1
  278. package/dist/test/reactor-client.test.d.ts +0 -2
  279. package/dist/test/reactor-client.test.d.ts.map +0 -1
  280. package/dist/test/reactor-client.test.js +0 -212
  281. package/dist/test/reactor-client.test.js.map +0 -1
  282. package/dist/test/reactor-resolvers.test.d.ts +0 -2
  283. package/dist/test/reactor-resolvers.test.d.ts.map +0 -1
  284. package/dist/test/reactor-resolvers.test.js +0 -261
  285. package/dist/test/reactor-resolvers.test.js.map +0 -1
  286. package/dist/test/reactor-subgraph-permissions.test.d.ts +0 -2
  287. package/dist/test/reactor-subgraph-permissions.test.d.ts.map +0 -1
  288. package/dist/test/reactor-subgraph-permissions.test.js +0 -400
  289. package/dist/test/reactor-subgraph-permissions.test.js.map +0 -1
  290. package/dist/test/router.test.d.ts +0 -2
  291. package/dist/test/router.test.d.ts.map +0 -1
  292. package/dist/test/router.test.js +0 -38
  293. package/dist/test/router.test.js.map +0 -1
  294. package/dist/test/subscriptions.test.d.ts +0 -2
  295. package/dist/test/subscriptions.test.d.ts.map +0 -1
  296. package/dist/test/subscriptions.test.js +0 -246
  297. package/dist/test/subscriptions.test.js.map +0 -1
  298. package/dist/test/system.test.d.ts +0 -2
  299. package/dist/test/system.test.d.ts.map +0 -1
  300. package/dist/test/system.test.js +0 -211
  301. package/dist/test/system.test.js.map +0 -1
  302. package/dist/test/three-reactor-gql-sync.test.d.ts +0 -2
  303. package/dist/test/three-reactor-gql-sync.test.d.ts.map +0 -1
  304. package/dist/test/three-reactor-gql-sync.test.js +0 -368
  305. package/dist/test/three-reactor-gql-sync.test.js.map +0 -1
  306. package/dist/test/two-reactor-gql-catchup-duplicate.test.d.ts +0 -2
  307. package/dist/test/two-reactor-gql-catchup-duplicate.test.d.ts.map +0 -1
  308. package/dist/test/two-reactor-gql-catchup-duplicate.test.js +0 -264
  309. package/dist/test/two-reactor-gql-catchup-duplicate.test.js.map +0 -1
  310. package/dist/test/two-reactor-gql-sync.test.d.ts +0 -2
  311. package/dist/test/two-reactor-gql-sync.test.d.ts.map +0 -1
  312. package/dist/test/two-reactor-gql-sync.test.js +0 -348
  313. package/dist/test/two-reactor-gql-sync.test.js.map +0 -1
  314. package/dist/test/utils/gql-resolver-bridge.d.ts +0 -12
  315. package/dist/test/utils/gql-resolver-bridge.d.ts.map +0 -1
  316. package/dist/test/utils/gql-resolver-bridge.js +0 -60
  317. package/dist/test/utils/gql-resolver-bridge.js.map +0 -1
  318. package/dist/test/utils.d.ts +0 -10
  319. package/dist/test/utils.d.ts.map +0 -1
  320. package/dist/test/utils.js +0 -23
  321. package/dist/test/utils.js.map +0 -1
  322. package/dist/tsconfig.tsbuildinfo +0 -1
  323. package/dist/vitest.config.d.ts +0 -3
  324. package/dist/vitest.config.d.ts.map +0 -1
  325. package/dist/vitest.config.js +0 -38
  326. package/dist/vitest.config.js.map +0 -1
@@ -1,264 +0,0 @@
1
- import { CompositeChannelFactory, ConsoleLogger, driveCollectionId, JobStatus, OperationEventTypes, 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_MODELS_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(OperationEventTypes.OPERATIONS_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(OperationEventTypes.OPERATIONS_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(OperationEventTypes.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
@@ -1 +0,0 @@
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,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=two-reactor-gql-sync.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"two-reactor-gql-sync.test.d.ts","sourceRoot":"","sources":["../../test/two-reactor-gql-sync.test.ts"],"names":[],"mappings":""}
@@ -1,348 +0,0 @@
1
- import { CompositeChannelFactory, ConsoleLogger, JobStatus, OperationEventTypes, 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_MODELS_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(OperationEventTypes.OPERATIONS_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
- 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() {
54
- const syncManagerRegistry = new Map();
55
- const resolverBridge = createResolverBridge(syncManagerRegistry);
56
- const logger = new ConsoleLogger(["test"]);
57
- const channelFactoryA = new CompositeChannelFactory(logger);
58
- const channelFactoryB = new CompositeChannelFactory(logger);
59
- const models = [
60
- driveDocumentModelModule,
61
- documentModelDocumentModelModule,
62
- ];
63
- const reactorAModule = await new ReactorBuilder()
64
- .withDocumentModels(models)
65
- .withSync(new SyncBuilder().withChannelFactory(channelFactoryA))
66
- .buildModule();
67
- const reactorA = reactorAModule.reactor;
68
- const eventBusA = reactorAModule.eventBus;
69
- const syncManagerA = reactorAModule.syncModule.syncManager;
70
- const reactorBModule = await new ReactorBuilder()
71
- .withDocumentModels(models)
72
- .withSync(new SyncBuilder().withChannelFactory(channelFactoryB))
73
- .buildModule();
74
- const reactorB = reactorBModule.reactor;
75
- const eventBusB = reactorBModule.eventBus;
76
- const syncManagerB = reactorBModule.syncModule.syncManager;
77
- syncManagerRegistry.set("reactora", syncManagerA);
78
- syncManagerRegistry.set("reactorb", syncManagerB);
79
- const filter = {
80
- documentId: [],
81
- scope: [],
82
- branch: "main",
83
- };
84
- const gqlParamsToB = {
85
- url: "http://reactorB/graphql",
86
- pollIntervalMs: 100,
87
- maxFailures: 10,
88
- retryBaseDelayMs: 50,
89
- fetchFn: resolverBridge,
90
- };
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 };
95
- }
96
- describe("Two-Reactor Sync with GqlChannel", () => {
97
- let reactorA;
98
- let reactorB;
99
- let eventBusA;
100
- let eventBusB;
101
- beforeEach(async () => {
102
- const setup = await setupTwoReactorsWithGqlChannel();
103
- reactorA = setup.reactorA;
104
- reactorB = setup.reactorB;
105
- eventBusA = setup.eventBusA;
106
- eventBusB = setup.eventBusB;
107
- });
108
- afterEach(() => {
109
- reactorA.kill();
110
- reactorB.kill();
111
- });
112
- it("should sync operation from ReactorA to ReactorB via GqlChannel", async () => {
113
- const document = driveDocumentModelModule.utils.createDocument();
114
- const readyPromise = waitForOperationsReady(eventBusB, document.header.id);
115
- const jobInfo = await reactorA.create(document);
116
- await waitForJobCompletion(reactorA, jobInfo.id);
117
- const resultA = await reactorA.getOperations(document.header.id, {
118
- branch: "main",
119
- });
120
- const opsA = Object.values(resultA).flatMap((scope) => scope.results);
121
- await readyPromise;
122
- const resultB = await reactorB.getOperations(document.header.id, {
123
- branch: "main",
124
- });
125
- const opsB = Object.values(resultB).flatMap((scope) => scope.results);
126
- expect(opsA.length).toBeGreaterThan(0);
127
- expect(opsB.length).toBe(opsA.length);
128
- for (let i = 0; i < opsA.length; i++) {
129
- expect(opsB[i]).toEqual(opsA[i]);
130
- }
131
- const docA = await reactorA.get(document.header.id, { branch: "main" });
132
- const docB = await reactorB.get(document.header.id, { branch: "main" });
133
- expect(docA.document).toEqual(docB.document);
134
- });
135
- it("should sync operation from ReactorB to ReactorA via GqlChannel", async () => {
136
- const document = driveDocumentModelModule.utils.createDocument();
137
- const readyPromise = waitForOperationsReady(eventBusA, document.header.id);
138
- const jobInfo = await reactorB.create(document);
139
- await waitForJobCompletion(reactorB, jobInfo.id);
140
- const resultB = await reactorB.getOperations(document.header.id, {
141
- branch: "main",
142
- });
143
- const opsB = Object.values(resultB).flatMap((scope) => scope.results);
144
- await readyPromise;
145
- const resultA = await reactorA.getOperations(document.header.id, {
146
- branch: "main",
147
- });
148
- const opsA = Object.values(resultA).flatMap((scope) => scope.results);
149
- expect(opsB.length).toBeGreaterThan(0);
150
- expect(opsA.length).toBe(opsB.length);
151
- for (let i = 0; i < opsB.length; i++) {
152
- expect(opsA[i]).toEqual(opsB[i]);
153
- }
154
- const docA = await reactorA.get(document.header.id, { branch: "main" });
155
- const docB = await reactorB.get(document.header.id, { branch: "main" });
156
- expect(docA.document).toEqual(docB.document);
157
- });
158
- it("should sync multiple documents with concurrent operations from both reactors", async () => {
159
- // Create 4 documents (2 for each reactor)
160
- const docA1 = driveDocumentModelModule.utils.createDocument();
161
- const docA2 = driveDocumentModelModule.utils.createDocument();
162
- const docB1 = driveDocumentModelModule.utils.createDocument();
163
- const docB2 = driveDocumentModelModule.utils.createDocument();
164
- const allDocIds = [
165
- docA1.header.id,
166
- docA2.header.id,
167
- docB1.header.id,
168
- docB2.header.id,
169
- ];
170
- // Set up listeners for docs to sync to the other reactor
171
- const readyOnB_A1 = waitForOperationsReady(eventBusB, docA1.header.id);
172
- const readyOnB_A2 = waitForOperationsReady(eventBusB, docA2.header.id);
173
- const readyOnA_B1 = waitForOperationsReady(eventBusA, docB1.header.id);
174
- const readyOnA_B2 = waitForOperationsReady(eventBusA, docB2.header.id);
175
- // Create documents on their respective reactors
176
- const [jobA1, jobA2] = await Promise.all([
177
- reactorA.create(docA1),
178
- reactorA.create(docA2),
179
- ]);
180
- const [jobB1, jobB2] = await Promise.all([
181
- reactorB.create(docB1),
182
- reactorB.create(docB2),
183
- ]);
184
- // Wait for all creates to complete on source reactors
185
- await Promise.all([
186
- waitForJobCompletion(reactorA, jobA1.id),
187
- waitForJobCompletion(reactorA, jobA2.id),
188
- waitForJobCompletion(reactorB, jobB1.id),
189
- waitForJobCompletion(reactorB, jobB2.id),
190
- ]);
191
- // Wait for all docs to sync to the other reactor
192
- await Promise.all([readyOnB_A1, readyOnB_A2, readyOnA_B1, readyOnA_B2]);
193
- // Now fire concurrent modify operations on each doc
194
- void reactorA.execute(docA1.header.id, "main", [
195
- driveDocumentModelModule.actions.setDriveName({ name: "Drive A1" }),
196
- driveDocumentModelModule.actions.addFolder({
197
- id: "folder-a1",
198
- name: "Folder A1",
199
- parentFolder: null,
200
- }),
201
- ]);
202
- void reactorA.execute(docA2.header.id, "main", [
203
- driveDocumentModelModule.actions.setDriveName({ name: "Drive A2" }),
204
- driveDocumentModelModule.actions.addFolder({
205
- id: "folder-a2",
206
- name: "Folder A2",
207
- parentFolder: null,
208
- }),
209
- ]);
210
- void reactorB.execute(docB1.header.id, "main", [
211
- driveDocumentModelModule.actions.setDriveName({ name: "Drive B1" }),
212
- driveDocumentModelModule.actions.addFolder({
213
- id: "folder-b1",
214
- name: "Folder B1",
215
- parentFolder: null,
216
- }),
217
- ]);
218
- void reactorB.execute(docB2.header.id, "main", [
219
- driveDocumentModelModule.actions.setDriveName({ name: "Drive B2" }),
220
- driveDocumentModelModule.actions.addFolder({
221
- id: "folder-b2",
222
- name: "Folder B2",
223
- parentFolder: null,
224
- }),
225
- ]);
226
- // Poll until all 4 documents are synced on both reactors
227
- const startTime = Date.now();
228
- const timeout = 25000;
229
- let synced = false;
230
- while (Date.now() - startTime < timeout) {
231
- let allDocsSynced = true;
232
- for (const docId of allDocIds) {
233
- try {
234
- const resultA = await reactorA.getOperations(docId, {
235
- branch: "main",
236
- });
237
- const opsA = Object.values(resultA).flatMap((scope) => scope.results);
238
- const resultB = await reactorB.getOperations(docId, {
239
- branch: "main",
240
- });
241
- 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
- if (opsA.length < 2 ||
245
- opsB.length < 2 ||
246
- opsA.length !== opsB.length) {
247
- allDocsSynced = false;
248
- break;
249
- }
250
- }
251
- catch {
252
- // Document may not exist on one reactor yet
253
- allDocsSynced = false;
254
- break;
255
- }
256
- }
257
- if (allDocsSynced) {
258
- synced = true;
259
- break;
260
- }
261
- await new Promise((resolve) => setTimeout(resolve, 100));
262
- }
263
- expect(synced).toBe(true);
264
- // Verify operation equality for all 4 documents
265
- for (const docId of allDocIds) {
266
- const resultA = await reactorA.getOperations(docId, { branch: "main" });
267
- const opsA = Object.values(resultA).flatMap((scope) => scope.results);
268
- const resultB = await reactorB.getOperations(docId, { branch: "main" });
269
- const opsB = Object.values(resultB).flatMap((scope) => scope.results);
270
- expect(opsA.length).toBeGreaterThan(0);
271
- expect(opsB.length).toBe(opsA.length);
272
- for (let i = 0; i < opsA.length; i++) {
273
- expect(opsB[i]).toEqual(opsA[i]);
274
- }
275
- }
276
- // Verify document state equality for all 4 documents
277
- for (const docId of allDocIds) {
278
- const docFromA = await reactorA.get(docId, { branch: "main" });
279
- const docFromB = await reactorB.get(docId, { branch: "main" });
280
- expect(docFromA.document).toEqual(docFromB.document);
281
- }
282
- }, 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);
288
- 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
- ]);
311
- const startTime = Date.now();
312
- const timeout = 15000;
313
- let synced = false;
314
- 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;
325
- break;
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]);
342
- }
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);
347
- });
348
- //# sourceMappingURL=two-reactor-gql-sync.test.js.map