@reventlessdev/reventless-local 3.0.0-alpha.113

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 (334) hide show
  1. package/CHANGELOG.md +1197 -0
  2. package/LICENSE +202 -0
  3. package/__mocks__/emptyModule.js +2 -0
  4. package/__mocks__/nodeSqlite.mjs +6 -0
  5. package/package.json +67 -0
  6. package/rescript.json +39 -0
  7. package/src/Platform.res +2162 -0
  8. package/src/Platform.res.mjs +3389 -0
  9. package/src/adapter/Api/LocalGraphQL_Adapter.res +29 -0
  10. package/src/adapter/Api/LocalGraphQL_Adapter.res.mjs +26 -0
  11. package/src/adapter/Api/LocalGraphQL_SubscriptionResolvers.res +146 -0
  12. package/src/adapter/Api/LocalGraphQL_SubscriptionResolvers.res.mjs +88 -0
  13. package/src/adapter/Auth/Auth_GraphqlContext.res +83 -0
  14. package/src/adapter/Auth/Auth_GraphqlContext.res.mjs +76 -0
  15. package/src/adapter/Auth/LocalAuth.res +239 -0
  16. package/src/adapter/Auth/LocalAuth.res.mjs +247 -0
  17. package/src/adapter/Auth/UserStore.res +134 -0
  18. package/src/adapter/Auth/UserStore.res.mjs +146 -0
  19. package/src/adapter/Backend.res +46 -0
  20. package/src/adapter/Backend.res.mjs +66 -0
  21. package/src/adapter/BackendState.res +34 -0
  22. package/src/adapter/BackendState.res.mjs +42 -0
  23. package/src/adapter/Cloner/LocalClonerRunner.res +13 -0
  24. package/src/adapter/Cloner/LocalClonerRunner.res.mjs +12 -0
  25. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res +329 -0
  26. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res.mjs +301 -0
  27. package/src/adapter/CommandGenerator/InboundTranslationResolvers_GraphQL.res +76 -0
  28. package/src/adapter/CommandGenerator/InboundTranslationResolvers_GraphQL.res.mjs +57 -0
  29. package/src/adapter/CommandGenerator/LocalCommandGeneratorResolvers.res +19 -0
  30. package/src/adapter/CommandGenerator/LocalCommandGeneratorResolvers.res.mjs +19 -0
  31. package/src/adapter/CommandTopic/LocalCommandTopicChannel.res +104 -0
  32. package/src/adapter/CommandTopic/LocalCommandTopicChannel.res.mjs +82 -0
  33. package/src/adapter/CommandTopic/LocalCommandTopicRemoteChannel.res +32 -0
  34. package/src/adapter/CommandTopic/LocalCommandTopicRemoteChannel.res.mjs +38 -0
  35. package/src/adapter/Counter/LocalCounterHandler.res +42 -0
  36. package/src/adapter/Counter/LocalCounterHandler.res.mjs +45 -0
  37. package/src/adapter/DcbEventLog/DcbEventLogStorage_InMemory.res +131 -0
  38. package/src/adapter/DcbEventLog/DcbEventLogStorage_InMemory.res.mjs +140 -0
  39. package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res +334 -0
  40. package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res.mjs +299 -0
  41. package/src/adapter/DomainGraphQL_Server.res +565 -0
  42. package/src/adapter/DomainGraphQL_Server.res.mjs +576 -0
  43. package/src/adapter/DomainMCP_Server.res +369 -0
  44. package/src/adapter/DomainMCP_Server.res.mjs +358 -0
  45. package/src/adapter/EventCollector/LocalEventCollectorChannel.res +87 -0
  46. package/src/adapter/EventCollector/LocalEventCollectorChannel.res.mjs +45 -0
  47. package/src/adapter/EventLog/EventLogStorage_InMemory.res +105 -0
  48. package/src/adapter/EventLog/EventLogStorage_InMemory.res.mjs +117 -0
  49. package/src/adapter/EventLog/EventLogStorage_Sqlite.res +145 -0
  50. package/src/adapter/EventLog/EventLogStorage_Sqlite.res.mjs +155 -0
  51. package/src/adapter/EventTopic/LocalEventTopicPublisher.res +36 -0
  52. package/src/adapter/EventTopic/LocalEventTopicPublisher.res.mjs +31 -0
  53. package/src/adapter/GraphQL_Server.res +6 -0
  54. package/src/adapter/GraphQL_Server.res.mjs +154 -0
  55. package/src/adapter/GraphQL_ServerInstance.res +310 -0
  56. package/src/adapter/GraphQL_ServerInstance.res.mjs +257 -0
  57. package/src/adapter/Heartbeat/LocalHeartbeatRunner.res +57 -0
  58. package/src/adapter/Heartbeat/LocalHeartbeatRunner.res.mjs +34 -0
  59. package/src/adapter/LocalBus.res +498 -0
  60. package/src/adapter/LocalBus.res.mjs +1011 -0
  61. package/src/adapter/LocalPluginSpec.res +16 -0
  62. package/src/adapter/LocalPluginSpec.res.mjs +33 -0
  63. package/src/adapter/LocalSideEffectHandler.res +44 -0
  64. package/src/adapter/LocalSideEffectHandler.res.mjs +34 -0
  65. package/src/adapter/MCP_Server.res +4 -0
  66. package/src/adapter/MCP_Server.res.mjs +67 -0
  67. package/src/adapter/MCP_ServerInstance.res +362 -0
  68. package/src/adapter/MCP_ServerInstance.res.mjs +313 -0
  69. package/src/adapter/PlatformGraphQL_Server.res +75 -0
  70. package/src/adapter/PlatformGraphQL_Server.res.mjs +84 -0
  71. package/src/adapter/PlatformMCP_Server.res +23 -0
  72. package/src/adapter/PlatformMCP_Server.res.mjs +46 -0
  73. package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res +793 -0
  74. package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res.mjs +569 -0
  75. package/src/adapter/QueryDb/QueryDbStorage_InMemory.res +242 -0
  76. package/src/adapter/QueryDb/QueryDbStorage_InMemory.res.mjs +241 -0
  77. package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res +339 -0
  78. package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res.mjs +318 -0
  79. package/src/adapter/QueryDb/SortKey_Filter.res +48 -0
  80. package/src/adapter/QueryDb/SortKey_Filter.res.mjs +46 -0
  81. package/src/adapter/QueryEngine/LocalQueryEngine.res +57 -0
  82. package/src/adapter/QueryEngine/LocalQueryEngine.res.mjs +56 -0
  83. package/src/adapter/Runtime/LocalAggregateRuntime_Builder.res +91 -0
  84. package/src/adapter/Runtime/LocalAggregateRuntime_Builder.res.mjs +50 -0
  85. package/src/adapter/Runtime/LocalEventCollectorRuntime_Builder.res +47 -0
  86. package/src/adapter/Runtime/LocalEventCollectorRuntime_Builder.res.mjs +37 -0
  87. package/src/adapter/Runtime/LocalPluginRuntime_Builder.res +11 -0
  88. package/src/adapter/Runtime/LocalPluginRuntime_Builder.res.mjs +30 -0
  89. package/src/adapter/Runtime/LocalRuntimeEnvironment.res +54 -0
  90. package/src/adapter/Runtime/LocalRuntimeEnvironment.res.mjs +40 -0
  91. package/src/adapter/Scheduler/LocalScheduledPublisher.res +102 -0
  92. package/src/adapter/Scheduler/LocalScheduledPublisher.res.mjs +84 -0
  93. package/src/adapter/SqliteDriver.res +52 -0
  94. package/src/adapter/SqliteDriver.res.mjs +76 -0
  95. package/src/adapter/SqliteDriver.resi +31 -0
  96. package/src/adapter/Task/LocalTaskBucket.res +75 -0
  97. package/src/adapter/Task/LocalTaskBucket.res.mjs +48 -0
  98. package/src/adapter/Task/TaskBucket_Sqlite.res +47 -0
  99. package/src/adapter/Task/TaskBucket_Sqlite.res.mjs +40 -0
  100. package/src/components/Aggregate_Builder.res +55 -0
  101. package/src/components/Aggregate_Builder.res.mjs +140 -0
  102. package/src/components/AutomationSlice_Builder.res +41 -0
  103. package/src/components/AutomationSlice_Builder.res.mjs +57 -0
  104. package/src/components/Counter_Builder.res +14 -0
  105. package/src/components/Counter_Builder.res.mjs +32 -0
  106. package/src/components/DcbEventLog_Builder.res +18 -0
  107. package/src/components/DcbEventLog_Builder.res.mjs +24 -0
  108. package/src/components/ExtensionPoint_Builder.res +25 -0
  109. package/src/components/ExtensionPoint_Builder.res.mjs +44 -0
  110. package/src/components/InboundTranslationSlice_Builder.res +34 -0
  111. package/src/components/InboundTranslationSlice_Builder.res.mjs +45 -0
  112. package/src/components/OutboundTranslationSlice_Builder.res +43 -0
  113. package/src/components/OutboundTranslationSlice_Builder.res.mjs +59 -0
  114. package/src/components/Plugin_Builder.res +37 -0
  115. package/src/components/Plugin_Builder.res.mjs +67 -0
  116. package/src/components/ReadModel_Builder.res +47 -0
  117. package/src/components/ReadModel_Builder.res.mjs +54 -0
  118. package/src/components/StateChangeSlice_Builder.res +19 -0
  119. package/src/components/StateChangeSlice_Builder.res.mjs +21 -0
  120. package/src/components/StateViewSlice_Builder.res +42 -0
  121. package/src/components/StateViewSlice_Builder.res.mjs +58 -0
  122. package/src/components/Task_Builder.res +28 -0
  123. package/src/components/Task_Builder.res.mjs +41 -0
  124. package/src/reexport/CommandTopic.res +3 -0
  125. package/src/reexport/CommandTopic.res.mjs +55 -0
  126. package/src/test/Mocks/MockDcbEventLogStorage.res +136 -0
  127. package/src/test/Mocks/MockDcbEventLogStorage.res.mjs +146 -0
  128. package/src/test/Mocks/MockEventLogStorage.res +57 -0
  129. package/src/test/Mocks/MockEventLogStorage.res.mjs +65 -0
  130. package/src/test/Mocks/MockPublisher.res +29 -0
  131. package/src/test/Mocks/MockPublisher.res.mjs +28 -0
  132. package/src/test/Mocks/MockQueryDbStorage.res +104 -0
  133. package/src/test/Mocks/MockQueryDbStorage.res.mjs +136 -0
  134. package/src/test/TestRunner.res +96 -0
  135. package/src/test/TestRunner.res.mjs +71 -0
  136. package/tests/SplitApiFixtures.res +84 -0
  137. package/tests/SplitApiFixtures.res.mjs +88 -0
  138. package/tests/SplitApiTest.res +102 -0
  139. package/tests/SplitApiTest.res.mjs +82 -0
  140. package/tests/TestFixtures.res +54 -0
  141. package/tests/TestFixtures.res.mjs +65 -0
  142. package/tests/adapter/BackendParityTest.res +94 -0
  143. package/tests/adapter/BackendParityTest.res.mjs +96 -0
  144. package/tests/adapter/CollectNEventsTest.res +82 -0
  145. package/tests/adapter/CollectNEventsTest.res.mjs +83 -0
  146. package/tests/adapter/CommandAuthorizationTest.res +200 -0
  147. package/tests/adapter/CommandAuthorizationTest.res.mjs +190 -0
  148. package/tests/adapter/CommandTopicChannelTest.res +174 -0
  149. package/tests/adapter/CommandTopicChannelTest.res.mjs +170 -0
  150. package/tests/adapter/CounterHandlerTest.res +113 -0
  151. package/tests/adapter/CounterHandlerTest.res.mjs +96 -0
  152. package/tests/adapter/DcbEventLogStorageSqliteTest.res +157 -0
  153. package/tests/adapter/DcbEventLogStorageSqliteTest.res.mjs +165 -0
  154. package/tests/adapter/DcbEventLogStorageTest.res +187 -0
  155. package/tests/adapter/DcbEventLogStorageTest.res.mjs +195 -0
  156. package/tests/adapter/EventCollectorChannelTest.res +166 -0
  157. package/tests/adapter/EventCollectorChannelTest.res.mjs +178 -0
  158. package/tests/adapter/EventLogStorageSqliteTest.res +105 -0
  159. package/tests/adapter/EventLogStorageSqliteTest.res.mjs +116 -0
  160. package/tests/adapter/EventTapTest.res +102 -0
  161. package/tests/adapter/EventTapTest.res.mjs +94 -0
  162. package/tests/adapter/EventTopicPublisherTest.res +76 -0
  163. package/tests/adapter/EventTopicPublisherTest.res.mjs +86 -0
  164. package/tests/adapter/GraphQL_SchemaInspectorTest.res +763 -0
  165. package/tests/adapter/GraphQL_SchemaInspectorTest.res.mjs +578 -0
  166. package/tests/adapter/GraphQL_SubscriptionResolversTest.res +161 -0
  167. package/tests/adapter/GraphQL_SubscriptionResolversTest.res.mjs +134 -0
  168. package/tests/adapter/HeartbeatRunnerTest.res +136 -0
  169. package/tests/adapter/HeartbeatRunnerTest.res.mjs +96 -0
  170. package/tests/adapter/LocalAuthHttpTest.res +224 -0
  171. package/tests/adapter/LocalAuthHttpTest.res.mjs +237 -0
  172. package/tests/adapter/LocalAuthLoginTest.res +180 -0
  173. package/tests/adapter/LocalAuthLoginTest.res.mjs +207 -0
  174. package/tests/adapter/LocalAuthTest.res +106 -0
  175. package/tests/adapter/LocalAuthTest.res.mjs +145 -0
  176. package/tests/adapter/LocalAuthUserStoreTest.res +172 -0
  177. package/tests/adapter/LocalAuthUserStoreTest.res.mjs +208 -0
  178. package/tests/adapter/LocalBusBoundedTest.res +157 -0
  179. package/tests/adapter/LocalBusBoundedTest.res.mjs +168 -0
  180. package/tests/adapter/LocalBusPubSubTest.res +81 -0
  181. package/tests/adapter/LocalBusPubSubTest.res.mjs +81 -0
  182. package/tests/adapter/QueryDbGsiTtlTest.res +268 -0
  183. package/tests/adapter/QueryDbGsiTtlTest.res.mjs +209 -0
  184. package/tests/adapter/QueryDbListResolverTest.res +361 -0
  185. package/tests/adapter/QueryDbListResolverTest.res.mjs +426 -0
  186. package/tests/adapter/QueryDbStorageSqliteTest.res +159 -0
  187. package/tests/adapter/QueryDbStorageSqliteTest.res.mjs +126 -0
  188. package/tests/adapter/QueryDbStorageTest.res +171 -0
  189. package/tests/adapter/QueryDbStorageTest.res.mjs +170 -0
  190. package/tests/adapter/QueryEngineTest.res +152 -0
  191. package/tests/adapter/QueryEngineTest.res.mjs +148 -0
  192. package/tests/adapter/ScheduledPublisherTest.res +199 -0
  193. package/tests/adapter/ScheduledPublisherTest.res.mjs +203 -0
  194. package/tests/adapter/SortKey_FilterTest.res +103 -0
  195. package/tests/adapter/SortKey_FilterTest.res.mjs +135 -0
  196. package/tests/adapter/SqliteDriverTest.res +86 -0
  197. package/tests/adapter/SqliteDriverTest.res.mjs +101 -0
  198. package/tests/adapter/TaskBucketSqliteTest.res +44 -0
  199. package/tests/adapter/TaskBucketSqliteTest.res.mjs +40 -0
  200. package/tests/adapter/TaskBucketTest.res +106 -0
  201. package/tests/adapter/TaskBucketTest.res.mjs +118 -0
  202. package/tests/components/aggregate/AggregateFixtures.res +78 -0
  203. package/tests/components/aggregate/AggregateFixtures.res.mjs +109 -0
  204. package/tests/components/aggregate/AggregateTest.res +129 -0
  205. package/tests/components/aggregate/AggregateTest.res.mjs +129 -0
  206. package/tests/components/automationslice/AutomationSliceCallbackTest.res +212 -0
  207. package/tests/components/automationslice/AutomationSliceCallbackTest.res.mjs +233 -0
  208. package/tests/components/automationslice/AutomationSliceFixtures.res +189 -0
  209. package/tests/components/automationslice/AutomationSliceFixtures.res.mjs +276 -0
  210. package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res +289 -0
  211. package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res.mjs +453 -0
  212. package/tests/components/automationslice/AutomationSliceSelfDeadlockTest.res +47 -0
  213. package/tests/components/automationslice/AutomationSliceSelfDeadlockTest.res.mjs +31 -0
  214. package/tests/components/automationslice/MixedSourceAutomationSliceFixtures.res +213 -0
  215. package/tests/components/automationslice/MixedSourceAutomationSliceFixtures.res.mjs +268 -0
  216. package/tests/components/automationslice/MixedSourceAutomationSliceTest.res +118 -0
  217. package/tests/components/automationslice/MixedSourceAutomationSliceTest.res.mjs +99 -0
  218. package/tests/components/commandgenerator/CommandGeneratorFixtures.res +73 -0
  219. package/tests/components/commandgenerator/CommandGeneratorFixtures.res.mjs +109 -0
  220. package/tests/components/commandgenerator/CommandGeneratorTest.res +43 -0
  221. package/tests/components/commandgenerator/CommandGeneratorTest.res.mjs +46 -0
  222. package/tests/components/commandtopic/CommandTopicFixtures.res +37 -0
  223. package/tests/components/commandtopic/CommandTopicFixtures.res.mjs +68 -0
  224. package/tests/components/commandtopic/CommandTopicStreamFixtures.res +43 -0
  225. package/tests/components/commandtopic/CommandTopicStreamFixtures.res.mjs +91 -0
  226. package/tests/components/commandtopic/CommandTopicStreamTest.res +30 -0
  227. package/tests/components/commandtopic/CommandTopicStreamTest.res.mjs +35 -0
  228. package/tests/components/commandtopic/CommandTopicTest.res +55 -0
  229. package/tests/components/commandtopic/CommandTopicTest.res.mjs +62 -0
  230. package/tests/components/counter/CounterFixtures.res +42 -0
  231. package/tests/components/counter/CounterFixtures.res.mjs +34 -0
  232. package/tests/components/counter/CounterTest.res +46 -0
  233. package/tests/components/counter/CounterTest.res.mjs +61 -0
  234. package/tests/components/dcb/DcbCrossPartitionFixtures.res +170 -0
  235. package/tests/components/dcb/DcbCrossPartitionFixtures.res.mjs +276 -0
  236. package/tests/components/dcb/DcbCrossPartitionReadTest.res +68 -0
  237. package/tests/components/dcb/DcbCrossPartitionReadTest.res.mjs +78 -0
  238. package/tests/components/dcb/DcbEventLogAppendStreamTest.res +72 -0
  239. package/tests/components/dcb/DcbEventLogAppendStreamTest.res.mjs +82 -0
  240. package/tests/components/dcb/DcbEventLogStreamTest.res +103 -0
  241. package/tests/components/dcb/DcbEventLogStreamTest.res.mjs +91 -0
  242. package/tests/components/dcb/DcbFixtures.res +145 -0
  243. package/tests/components/dcb/DcbFixtures.res.mjs +221 -0
  244. package/tests/components/dcb/DcbTest.res +55 -0
  245. package/tests/components/dcb/DcbTest.res.mjs +55 -0
  246. package/tests/components/eventcollector/EventCollectorFixtures.res +42 -0
  247. package/tests/components/eventcollector/EventCollectorFixtures.res.mjs +49 -0
  248. package/tests/components/eventcollector/EventCollectorTest.res +37 -0
  249. package/tests/components/eventcollector/EventCollectorTest.res.mjs +36 -0
  250. package/tests/components/eventlog/EventLogAppendStreamTest.res +93 -0
  251. package/tests/components/eventlog/EventLogAppendStreamTest.res.mjs +113 -0
  252. package/tests/components/eventlog/EventLogConflictTest.res +86 -0
  253. package/tests/components/eventlog/EventLogConflictTest.res.mjs +81 -0
  254. package/tests/components/eventlog/EventLogFixtures.res +52 -0
  255. package/tests/components/eventlog/EventLogFixtures.res.mjs +80 -0
  256. package/tests/components/eventlog/EventLogStreamTest.res +98 -0
  257. package/tests/components/eventlog/EventLogStreamTest.res.mjs +142 -0
  258. package/tests/components/eventlog/EventLogTest.res +53 -0
  259. package/tests/components/eventlog/EventLogTest.res.mjs +74 -0
  260. package/tests/components/eventtopic/EventTopicFixtures.res +47 -0
  261. package/tests/components/eventtopic/EventTopicFixtures.res.mjs +77 -0
  262. package/tests/components/eventtopic/EventTopicStreamFixtures.res +27 -0
  263. package/tests/components/eventtopic/EventTopicStreamFixtures.res.mjs +62 -0
  264. package/tests/components/eventtopic/EventTopicStreamTest.res +35 -0
  265. package/tests/components/eventtopic/EventTopicStreamTest.res.mjs +44 -0
  266. package/tests/components/eventtopic/EventTopicTest.res +35 -0
  267. package/tests/components/eventtopic/EventTopicTest.res.mjs +43 -0
  268. package/tests/components/extensionpoint/ExtensionPointFixtures.res +145 -0
  269. package/tests/components/extensionpoint/ExtensionPointFixtures.res.mjs +188 -0
  270. package/tests/components/extensionpoint/ExtensionPointTest.res +62 -0
  271. package/tests/components/extensionpoint/ExtensionPointTest.res.mjs +55 -0
  272. package/tests/components/heartbeat/HeartbeatFixtures.res +48 -0
  273. package/tests/components/heartbeat/HeartbeatFixtures.res.mjs +46 -0
  274. package/tests/components/heartbeat/HeartbeatTest.res +67 -0
  275. package/tests/components/heartbeat/HeartbeatTest.res.mjs +65 -0
  276. package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res +241 -0
  277. package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res.mjs +239 -0
  278. package/tests/components/inboundtranslationslice/InboundTranslationSliceFixtures.res +36 -0
  279. package/tests/components/inboundtranslationslice/InboundTranslationSliceFixtures.res.mjs +73 -0
  280. package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res +191 -0
  281. package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res.mjs +297 -0
  282. package/tests/components/outboundtranslationslice/OutboundTranslationSliceFixtures.res +78 -0
  283. package/tests/components/outboundtranslationslice/OutboundTranslationSliceFixtures.res.mjs +137 -0
  284. package/tests/components/querydb/QueryDbFixtures.res +39 -0
  285. package/tests/components/querydb/QueryDbFixtures.res.mjs +73 -0
  286. package/tests/components/querydb/QueryDbSubIdFixtures.res +44 -0
  287. package/tests/components/querydb/QueryDbSubIdFixtures.res.mjs +80 -0
  288. package/tests/components/querydb/QueryDbSubIdTest.res +104 -0
  289. package/tests/components/querydb/QueryDbSubIdTest.res.mjs +104 -0
  290. package/tests/components/querydb/QueryDbTest.res +81 -0
  291. package/tests/components/querydb/QueryDbTest.res.mjs +126 -0
  292. package/tests/components/readmodel/DcbReadModelE2EFixtures.res +215 -0
  293. package/tests/components/readmodel/DcbReadModelE2EFixtures.res.mjs +308 -0
  294. package/tests/components/readmodel/DcbReadModelE2ETest.res +45 -0
  295. package/tests/components/readmodel/DcbReadModelE2ETest.res.mjs +37 -0
  296. package/tests/components/readmodel/DcbReadModelTypoFailFastTest.res +88 -0
  297. package/tests/components/readmodel/DcbReadModelTypoFailFastTest.res.mjs +152 -0
  298. package/tests/components/readmodel/MixedSourceReadModelFixtures.res +181 -0
  299. package/tests/components/readmodel/MixedSourceReadModelFixtures.res.mjs +277 -0
  300. package/tests/components/readmodel/MixedSourceReadModelTest.res +61 -0
  301. package/tests/components/readmodel/MixedSourceReadModelTest.res.mjs +68 -0
  302. package/tests/components/readmodel/ReadModelFixtures.res +138 -0
  303. package/tests/components/readmodel/ReadModelFixtures.res.mjs +169 -0
  304. package/tests/components/readmodel/ReadModelTest.res +46 -0
  305. package/tests/components/readmodel/ReadModelTest.res.mjs +36 -0
  306. package/tests/components/scheduler/SchedulerFixtures.res +59 -0
  307. package/tests/components/scheduler/SchedulerFixtures.res.mjs +63 -0
  308. package/tests/components/scheduler/SchedulerTest.res +76 -0
  309. package/tests/components/scheduler/SchedulerTest.res.mjs +86 -0
  310. package/tests/components/sideeffecthandler/SideEffectHandlerFixtures.res +133 -0
  311. package/tests/components/sideeffecthandler/SideEffectHandlerFixtures.res.mjs +149 -0
  312. package/tests/components/sideeffecthandler/SideEffectHandlerTest.res +39 -0
  313. package/tests/components/sideeffecthandler/SideEffectHandlerTest.res.mjs +28 -0
  314. package/tests/components/stateviewslice/StateViewSliceFixtures.res +117 -0
  315. package/tests/components/stateviewslice/StateViewSliceFixtures.res.mjs +188 -0
  316. package/tests/components/stateviewslice/StateViewSliceSubIdFixtures.res +117 -0
  317. package/tests/components/stateviewslice/StateViewSliceSubIdFixtures.res.mjs +183 -0
  318. package/tests/components/stateviewslice/StateViewSliceSubIdTest.res +78 -0
  319. package/tests/components/stateviewslice/StateViewSliceSubIdTest.res.mjs +107 -0
  320. package/tests/components/stateviewslice/StateViewSliceTest.res +59 -0
  321. package/tests/components/stateviewslice/StateViewSliceTest.res.mjs +72 -0
  322. package/tests/components/task/TaskFixtures.res +87 -0
  323. package/tests/components/task/TaskFixtures.res.mjs +82 -0
  324. package/tests/components/task/TaskTest.res +98 -0
  325. package/tests/components/task/TaskTest.res.mjs +46 -0
  326. package/tests/plugin/PluginBehavior_GWT.res +146 -0
  327. package/tests/plugin/PluginBehavior_GWT.res.mjs +281 -0
  328. package/tests/plugin/Plugin_Fixtures.res +61 -0
  329. package/tests/plugin/Plugin_Fixtures.res.mjs +105 -0
  330. package/tests/plugin/PluginsProjection_GWT.res +95 -0
  331. package/tests/plugin/PluginsProjection_GWT.res.mjs +120 -0
  332. package/tests/plugin/UIFragmentRegistryProjection_GWT.res +72 -0
  333. package/tests/plugin/UIFragmentRegistryProjection_GWT.res.mjs +117 -0
  334. package/tests/setup/sqliteGlobal.cjs +12 -0
