@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,68 @@
1
+ // End-to-end proof of the inferred cross-partition read fix: a sibling product
2
+ // sharing the same categoryId must NOT be returned by AddProduct's decision read,
3
+ // because the categoryId clause indexes only the category's own events. Exercises
4
+ // the full command → narrowed query → in-memory DcbEventLog read → decide path
5
+ // that the unit tests and GWTs do not cover (the GWT harness folds events raw).
6
+
7
+ open JestGlobals
8
+ open DcbCrossPartitionFixtures
9
+
10
+ describe("DCB inferred cross-partition read (sibling exclusion)", () => {
11
+ let _ = beforeAllAsync(async () => {
12
+ let _ = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
13
+ })
14
+
15
+ let _ = beforeEach(() => {
16
+ capturedEventCount := 0
17
+ })
18
+
19
+ testPromise(
20
+ "a sibling product sharing the categoryId is not read; the new product is added",
21
+ async () => {
22
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
23
+ // Category cat1 exists, and a DIFFERENT product p1 already lives in it
24
+ // (tagged productId=p1 AND categoryId=cat1).
25
+ let _ = await ops.append([
26
+ encodeEvent(CategoryAdded({categoryId: "cat1", name: "Electronics"})),
27
+ encodeEvent(ProductAdded({productId: "p1", categoryId: "cat1", name: "Laptop"})),
28
+ ])
29
+ capturedEventCount := 0 // count only events produced by the command below
30
+
31
+ // Add a new product p2 in cat1. The categoryId clause narrows ProductAdded
32
+ // out, so it reads only CategoryAdded — p1 is invisible — and p2 is added.
33
+ await dispatch(addProductJson("p2", "cat1", "Mouse"), "p2")
34
+ expect(capturedEventCount.contents)->toBe(1)
35
+ },
36
+ )
37
+
38
+ testPromise(
39
+ "a product added through the slice is NOT written to the categoryId GSI (payload, not indexed)",
40
+ async () => {
41
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
42
+ let _ = await ops.append([encodeEvent(CategoryAdded({categoryId: "cat3", name: "Toys"}))])
43
+ // p3 is produced by the slice → its categoryId is filtered out on write.
44
+ await dispatch(addProductJson("p3", "cat3", "Blocks"), "p3")
45
+
46
+ // Queryable by its own partition key…
47
+ let byProduct = await ops.read(~query=[{tags: [{Reventless.DcbTag.key: "productId", value: "p3"}]}])
48
+ expect(byProduct.events->Array.some(e => e.eventType == "ProductAdded"))->toBe(true)
49
+
50
+ // …but the categoryId index returns only the category's own event, never p3.
51
+ let byCategory = await ops.read(~query=[{tags: [{Reventless.DcbTag.key: "categoryId", value: "cat3"}]}])
52
+ expect(byCategory.events->Array.some(e => e.eventType == "ProductAdded"))->toBe(false)
53
+ expect(byCategory.events->Array.some(e => e.eventType == "CategoryAdded"))->toBe(true)
54
+ },
55
+ )
56
+
57
+ testPromise("re-adding the same product is still rejected (its own read is partition-scoped)", async () => {
58
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
59
+ let _ = await ops.append([
60
+ encodeEvent(CategoryAdded({categoryId: "cat2", name: "Books"})),
61
+ encodeEvent(ProductAdded({productId: "p9", categoryId: "cat2", name: "Novel"})),
62
+ ])
63
+ capturedEventCount := 0 // count only events produced by the command below
64
+ // p9 already exists — its own productId clause returns its ProductAdded.
65
+ await dispatch(addProductJson("p9", "cat2", "Novel"), "p9")
66
+ expect(capturedEventCount.contents)->toBe(0)
67
+ })
68
+ })
@@ -0,0 +1,78 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
4
+ import * as TestRunner$ReventlessLocal from "../../../src/test/TestRunner.res.mjs";
5
+ import * as DcbCrossPartitionFixtures$ReventlessLocal from "./DcbCrossPartitionFixtures.res.mjs";
6
+
7
+ globalThis.describe("DCB inferred cross-partition read (sibling exclusion)", () => {
8
+ globalThis.beforeAll(async () => {
9
+ await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(DcbCrossPartitionFixtures$ReventlessLocal.eventLog));
10
+ });
11
+ globalThis.beforeEach(() => {
12
+ DcbCrossPartitionFixtures$ReventlessLocal.capturedEventCount.contents = 0;
13
+ });
14
+ globalThis.test("a sibling product sharing the categoryId is not read; the new product is added", async () => {
15
+ let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(DcbCrossPartitionFixtures$ReventlessLocal.eventLog));
16
+ await ops.append([
17
+ DcbCrossPartitionFixtures$ReventlessLocal.encodeEvent({
18
+ TAG: "CategoryAdded",
19
+ categoryId: "cat1",
20
+ name: "Electronics"
21
+ }),
22
+ DcbCrossPartitionFixtures$ReventlessLocal.encodeEvent({
23
+ TAG: "ProductAdded",
24
+ productId: "p1",
25
+ categoryId: "cat1",
26
+ name: "Laptop"
27
+ })
28
+ ], undefined);
29
+ DcbCrossPartitionFixtures$ReventlessLocal.capturedEventCount.contents = 0;
30
+ await DcbCrossPartitionFixtures$ReventlessLocal.dispatch(DcbCrossPartitionFixtures$ReventlessLocal.addProductJson("p2", "cat1", "Mouse"), "p2");
31
+ globalThis.expect(DcbCrossPartitionFixtures$ReventlessLocal.capturedEventCount.contents).toBe(1);
32
+ });
33
+ globalThis.test("a product added through the slice is NOT written to the categoryId GSI (payload, not indexed)", async () => {
34
+ let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(DcbCrossPartitionFixtures$ReventlessLocal.eventLog));
35
+ await ops.append([DcbCrossPartitionFixtures$ReventlessLocal.encodeEvent({
36
+ TAG: "CategoryAdded",
37
+ categoryId: "cat3",
38
+ name: "Toys"
39
+ })], undefined);
40
+ await DcbCrossPartitionFixtures$ReventlessLocal.dispatch(DcbCrossPartitionFixtures$ReventlessLocal.addProductJson("p3", "cat3", "Blocks"), "p3");
41
+ let byProduct = await ops.read([{
42
+ tags: [{
43
+ key: "productId",
44
+ value: "p3"
45
+ }]
46
+ }], undefined);
47
+ globalThis.expect(byProduct.events.some(e => e.eventType === "ProductAdded")).toBe(true);
48
+ let byCategory = await ops.read([{
49
+ tags: [{
50
+ key: "categoryId",
51
+ value: "cat3"
52
+ }]
53
+ }], undefined);
54
+ globalThis.expect(byCategory.events.some(e => e.eventType === "ProductAdded")).toBe(false);
55
+ globalThis.expect(byCategory.events.some(e => e.eventType === "CategoryAdded")).toBe(true);
56
+ });
57
+ globalThis.test("re-adding the same product is still rejected (its own read is partition-scoped)", async () => {
58
+ let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(DcbCrossPartitionFixtures$ReventlessLocal.eventLog));
59
+ await ops.append([
60
+ DcbCrossPartitionFixtures$ReventlessLocal.encodeEvent({
61
+ TAG: "CategoryAdded",
62
+ categoryId: "cat2",
63
+ name: "Books"
64
+ }),
65
+ DcbCrossPartitionFixtures$ReventlessLocal.encodeEvent({
66
+ TAG: "ProductAdded",
67
+ productId: "p9",
68
+ categoryId: "cat2",
69
+ name: "Novel"
70
+ })
71
+ ], undefined);
72
+ DcbCrossPartitionFixtures$ReventlessLocal.capturedEventCount.contents = 0;
73
+ await DcbCrossPartitionFixtures$ReventlessLocal.dispatch(DcbCrossPartitionFixtures$ReventlessLocal.addProductJson("p9", "cat2", "Novel"), "p9");
74
+ globalThis.expect(DcbCrossPartitionFixtures$ReventlessLocal.capturedEventCount.contents).toBe(0);
75
+ });
76
+ });
77
+
78
+ /* Not a pure module */
@@ -0,0 +1,72 @@
1
+ // Tests for DcbEventLog.appendStream (Phase H of effect-stream-integration plan).
2
+ // Verifies that a Stream<rawEvent> can drive a single atomic append, and that
3
+ // the readStream → appendStream pipeline works end-to-end.
4
+
5
+ open JestGlobals
6
+ open DcbFixtures
7
+
8
+ // Helper to encode a typed event into a raw event for appending to the DcbEventLog
9
+ let encodeEvent = (event: DcbFixtures.ItemEventLog.event): ReventlessInfra.DcbEventLog.rawEvent => {
10
+ let json = event->S.reverseConvertToJsonOrThrow(DcbFixtures.ItemEventLog.eventSchema)
11
+ let (eventType, data) = json->ReventlessCore.Message.splitMessage
12
+ let tags = Reventless.DcbTag.extractTags(DcbFixtures.ItemEventLog.eventSchema, event)
13
+ let meta = ReventlessCore.Message.generateMeta(~service="test")
14
+ {eventType, data: JSON.Object(data), tags, meta}
15
+ }
16
+
17
+ describe("DcbEventLog.appendStream (in-memory adapter)", () => {
18
+ let _ = beforeAllAsync(async () => {
19
+ let _ = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
20
+ })
21
+
22
+ let _ = beforeEach(() => {
23
+ capturedEventCount := 0
24
+ })
25
+
26
+ testPromise("appendStream writes all events from stream", async () => {
27
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
28
+ let stream = [
29
+ encodeEvent(DcbFixtures.ItemEventLog.ItemAdded({id: "as-w1", name: "Widget1"})),
30
+ encodeEvent(DcbFixtures.ItemEventLog.ItemAdded({id: "as-w1", name: "Widget2"})),
31
+ ]->Stream.fromIterable
32
+ let result = await ops.appendStream(stream)->Effect.runPromise
33
+ let isOk = switch result {
34
+ | Ok(_) => true
35
+ | Error(_) => false
36
+ }
37
+ expect(isOk)->toBe(true)
38
+ let read = await ops.read(~query=tagQuery("as-w1"))
39
+ expect(read.events->Array.length)->toBe(2)
40
+ })
41
+
42
+ testPromise("appendStream on empty stream succeeds", async () => {
43
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
44
+ // Empty stream → empty array append → storage returns Ok(position)
45
+ let result = await ops.appendStream(Stream.empty)->Effect.runPromise
46
+ let isOk = switch result {
47
+ | Ok(_) => true
48
+ | Error(_) => false
49
+ }
50
+ expect(isOk)->toBe(true)
51
+ })
52
+
53
+ testPromise("readStream → appendStream pipeline copies events", async () => {
54
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
55
+ let _ = await ops.append([encodeEvent(DcbFixtures.ItemEventLog.ItemAdded({id: "as-src", name: "Source"}))])
56
+ // readStream returns rawSequencedEvent; extract bare rawEvent for appendStream
57
+ let srcStream =
58
+ ops.readStream(~query=tagQuery("as-src"))->Stream.map(se => {
59
+ let rawEvent: ReventlessInfra.DcbEventLog.rawEvent = {
60
+ eventType: se.eventType,
61
+ data: se.data,
62
+ tags: se.tags,
63
+ meta: se.meta,
64
+ }
65
+ rawEvent
66
+ })
67
+ let _ = await ops.appendStream(srcStream)->Effect.runPromise
68
+ let dst = await ops.read(~query=tagQuery("as-src"))
69
+ // 1 original + 1 copy = 2 events for the same tag
70
+ expect(dst.events->Array.length)->toBe(2)
71
+ })
72
+ })
@@ -0,0 +1,82 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Stream from "effect/Stream";
6
+ import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
7
+ import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
8
+ import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
9
+ import * as TestRunner$ReventlessLocal from "../../../src/test/TestRunner.res.mjs";
10
+ import * as DcbFixtures$ReventlessLocal from "./DcbFixtures.res.mjs";
11
+
12
+ function encodeEvent(event) {
13
+ let json = S.reverseConvertToJsonOrThrow(event, DcbFixtures$ReventlessLocal.ItemEventLog.eventSchema);
14
+ let match = Message$ReventlessCore.splitMessage(json);
15
+ let tags = DcbTag$Reventless.extractTags(DcbFixtures$ReventlessLocal.ItemEventLog.eventSchema, event);
16
+ let meta = Message$ReventlessCore.generateMeta("test", undefined, undefined, undefined, undefined, undefined, undefined, undefined);
17
+ return {
18
+ eventType: match[0],
19
+ data: match[1],
20
+ tags: tags,
21
+ meta: meta
22
+ };
23
+ }
24
+
25
+ globalThis.describe("DcbEventLog.appendStream (in-memory adapter)", () => {
26
+ globalThis.beforeAll(async () => {
27
+ await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(DcbFixtures$ReventlessLocal.eventLog));
28
+ });
29
+ globalThis.beforeEach(() => {
30
+ DcbFixtures$ReventlessLocal.capturedEventCount.contents = 0;
31
+ });
32
+ globalThis.test("appendStream writes all events from stream", async () => {
33
+ let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(DcbFixtures$ReventlessLocal.eventLog));
34
+ let stream = Stream.fromIterable([
35
+ encodeEvent({
36
+ TAG: "ItemAdded",
37
+ id: "as-w1",
38
+ name: "Widget1"
39
+ }),
40
+ encodeEvent({
41
+ TAG: "ItemAdded",
42
+ id: "as-w1",
43
+ name: "Widget2"
44
+ })
45
+ ]);
46
+ let result = await Effect.runPromise(ops.appendStream(stream, undefined));
47
+ let isOk;
48
+ isOk = result.TAG === "Ok";
49
+ globalThis.expect(isOk).toBe(true);
50
+ let read = await ops.read(DcbFixtures$ReventlessLocal.tagQuery("as-w1"), undefined);
51
+ globalThis.expect(read.events.length).toBe(2);
52
+ });
53
+ globalThis.test("appendStream on empty stream succeeds", async () => {
54
+ let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(DcbFixtures$ReventlessLocal.eventLog));
55
+ let result = await Effect.runPromise(ops.appendStream(Stream.empty, undefined));
56
+ let isOk;
57
+ isOk = result.TAG === "Ok";
58
+ globalThis.expect(isOk).toBe(true);
59
+ });
60
+ globalThis.test("readStream → appendStream pipeline copies events", async () => {
61
+ let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(DcbFixtures$ReventlessLocal.eventLog));
62
+ await ops.append([encodeEvent({
63
+ TAG: "ItemAdded",
64
+ id: "as-src",
65
+ name: "Source"
66
+ })], undefined);
67
+ let srcStream = Stream.map(ops.readStream(DcbFixtures$ReventlessLocal.tagQuery("as-src"), undefined, undefined), se => ({
68
+ eventType: se.eventType,
69
+ data: se.data,
70
+ tags: se.tags,
71
+ meta: se.meta
72
+ }));
73
+ await Effect.runPromise(ops.appendStream(srcStream, undefined));
74
+ let dst = await ops.read(DcbFixtures$ReventlessLocal.tagQuery("as-src"), undefined);
75
+ globalThis.expect(dst.events.length).toBe(2);
76
+ });
77
+ });
78
+
79
+ export {
80
+ encodeEvent,
81
+ }
82
+ /* Not a pure module */
@@ -0,0 +1,103 @@
1
+ // Tests for DcbEventLog.readStream (Phase D of effect-stream-integration plan).
2
+ // Mirrors the pattern of EventLogStreamTest.res for the DCB event log.
3
+
4
+ open JestGlobals
5
+ open DcbFixtures
6
+
7
+ describe("DcbEventLog.readStream (in-memory adapter)", () => {
8
+ let _ = beforeAllAsync(async () => {
9
+ let _ = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
10
+ })
11
+
12
+ let _ = beforeEach(() => {
13
+ capturedEventCount := 0
14
+ })
15
+
16
+ describe("basic streaming", () => {
17
+ testPromise("readStream for tag with no matching events returns empty stream", async () => {
18
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
19
+ let count = await ops.readStream(~query=tagQuery("no-such-id"))
20
+ ->Stream.runFold(0, (n, _) => n + 1)
21
+ ->Effect.runPromise
22
+ expect(count)->toBe(0)
23
+ })
24
+
25
+ testPromise("readStream emits all events matching a tag query", async () => {
26
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
27
+ let _ = await dispatch(addItemJson("stream-1", "Widget"), "stream-1")
28
+ let count = await ops.readStream(~query=tagQuery("stream-1"))
29
+ ->Stream.runFold(0, (n, _) => n + 1)
30
+ ->Effect.runPromise
31
+ expect(count)->toBe(1)
32
+ })
33
+
34
+ testPromise("readStream for distinct IDs returns independent streams", async () => {
35
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
36
+ let _ = await dispatch(addItemJson("stream-A", "Alpha"), "stream-A")
37
+ let _ = await dispatch(addItemJson("stream-B", "Beta"), "stream-B")
38
+ let countA = await ops.readStream(~query=tagQuery("stream-A"))
39
+ ->Stream.runFold(0, (n, _) => n + 1)
40
+ ->Effect.runPromise
41
+ let countB = await ops.readStream(~query=tagQuery("stream-B"))
42
+ ->Stream.runFold(0, (n, _) => n + 1)
43
+ ->Effect.runPromise
44
+ expect(countA)->toBe(1)
45
+ expect(countB)->toBe(1)
46
+ })
47
+
48
+ testPromise("take(1) limits stream without loading all", async () => {
49
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
50
+ let _ = await dispatch(addItemJson("stream-C1", "C1"), "stream-C1")
51
+ let _ = await dispatch(addItemJson("stream-C2", "C2"), "stream-C2")
52
+ // Broad query: all ItemAdded events regardless of tag value
53
+ let result = await ops.readStream(~query=typeQuery("ItemAdded"))
54
+ ->Stream.take(1)
55
+ ->Stream.runCollect
56
+ ->Effect.runPromise
57
+ expect(result->Array.length)->toBe(1)
58
+ })
59
+ })
60
+
61
+ describe("fold patterns — (state, headPosition) used in StateChangeSlice_Callback", () => {
62
+ testPromise("runFold extracts last headPosition for append condition", async () => {
63
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
64
+ let _ = await dispatch(addItemJson("fold-1", "FoldItem"), "fold-1")
65
+ let (_, headPosition) = await ops.readStream(~query=tagQuery("fold-1"))
66
+ ->Stream.runFold(((), None), ((_dm, _pos), se) => ((), Some(se.position)))
67
+ ->Effect.runPromise
68
+ expect(Option.isSome(headPosition))->toBe(true)
69
+ })
70
+
71
+ testPromise("runFold on empty stream returns initial accumulator", async () => {
72
+ let ops = await eventLog->ReventlessCore.Component.operations->TestRunner.resolve
73
+ let (_, headPosition) = await ops.readStream(~query=tagQuery("no-such-id-fold"))
74
+ ->Stream.runFold((false, None), ((_dm, _pos), se) => (true, Some(se.position)))
75
+ ->Effect.runPromise
76
+ expect(headPosition)->toEqual(None)
77
+ })
78
+ })
79
+
80
+ describe("StateChangeSlice regression — second command sees first event via stream fold", () => {
81
+ testPromise("second AddItem with same ID is rejected (state sees existing event)", async () => {
82
+ // First command succeeds → ItemAdded event stored
83
+ // Second command: readStream folds all events → state = true (exists)
84
+ // decide(true, AddItem) → Error(ItemAlreadyExists) → no new events
85
+ let id = "dup-test-stream"
86
+ let _ = await dispatch(addItemJson(id, "First"), id)
87
+ let countBefore = capturedEventCount.contents
88
+ let _ = await dispatch(addItemJson(id, "Duplicate"), id)
89
+ // Duplicate command must not publish a second event
90
+ expect(capturedEventCount.contents)->toBe(countBefore)
91
+ })
92
+
93
+ testPromise("second AddItem with different ID succeeds", async () => {
94
+ let id1 = "distinct-1"
95
+ let id2 = "distinct-2"
96
+ let _ = await dispatch(addItemJson(id1, "First"), id1)
97
+ let countAfterFirst = capturedEventCount.contents
98
+ let _ = await dispatch(addItemJson(id2, "Second"), id2)
99
+ // New ID should publish an event
100
+ expect(capturedEventCount.contents)->toBe(countAfterFirst + 1)
101
+ })
102
+ })
103
+ })
@@ -0,0 +1,91 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stream from "@reventlessdev/rescript-effect/src/Stream.res.mjs";
4
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
+ import * as Effect from "effect/Effect";
6
+ import * as Stream$1 from "effect/Stream";
7
+ import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
8
+ import * as TestRunner$ReventlessLocal from "../../../src/test/TestRunner.res.mjs";
9
+ import * as DcbFixtures$ReventlessLocal from "./DcbFixtures.res.mjs";
10
+
11
+ globalThis.describe("DcbEventLog.readStream (in-memory adapter)", () => {
12
+ globalThis.beforeAll(async () => {
13
+ await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(DcbFixtures$ReventlessLocal.eventLog));
14
+ });
15
+ globalThis.beforeEach(() => {
16
+ DcbFixtures$ReventlessLocal.capturedEventCount.contents = 0;
17
+ });
18
+ globalThis.describe("basic streaming", () => {
19
+ globalThis.test("readStream for tag with no matching events returns empty stream", async () => {
20
+ let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(DcbFixtures$ReventlessLocal.eventLog));
21
+ let count = await Effect.runPromise(Stream$1.runFold(ops.readStream(DcbFixtures$ReventlessLocal.tagQuery("no-such-id"), undefined, undefined), 0, (n, param) => n + 1 | 0));
22
+ globalThis.expect(count).toBe(0);
23
+ });
24
+ globalThis.test("readStream emits all events matching a tag query", async () => {
25
+ let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(DcbFixtures$ReventlessLocal.eventLog));
26
+ await DcbFixtures$ReventlessLocal.dispatch(DcbFixtures$ReventlessLocal.addItemJson("stream-1", "Widget"), "stream-1");
27
+ let count = await Effect.runPromise(Stream$1.runFold(ops.readStream(DcbFixtures$ReventlessLocal.tagQuery("stream-1"), undefined, undefined), 0, (n, param) => n + 1 | 0));
28
+ globalThis.expect(count).toBe(1);
29
+ });
30
+ globalThis.test("readStream for distinct IDs returns independent streams", async () => {
31
+ let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(DcbFixtures$ReventlessLocal.eventLog));
32
+ await DcbFixtures$ReventlessLocal.dispatch(DcbFixtures$ReventlessLocal.addItemJson("stream-A", "Alpha"), "stream-A");
33
+ await DcbFixtures$ReventlessLocal.dispatch(DcbFixtures$ReventlessLocal.addItemJson("stream-B", "Beta"), "stream-B");
34
+ let countA = await Effect.runPromise(Stream$1.runFold(ops.readStream(DcbFixtures$ReventlessLocal.tagQuery("stream-A"), undefined, undefined), 0, (n, param) => n + 1 | 0));
35
+ let countB = await Effect.runPromise(Stream$1.runFold(ops.readStream(DcbFixtures$ReventlessLocal.tagQuery("stream-B"), undefined, undefined), 0, (n, param) => n + 1 | 0));
36
+ globalThis.expect(countA).toBe(1);
37
+ globalThis.expect(countB).toBe(1);
38
+ });
39
+ globalThis.test("take(1) limits stream without loading all", async () => {
40
+ let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(DcbFixtures$ReventlessLocal.eventLog));
41
+ await DcbFixtures$ReventlessLocal.dispatch(DcbFixtures$ReventlessLocal.addItemJson("stream-C1", "C1"), "stream-C1");
42
+ await DcbFixtures$ReventlessLocal.dispatch(DcbFixtures$ReventlessLocal.addItemJson("stream-C2", "C2"), "stream-C2");
43
+ let result = await Effect.runPromise(Stream.runCollect(Stream$1.take(ops.readStream(DcbFixtures$ReventlessLocal.typeQuery("ItemAdded"), undefined, undefined), 1)));
44
+ globalThis.expect(result.length).toBe(1);
45
+ });
46
+ });
47
+ globalThis.describe("fold patterns — (state, headPosition) used in StateChangeSlice_Callback", () => {
48
+ globalThis.test("runFold extracts last headPosition for append condition", async () => {
49
+ let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(DcbFixtures$ReventlessLocal.eventLog));
50
+ await DcbFixtures$ReventlessLocal.dispatch(DcbFixtures$ReventlessLocal.addItemJson("fold-1", "FoldItem"), "fold-1");
51
+ let match = await Effect.runPromise(Stream$1.runFold(ops.readStream(DcbFixtures$ReventlessLocal.tagQuery("fold-1"), undefined, undefined), [
52
+ undefined,
53
+ undefined
54
+ ], (param, se) => [
55
+ undefined,
56
+ se.position
57
+ ]));
58
+ globalThis.expect(Stdlib_Option.isSome(match[1])).toBe(true);
59
+ });
60
+ globalThis.test("runFold on empty stream returns initial accumulator", async () => {
61
+ let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(DcbFixtures$ReventlessLocal.eventLog));
62
+ let match = await Effect.runPromise(Stream$1.runFold(ops.readStream(DcbFixtures$ReventlessLocal.tagQuery("no-such-id-fold"), undefined, undefined), [
63
+ false,
64
+ undefined
65
+ ], (param, se) => [
66
+ true,
67
+ se.position
68
+ ]));
69
+ globalThis.expect(match[1]).toEqual(undefined);
70
+ });
71
+ });
72
+ globalThis.describe("StateChangeSlice regression — second command sees first event via stream fold", () => {
73
+ globalThis.test("second AddItem with same ID is rejected (state sees existing event)", async () => {
74
+ let id = "dup-test-stream";
75
+ await DcbFixtures$ReventlessLocal.dispatch(DcbFixtures$ReventlessLocal.addItemJson(id, "First"), id);
76
+ let countBefore = DcbFixtures$ReventlessLocal.capturedEventCount.contents;
77
+ await DcbFixtures$ReventlessLocal.dispatch(DcbFixtures$ReventlessLocal.addItemJson(id, "Duplicate"), id);
78
+ globalThis.expect(DcbFixtures$ReventlessLocal.capturedEventCount.contents).toBe(countBefore);
79
+ });
80
+ globalThis.test("second AddItem with different ID succeeds", async () => {
81
+ let id1 = "distinct-1";
82
+ let id2 = "distinct-2";
83
+ await DcbFixtures$ReventlessLocal.dispatch(DcbFixtures$ReventlessLocal.addItemJson(id1, "First"), id1);
84
+ let countAfterFirst = DcbFixtures$ReventlessLocal.capturedEventCount.contents;
85
+ await DcbFixtures$ReventlessLocal.dispatch(DcbFixtures$ReventlessLocal.addItemJson(id2, "Second"), id2);
86
+ globalThis.expect(DcbFixtures$ReventlessLocal.capturedEventCount.contents).toBe(countAfterFirst + 1 | 0);
87
+ });
88
+ });
89
+ });
90
+
91
+ /* Not a pure module */
@@ -0,0 +1,145 @@
1
+ // E2E test fixtures for DcbEventLog + StateChangeSlice.
2
+ // Contains specs, bus setup, component wiring, and shared test helpers.
3
+
4
+ open TestFixtures
5
+
6
+ // ─────────────────────────────────────────────────────────────
7
+ // Event type definition for the DcbEventLog
8
+ // ─────────────────────────────────────────────────────────────
9
+
10
+ module ItemEventLog = {
11
+ @schema
12
+ type event = ItemAdded({id: @s.matches(Reventless.DcbTag.string) string, name: string})
13
+ }
14
+
15
+ // ─────────────────────────────────────────────────────────────
16
+ // Minimal StateChangeSlice spec
17
+ // ─────────────────────────────────────────────────────────────
18
+
19
+ module AddItemSpec = {
20
+ let name = "AddItem"
21
+ module Id = Reventless.Id.String
22
+ let moduleUrl: string = %raw(`import.meta.url`)
23
+
24
+ @schema
25
+ type event = ItemAdded({id: @s.matches(Reventless.DcbTag.string) string, name: string})
26
+
27
+ @schema
28
+ type consumedEvent = ItemAdded
29
+
30
+ @schema
31
+ type command = AddItem({id: @s.matches(Reventless.DcbTag.string) string, name: string})
32
+
33
+ @schema
34
+ type error = ItemAlreadyExists
35
+
36
+ let commandSchema = commandSchema // auto-generated by @schema
37
+ }
38
+
39
+ module AddItemBehavior = {
40
+ module Spec = AddItemSpec
41
+ let moduleUrl: string = %raw(`import.meta.url`)
42
+
43
+ type state = bool // true = item exists
44
+ let initialState = false
45
+
46
+ let evolve = (_state: state, _event: Spec.consumedEvent) => true // any event means item exists
47
+
48
+ let decide = (state: state, command: Spec.command): result<array<Spec.event>, Spec.error> =>
49
+ if state {
50
+ Error(ItemAlreadyExists)
51
+ } else {
52
+ switch command {
53
+ | AddItem({id, name}) => Ok([Spec.ItemAdded({id, name})])
54
+ }
55
+ }
56
+ }
57
+
58
+ // ─────────────────────────────────────────────────────────────
59
+ // Isolated bus for this test suite
60
+ // ─────────────────────────────────────────────────────────────
61
+
62
+ module Bus = LocalBus.Make()
63
+
64
+ // Topic name = DcbEventLog name ++ "EventTopic" = "ItemEventLogEventTopic"
65
+ let capturedEventCount: ref<int> = ref(0)
66
+ let _ = Bus.subscribeToEvents("ItemEventLogEventTopic", async (_, _, _) => {
67
+ capturedEventCount := capturedEventCount.contents + 1
68
+ })
69
+
70
+ // ─────────────────────────────────────────────────────────────
71
+ // Activate Pulumi mock mode (must be before any Component.make)
72
+ // ─────────────────────────────────────────────────────────────
73
+
74
+ let _ = TestRunner.setup()
75
+
76
+ // ─────────────────────────────────────────────────────────────
77
+ // Build DcbEventLog
78
+ // ─────────────────────────────────────────────────────────────
79
+
80
+ module ItemEventLogMaker = DcbEventLog_Builder.Make(Bus)
81
+ let eventLog = ItemEventLogMaker.make(~name="ItemEventLog", ~partitionTag=Reventless.DcbTag.Simple({key: "id"}))
82
+
83
+ // ─────────────────────────────────────────────────────────────
84
+ // Build StateChangeSlice
85
+ // ─────────────────────────────────────────────────────────────
86
+
87
+ module AddItemMaker = StateChangeSlice_Builder.Make(AddItemSpec, AddItemBehavior)
88
+
89
+ // publishJsons routing — dispatches each command to its registered handler.
90
+ let publishJsons: ReventlessInfra.CommandTopic.publishJsons = async cmdJsons => {
91
+ let _ = await cmdJsons
92
+ ->Array.map(async cmdJson => {
93
+ let typeName = switch cmdJson.commandJson {
94
+ | JSON.Object(dict) =>
95
+ dict
96
+ ->Dict.get("TAG")
97
+ ->Option.flatMap(j =>
98
+ switch j {
99
+ | JSON.String(s) => Some(s)
100
+ | _ => None
101
+ }
102
+ )
103
+ ->Option.getOr("")
104
+ | _ => ""
105
+ }
106
+ let fullBody = JSON.Encode.object(
107
+ Dict.fromArray([
108
+ ("id", JSON.Encode.string(cmdJson.id)),
109
+ ("meta", cmdJson.meta->S.reverseConvertToJsonOrThrow(Reventless.Message.metaSchema)),
110
+ ("command", cmdJson.commandJson),
111
+ ]),
112
+ )
113
+ let handlers = ReventlessCore.CommandTopic.getHandlers(typeName)
114
+ let _ = await handlers
115
+ ->Array.map(async entry => {
116
+ let item: ReventlessInfra.CommandTopic.topicItem<JSON.t> = {
117
+ reference: cmdJson.id,
118
+ command: fullBody,
119
+ }
120
+ let _ = await entry.handler(Stream.fromIterable([item]))->Effect.runPromise
121
+ })
122
+ ->Promise.all
123
+ })
124
+ ->Promise.all
125
+ }
126
+
127
+ let publishJsonsOutput = publishJsons->Pulumi.Output.make
128
+
129
+ let _addItemSlice = AddItemMaker.make(~dcbEventLog=eventLog, ~publishJsons=publishJsonsOutput)
130
+
131
+ // ─────────────────────────────────────────────────────────────
132
+ // Test helpers
133
+ // ─────────────────────────────────────────────────────────────
134
+
135
+ let dispatch = async (commandJson, id) =>
136
+ await publishJsons([{Reventless.Message.id, meta: testMeta, commandJson}])
137
+
138
+ let tagQuery = (id: string): Reventless.DcbTag.query => [
139
+ {tags: [{Reventless.DcbTag.key: "id", value: id}]},
140
+ ]
141
+
142
+ let typeQuery = (eventType: string): Reventless.DcbTag.query => [{eventTypes: [eventType]}]
143
+
144
+ let addItemJson = (id, name) =>
145
+ AddItemSpec.AddItem({id, name})->S.reverseConvertToJsonOrThrow(AddItemSpec.commandSchema)