@@ -0,0 +1,200 @@
1
+ // Resolver-boundary tests for per-constructor authorization (A3.3 + A3.3b).
2
+ // Mirrors the catalog Category aggregate's command shape: a record-payload
3
+ // constructor `Add({name})` defaulting to AllowAuthenticated, plus a
4
+ // payload-less `Archive` constrained to AllowGroups(["Admin"]).
5
+ //
6
+ // The PPX emits a `commandAuthorization` switch at compile time (see
7
+ // examples/online-shop-hybrid/catalog/.../Category.res.mjs). This test hand-
8
+ // writes an equivalent switch and feeds it through the live resolver path
9
+ // (CommandGeneratorResolvers_GraphQL.register), so the test covers the same
10
+ // runtime call shape that PPX-generated code produces.
11
+
12
+ @@warning("-44")
13
+
14
+ open JestGlobals
15
+
16
+ let _ = TestRunner.setup()
17
+
18
+ // ── Synthetic command schema mirroring Category ──────────────────────────────
19
+
20
+ @schema
21
+ type command =
22
+ | Add({name: string})
23
+ | Rename({name: string})
24
+ | Archive
25
+
26
+ // Matches the shape the PPX emits for `@authorize(AllowGroups(["Admin"]))` on
27
+ // the payload-less `Archive` constructor with a file-level default.
28
+ let commandAuthorization = (cmd: unknown): Reventless.Authorization.permission =>
29
+ if (cmd->Obj.magic: 'a) === "Archive" {
30
+ AllowGroups(["Admin"])
31
+ } else {
32
+ AllowAuthenticated
33
+ }
34
+
35
+ // ── Identity fixtures ────────────────────────────────────────────────────────
36
+
37
+ let adminIdentity: Reventless.Identity.t = {
38
+ userId: "u-admin",
39
+ username: "admin",
40
+ groups: ["Admin", "User"],
41
+ provider: InMemory,
42
+ }
43
+
44
+ let userIdentity: Reventless.Identity.t = {
45
+ userId: "u-user",
46
+ username: "user",
47
+ groups: ["User"],
48
+ provider: InMemory,
49
+ }
50
+
51
+ let ctxFor = (identity: Reventless.Identity.t): JSON.t =>
52
+ JSON.Encode.object(
53
+ Dict.fromArray([("identity", (identity: Reventless.Identity.t)->Obj.magic)]),
54
+ )
55
+
56
+ let anonymousCtx: JSON.t =
57
+ JSON.Encode.object(
58
+ Dict.fromArray([("identity", (Reventless.Identity.anonymous: Reventless.Identity.t)->Obj.magic)]),
59
+ )
60
+
61
+ let getTypename = (response: JSON.t): string =>
62
+ response
63
+ ->JSON.Decode.object
64
+ ->Option.flatMap(d => d->Dict.get("__typename"))
65
+ ->Option.flatMap(JSON.Decode.string)
66
+ ->Option.getOr("")
67
+
68
+ let getErrorCode = (response: JSON.t): string =>
69
+ response
70
+ ->JSON.Decode.object
71
+ ->Option.flatMap(d => d->Dict.get("errorCode"))
72
+ ->Option.flatMap(JSON.Decode.string)
73
+ ->Option.getOr("")
74
+
75
+ // ── Per-test fixture — fresh server + handler + resolvers ────────────────────
76
+
77
+ let buildFixture = (~namespace: string) => {
78
+ // Use the domain server (a singleton) and reset it before each test so
79
+ // mutation registrations don't leak across cases.
80
+ let server = DomainGraphQL_Server.asInterface
81
+ let calls: ref<array<string>> = ref([])
82
+
83
+ let addField = `${namespace}_Add`
84
+ let renameField = `${namespace}_Rename`
85
+ let archiveField = `${namespace}_Archive`
86
+
87
+ CommandGeneratorResolvers_GraphQL.register(
88
+ ~fields=[addField, renameField, archiveField],
89
+ ~commandSchema=commandSchema->S.castToUnknown,
90
+ ~commandAuthorization,
91
+ ~server,
92
+ )
93
+
94
+ // Stub generateCommand: record the command name + return Accepted.
95
+ let stubGenerateCommand: ReventlessCore.CommandGenerator.commandGenerator = payload =>
96
+ Effect.promise(() => {
97
+ calls.contents->Array.push(payload.command)
98
+ Promise.resolve(
99
+ ReventlessCore.CommandTopic.Accepted({
100
+ msgId: "msg-" ++ payload.command,
101
+ eventCount: 1,
102
+ }),
103
+ )
104
+ })
105
+
106
+ [addField, renameField, archiveField]->Array.forEach(field =>
107
+ CommandGeneratorResolvers_GraphQL.bindHandler(~field, ~generateCommand=stubGenerateCommand)
108
+ )
109
+
110
+ let resolverFor = field =>
111
+ switch server.getMutationResolver(field) {
112
+ | Some(r) => r
113
+ | None => JsError.throwWithMessage("resolver not registered: " ++ field)
114
+ }
115
+
116
+ (resolverFor(addField), resolverFor(renameField), resolverFor(archiveField), calls)
117
+ }
118
+
119
+ // ── Tests ────────────────────────────────────────────────────────────────────
120
+
121
+ describe("CommandGeneratorResolvers_GraphQL — per-constructor authorization", () => {
122
+ beforeEach(() => {
123
+ DomainGraphQL_Server.asInterface.reset()
124
+ })
125
+
126
+ testPromise("admin invoking payload-less Archive succeeds", async () => {
127
+ let (_, _, archiveResolver, calls) = buildFixture(~namespace="Cat1")
128
+ let response =
129
+ await archiveResolver(JSON.Encode.null, JSON.Encode.object(Dict.make()), ctxFor(adminIdentity))
130
+ expect(getTypename(response))->toEqual("CommandAccepted")
131
+ expect(calls.contents)->toEqual(["Archive"])
132
+ })
133
+
134
+ testPromise("regular user invoking Archive is rejected with Forbidden", async () => {
135
+ let (_, _, archiveResolver, calls) = buildFixture(~namespace="Cat2")
136
+ let response =
137
+ await archiveResolver(JSON.Encode.null, JSON.Encode.object(Dict.make()), ctxFor(userIdentity))
138
+ expect(getTypename(response))->toEqual("CommandRejected")
139
+ expect(getErrorCode(response))->toEqual("Forbidden")
140
+ expect(calls.contents)->toEqual([])
141
+ })
142
+
143
+ testPromise("anonymous invoking Archive is rejected with Forbidden", async () => {
144
+ let (_, _, archiveResolver, calls) = buildFixture(~namespace="Cat3")
145
+ let response =
146
+ await archiveResolver(JSON.Encode.null, JSON.Encode.object(Dict.make()), anonymousCtx)
147
+ expect(getTypename(response))->toEqual("CommandRejected")
148
+ expect(getErrorCode(response))->toEqual("Forbidden")
149
+ expect(calls.contents)->toEqual([])
150
+ })
151
+
152
+ testPromise("regular user invoking Add (default AllowAuthenticated) succeeds", async () => {
153
+ let (addResolver, _, _, calls) = buildFixture(~namespace="Cat4")
154
+ let response =
155
+ await addResolver(
156
+ JSON.Encode.null,
157
+ JSON.Encode.object(Dict.fromArray([("name", JSON.Encode.string("Books"))])),
158
+ ctxFor(userIdentity),
159
+ )
160
+ expect(getTypename(response))->toEqual("CommandAccepted")
161
+ expect(calls.contents)->toEqual(["Add"])
162
+ })
163
+
164
+ testPromise("anonymous invoking Add is rejected (AllowAuthenticated default)", async () => {
165
+ let (addResolver, _, _, calls) = buildFixture(~namespace="Cat5")
166
+ let response =
167
+ await addResolver(
168
+ JSON.Encode.null,
169
+ JSON.Encode.object(Dict.fromArray([("name", JSON.Encode.string("Books"))])),
170
+ anonymousCtx,
171
+ )
172
+ expect(getTypename(response))->toEqual("CommandRejected")
173
+ expect(getErrorCode(response))->toEqual("Forbidden")
174
+ expect(calls.contents)->toEqual([])
175
+ })
176
+
177
+ testPromise("admin invoking Add also succeeds (admin has User group too)", async () => {
178
+ let (addResolver, _, _, calls) = buildFixture(~namespace="Cat6")
179
+ let response =
180
+ await addResolver(
181
+ JSON.Encode.null,
182
+ JSON.Encode.object(Dict.fromArray([("name", JSON.Encode.string("Books"))])),
183
+ ctxFor(adminIdentity),
184
+ )
185
+ expect(getTypename(response))->toEqual("CommandAccepted")
186
+ expect(calls.contents)->toEqual(["Add"])
187
+ })
188
+
189
+ testPromise(
190
+ "payload-less Archive registered as a GraphQL mutation field (no longer filtered)",
191
+ async () => {
192
+ let (_, _, _, _) = buildFixture(~namespace="Cat7")
193
+ let sdl = DomainGraphQL_Server.asInterface.buildSdl()
194
+ // Three mutation fields should now be present, including Archive.
195
+ expect(sdl->String.includes("Cat7_Add"))->toEqual(true)
196
+ expect(sdl->String.includes("Cat7_Rename"))->toEqual(true)
197
+ expect(sdl->String.includes("Cat7_Archive"))->toEqual(true)
198
+ },
199
+ )
200
+ })
@@ -0,0 +1,190 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
5
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
+ import * as Effect from "effect/Effect";
7
+ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
8
+ import * as Identity$Reventless from "@reventlessdev/reventless-spec/src/types/Identity.res.mjs";
9
+ import * as TestRunner$ReventlessLocal from "../../src/test/TestRunner.res.mjs";
10
+ import * as DomainGraphQL_Server$ReventlessLocal from "../../src/adapter/DomainGraphQL_Server.res.mjs";
11
+ import * as CommandGeneratorResolvers_GraphQL$ReventlessLocal from "../../src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res.mjs";
12
+
13
+ TestRunner$ReventlessLocal.setup();
14
+
15
+ let commandSchema = S.union([
16
+ S.schema(s => ({
17
+ TAG: "Add",
18
+ name: s.m(S.string)
19
+ })),
20
+ S.schema(s => ({
21
+ TAG: "Rename",
22
+ name: s.m(S.string)
23
+ })),
24
+ S.literal("Archive")
25
+ ]);
26
+
27
+ function commandAuthorization(cmd) {
28
+ if (cmd === "Archive") {
29
+ return {
30
+ TAG: "AllowGroups",
31
+ _0: ["Admin"]
32
+ };
33
+ } else {
34
+ return "AllowAuthenticated";
35
+ }
36
+ }
37
+
38
+ let adminIdentity_groups = [
39
+ "Admin",
40
+ "User"
41
+ ];
42
+
43
+ let adminIdentity = {
44
+ userId: "u-admin",
45
+ username: "admin",
46
+ groups: adminIdentity_groups,
47
+ provider: "InMemory"
48
+ };
49
+
50
+ let userIdentity_groups = ["User"];
51
+
52
+ let userIdentity = {
53
+ userId: "u-user",
54
+ username: "user",
55
+ groups: userIdentity_groups,
56
+ provider: "InMemory"
57
+ };
58
+
59
+ function ctxFor(identity) {
60
+ return Object.fromEntries([[
61
+ "identity",
62
+ identity
63
+ ]]);
64
+ }
65
+
66
+ let anonymousCtx = Object.fromEntries([[
67
+ "identity",
68
+ Identity$Reventless.anonymous
69
+ ]]);
70
+
71
+ function getTypename(response) {
72
+ return Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(response), d => d["__typename"]), Stdlib_JSON.Decode.string), "");
73
+ }
74
+
75
+ function getErrorCode(response) {
76
+ return Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(response), d => d["errorCode"]), Stdlib_JSON.Decode.string), "");
77
+ }
78
+
79
+ function buildFixture(namespace) {
80
+ let calls = {
81
+ contents: []
82
+ };
83
+ let addField = namespace + `_Add`;
84
+ let renameField = namespace + `_Rename`;
85
+ let archiveField = namespace + `_Archive`;
86
+ CommandGeneratorResolvers_GraphQL$ReventlessLocal.register([
87
+ addField,
88
+ renameField,
89
+ archiveField
90
+ ], commandSchema, commandAuthorization, DomainGraphQL_Server$ReventlessLocal.asInterface);
91
+ let stubGenerateCommand = payload => Effect.promise(() => {
92
+ calls.contents.push(payload.command);
93
+ return Promise.resolve({
94
+ TAG: "Accepted",
95
+ msgId: "msg-" + payload.command,
96
+ eventCount: 1
97
+ });
98
+ });
99
+ [
100
+ addField,
101
+ renameField,
102
+ archiveField
103
+ ].forEach(field => CommandGeneratorResolvers_GraphQL$ReventlessLocal.bindHandler(field, stubGenerateCommand));
104
+ let resolverFor = field => {
105
+ let r = DomainGraphQL_Server$ReventlessLocal.asInterface.getMutationResolver(field);
106
+ if (r !== undefined) {
107
+ return r;
108
+ } else {
109
+ return Stdlib_JsError.throwWithMessage("resolver not registered: " + field);
110
+ }
111
+ };
112
+ return [
113
+ resolverFor(addField),
114
+ resolverFor(renameField),
115
+ resolverFor(archiveField),
116
+ calls
117
+ ];
118
+ }
119
+
120
+ globalThis.describe("CommandGeneratorResolvers_GraphQL — per-constructor authorization", () => {
121
+ globalThis.beforeEach(() => DomainGraphQL_Server$ReventlessLocal.asInterface.reset());
122
+ globalThis.test("admin invoking payload-less Archive succeeds", async () => {
123
+ let match = buildFixture("Cat1");
124
+ let response = await match[2](null, {}, ctxFor(adminIdentity));
125
+ globalThis.expect(getTypename(response)).toEqual("CommandAccepted");
126
+ globalThis.expect(match[3].contents).toEqual(["Archive"]);
127
+ });
128
+ globalThis.test("regular user invoking Archive is rejected with Forbidden", async () => {
129
+ let match = buildFixture("Cat2");
130
+ let response = await match[2](null, {}, ctxFor(userIdentity));
131
+ globalThis.expect(getTypename(response)).toEqual("CommandRejected");
132
+ globalThis.expect(getErrorCode(response)).toEqual("Forbidden");
133
+ globalThis.expect(match[3].contents).toEqual([]);
134
+ });
135
+ globalThis.test("anonymous invoking Archive is rejected with Forbidden", async () => {
136
+ let match = buildFixture("Cat3");
137
+ let response = await match[2](null, {}, anonymousCtx);
138
+ globalThis.expect(getTypename(response)).toEqual("CommandRejected");
139
+ globalThis.expect(getErrorCode(response)).toEqual("Forbidden");
140
+ globalThis.expect(match[3].contents).toEqual([]);
141
+ });
142
+ globalThis.test("regular user invoking Add (default AllowAuthenticated) succeeds", async () => {
143
+ let match = buildFixture("Cat4");
144
+ let response = await match[0](null, Object.fromEntries([[
145
+ "name",
146
+ "Books"
147
+ ]]), ctxFor(userIdentity));
148
+ globalThis.expect(getTypename(response)).toEqual("CommandAccepted");
149
+ globalThis.expect(match[3].contents).toEqual(["Add"]);
150
+ });
151
+ globalThis.test("anonymous invoking Add is rejected (AllowAuthenticated default)", async () => {
152
+ let match = buildFixture("Cat5");
153
+ let response = await match[0](null, Object.fromEntries([[
154
+ "name",
155
+ "Books"
156
+ ]]), anonymousCtx);
157
+ globalThis.expect(getTypename(response)).toEqual("CommandRejected");
158
+ globalThis.expect(getErrorCode(response)).toEqual("Forbidden");
159
+ globalThis.expect(match[3].contents).toEqual([]);
160
+ });
161
+ globalThis.test("admin invoking Add also succeeds (admin has User group too)", async () => {
162
+ let match = buildFixture("Cat6");
163
+ let response = await match[0](null, Object.fromEntries([[
164
+ "name",
165
+ "Books"
166
+ ]]), ctxFor(adminIdentity));
167
+ globalThis.expect(getTypename(response)).toEqual("CommandAccepted");
168
+ globalThis.expect(match[3].contents).toEqual(["Add"]);
169
+ });
170
+ globalThis.test("payload-less Archive registered as a GraphQL mutation field (no longer filtered)", async () => {
171
+ buildFixture("Cat7");
172
+ let sdl = DomainGraphQL_Server$ReventlessLocal.asInterface.buildSdl();
173
+ globalThis.expect(sdl.includes("Cat7_Add")).toEqual(true);
174
+ globalThis.expect(sdl.includes("Cat7_Rename")).toEqual(true);
175
+ globalThis.expect(sdl.includes("Cat7_Archive")).toEqual(true);
176
+ });
177
+ });
178
+
179
+ export {
180
+ commandSchema,
181
+ commandAuthorization,
182
+ adminIdentity,
183
+ userIdentity,
184
+ ctxFor,
185
+ anonymousCtx,
186
+ getTypename,
187
+ getErrorCode,
188
+ buildFixture,
189
+ }
190
+ /* Not a pure module */
@@ -0,0 +1,174 @@
1
+ // Unit tests for LocalCommandTopicChannel.
2
+ // Covers encodeCommandJson (shared helper), decodeId, publishJsons, and connect wiring.
3
+
4
+ open JestGlobals
5
+
6
+ let _ = TestRunner.setup()
7
+
8
+ let testMeta: Reventless.Message.meta = {
9
+ service: "test",
10
+ time: "2024-01-01T00:00:00.000Z",
11
+ ip: "127.0.0.1",
12
+ user: "testuser",
13
+ msgId: "msg-001",
14
+ correlationId: "corr-001",
15
+ }
16
+
17
+ describe("LocalCommandTopicChannel", () => {
18
+ describe("encodeCommandJson", () => {
19
+ testPromise("produces {id, meta, command} JSON shape", async () => {
20
+ let cmdJson: Reventless.Message.commandJson = {
21
+ id: "agg-1",
22
+ meta: testMeta,
23
+ commandJson: JSON.Encode.string("DoSomething"),
24
+ }
25
+ let encoded = ReventlessCore.CommandTopic.encodeCommandJson(cmdJson)
26
+ let dict = switch encoded {
27
+ | JSON.Object(d) => d
28
+ | _ => Dict.make()
29
+ }
30
+ expect(dict->Dict.get("id"))->toEqual(Some(JSON.Encode.string("agg-1")))
31
+ expect(dict->Dict.get("command"))->toEqual(Some(JSON.Encode.string("DoSomething")))
32
+ expect(dict->Dict.get("meta")->Option.isSome)->toBe(true)
33
+ })
34
+ })
35
+
36
+ describe("decodeId", () => {
37
+ testPromise("extracts id string from encoded message body", async () => {
38
+ module TestBus = LocalBus.Make()
39
+ module TestChannel = LocalCommandTopicChannel.Make(TestBus)
40
+ let body = JSON.Encode.object(
41
+ Dict.fromArray([("id", JSON.Encode.string("agg-42"))]),
42
+ )
43
+ expect(TestChannel.decodeId(body))->toBe("agg-42")
44
+ })
45
+
46
+ testPromise("returns empty string for non-object input", async () => {
47
+ module TestBus = LocalBus.Make()
48
+ module TestChannel = LocalCommandTopicChannel.Make(TestBus)
49
+ expect(TestChannel.decodeId(JSON.Encode.string("not-an-object")))->toBe("")
50
+ })
51
+
52
+ testPromise("returns empty string when id field is missing", async () => {
53
+ module TestBus = LocalBus.Make()
54
+ module TestChannel = LocalCommandTopicChannel.Make(TestBus)
55
+ let body = JSON.Encode.object(Dict.fromArray([("other", JSON.Encode.int(1))]))
56
+ expect(TestChannel.decodeId(body))->toBe("")
57
+ })
58
+
59
+ testPromise("returns empty string when id field is not a string", async () => {
60
+ module TestBus = LocalBus.Make()
61
+ module TestChannel = LocalCommandTopicChannel.Make(TestBus)
62
+ let body = JSON.Encode.object(Dict.fromArray([("id", JSON.Encode.int(99))]))
63
+ expect(TestChannel.decodeId(body))->toBe("")
64
+ })
65
+ })
66
+
67
+ describe("publishJsons", () => {
68
+ testPromise("dispatches each command to the bus with full {id, meta, command} body", async () => {
69
+ module TestBus = LocalBus.Make()
70
+ module TestChannel = LocalCommandTopicChannel.Make(TestBus)
71
+ let received: ref<option<JSON.t>> = ref(None)
72
+ TestBus.registerCommandHandler("myCmd", async (json, _) => {
73
+ received := Some(json)
74
+ })
75
+ let ch = TestChannel.make(~name="myCmd")
76
+ let publishFn = await ch.publishJsons->TestRunner.resolve
77
+ await publishFn([
78
+ {
79
+ Reventless.Message.id: "item-1",
80
+ meta: testMeta,
81
+ commandJson: JSON.Encode.string("CreateItem"),
82
+ },
83
+ ])
84
+ let receivedJson = received.contents->Option.getOr(JSON.Null)
85
+ let receivedDict = switch receivedJson {
86
+ | JSON.Object(d) => d
87
+ | _ => Dict.make()
88
+ }
89
+ expect(receivedDict->Dict.get("id"))->toEqual(Some(JSON.Encode.string("item-1")))
90
+ })
91
+
92
+ testPromise("dispatches multiple commands in order", async () => {
93
+ module TestBus = LocalBus.Make()
94
+ module TestChannel = LocalCommandTopicChannel.Make(TestBus)
95
+ let count: ref<int> = ref(0)
96
+ TestBus.registerCommandHandler("multiCmd", async (_, _) => {
97
+ count := count.contents + 1
98
+ })
99
+ let ch = TestChannel.make(~name="multiCmd")
100
+ let publishFn = await ch.publishJsons->TestRunner.resolve
101
+ await publishFn([
102
+ {
103
+ Reventless.Message.id: "id-1",
104
+ meta: testMeta,
105
+ commandJson: JSON.Null,
106
+ },
107
+ {
108
+ Reventless.Message.id: "id-2",
109
+ meta: testMeta,
110
+ commandJson: JSON.Null,
111
+ },
112
+ ])
113
+ expect(count.contents)->toBe(2)
114
+ })
115
+ })
116
+
117
+ describe("connect", () => {
118
+ testPromise("registers handler; dispatch reaches the runtime handlerDeferred", async () => {
119
+ module TestBus = LocalBus.Make()
120
+ module TestChannel = LocalCommandTopicChannel.Make(TestBus)
121
+ let received: ref<option<JSON.t>> = ref(None)
122
+ let handlerDeferred: Deferred.t<LocalRuntimeEnvironment.handler, unit> =
123
+ Deferred.make()->Effect.runSync
124
+ Deferred.succeed(handlerDeferred, async (json, _ctx) => {
125
+ received := Some(json)
126
+ })->Effect.runSync->ignore
127
+ let runtime: ReventlessCore.Runtime.environment<LocalRuntimeEnvironment.parts> = {
128
+ parts: {
129
+ handlerDeferred,
130
+ subscriptionLatch: Effect.makeLatch(false)->Effect.runSync,
131
+ },
132
+ resources: [],
133
+ }
134
+ let ch = TestChannel.make(~name="testCmd")
135
+ let _ = ch.connect(
136
+ ~name="testCmd",
137
+ ~channel=ch,
138
+ ~runtime,
139
+ ~resources=[],
140
+ ~opts={},
141
+ )
142
+ await TestBus.dispatchCommand("testCmd", JSON.Encode.string("test-payload"))
143
+ expect(received.contents)->toEqual(Some(JSON.Encode.string("test-payload")))
144
+ })
145
+
146
+ testPromise("unresolved Deferred does not crash on dispatch", async () => {
147
+ module TestBus = LocalBus.Make()
148
+ module TestChannel = LocalCommandTopicChannel.Make(TestBus)
149
+ // Deferred left incomplete — dispatch starts a pending Effect (no crash)
150
+ let handlerDeferred: Deferred.t<LocalRuntimeEnvironment.handler, unit> =
151
+ Deferred.make()->Effect.runSync
152
+ let runtime: ReventlessCore.Runtime.environment<LocalRuntimeEnvironment.parts> = {
153
+ parts: {
154
+ handlerDeferred,
155
+ subscriptionLatch: Effect.makeLatch(false)->Effect.runSync,
156
+ },
157
+ resources: [],
158
+ }
159
+ let ch = TestChannel.make(~name="noHandlerCmd")
160
+ let _ = ch.connect(
161
+ ~name="noHandlerCmd",
162
+ ~channel=ch,
163
+ ~runtime,
164
+ ~resources=[],
165
+ ~opts={},
166
+ )
167
+ // Fire-and-forget — the Deferred.await_ will stay pending
168
+ TestBus.dispatchCommand("noHandlerCmd", JSON.Null)->ignore
169
+ expect(true)->toBe(true)
170
+ // Complete the Deferred to avoid open handle
171
+ Deferred.succeed(handlerDeferred, async (_, _) => ())->Effect.runSync->ignore
172
+ })
173
+ })
174
+ })