@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,207 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
4
+ import * as LocalAuth$ReventlessLocal from "../../src/adapter/Auth/LocalAuth.res.mjs";
5
+
6
+ let alice_groups = ["Editor"];
7
+
8
+ let alice = {
9
+ userId: "u-alice",
10
+ username: "alice",
11
+ groups: alice_groups,
12
+ provider: "InMemory"
13
+ };
14
+
15
+ let bob_groups = ["Viewer"];
16
+
17
+ let bob = {
18
+ userId: "u-bob",
19
+ username: "bob",
20
+ groups: bob_groups,
21
+ provider: "InMemory"
22
+ };
23
+
24
+ function buildContext(headers) {
25
+ return {
26
+ headers: Object.fromEntries(headers)
27
+ };
28
+ }
29
+
30
+ function resetAll() {
31
+ LocalAuth$ReventlessLocal.resetUsers();
32
+ LocalAuth$ReventlessLocal.Login.resetStore();
33
+ LocalAuth$ReventlessLocal.Login.setTokenSecret("test-secret-deterministic");
34
+ }
35
+
36
+ globalThis.test("issue returns Ok(token) for valid credentials", async () => {
37
+ resetAll();
38
+ LocalAuth$ReventlessLocal.Login.setCredentials("alice", "alice-pw", alice);
39
+ let result = await LocalAuth$ReventlessLocal.Login.issue("alice", "alice-pw");
40
+ if (result.TAG !== "Ok") {
41
+ return Stdlib_JsError.throwWithMessage("unexpected Error: " + result._0);
42
+ }
43
+ let parts = result._0.split(".");
44
+ globalThis.expect(parts.length).toEqual(2);
45
+ });
46
+
47
+ globalThis.test("issue returns Error for unknown username", async () => {
48
+ resetAll();
49
+ let result = await LocalAuth$ReventlessLocal.Login.issue("ghost", "x");
50
+ if (result.TAG === "Ok") {
51
+ return Stdlib_JsError.throwWithMessage("expected Error");
52
+ }
53
+ });
54
+
55
+ globalThis.test("issue returns Error for wrong password", async () => {
56
+ resetAll();
57
+ LocalAuth$ReventlessLocal.Login.setCredentials("alice", "alice-pw", alice);
58
+ let result = await LocalAuth$ReventlessLocal.Login.issue("alice", "WRONG");
59
+ if (result.TAG === "Ok") {
60
+ return Stdlib_JsError.throwWithMessage("expected Error");
61
+ }
62
+ });
63
+
64
+ globalThis.test("verifyAndDecode round-trips the identity for a fresh token", async () => {
65
+ resetAll();
66
+ LocalAuth$ReventlessLocal.Login.setCredentials("alice", "alice-pw", alice);
67
+ let t = await LocalAuth$ReventlessLocal.Login.issue("alice", "alice-pw");
68
+ let token;
69
+ token = t.TAG === "Ok" ? t._0 : Stdlib_JsError.throwWithMessage("issue failed: " + t._0);
70
+ let identity = LocalAuth$ReventlessLocal.Login.verifyAndDecode(token);
71
+ if (identity !== undefined) {
72
+ globalThis.expect(identity.username).toEqual("alice");
73
+ globalThis.expect(identity.groups).toEqual(["Editor"]);
74
+ globalThis.expect(identity.userId).toEqual("u-alice");
75
+ return;
76
+ } else {
77
+ return Stdlib_JsError.throwWithMessage("verifyAndDecode returned None");
78
+ }
79
+ });
80
+
81
+ globalThis.test("verifyAndDecode rejects a tampered payload", async () => {
82
+ resetAll();
83
+ LocalAuth$ReventlessLocal.Login.setCredentials("alice", "alice-pw", alice);
84
+ let t = await LocalAuth$ReventlessLocal.Login.issue("alice", "alice-pw");
85
+ let token;
86
+ token = t.TAG === "Ok" ? t._0 : Stdlib_JsError.throwWithMessage("issue failed: " + t._0);
87
+ let parts = token.split(".");
88
+ let payload = parts[0];
89
+ let sig = parts[1];
90
+ let firstChar = payload.charAt(0);
91
+ let replacement = firstChar === "A" ? "B" : "A";
92
+ let tamperedPayload = replacement + payload.slice(1, payload.length);
93
+ let tampered = tamperedPayload + `.` + sig;
94
+ let match = LocalAuth$ReventlessLocal.Login.verifyAndDecode(tampered);
95
+ if (match !== undefined) {
96
+ return Stdlib_JsError.throwWithMessage("expected verifyAndDecode to reject tampered payload");
97
+ }
98
+ });
99
+
100
+ globalThis.test("verifyAndDecode rejects a tampered signature", async () => {
101
+ resetAll();
102
+ LocalAuth$ReventlessLocal.Login.setCredentials("alice", "alice-pw", alice);
103
+ let t = await LocalAuth$ReventlessLocal.Login.issue("alice", "alice-pw");
104
+ let token;
105
+ token = t.TAG === "Ok" ? t._0 : Stdlib_JsError.throwWithMessage("issue failed: " + t._0);
106
+ let match = LocalAuth$ReventlessLocal.Login.verifyAndDecode(token + "x");
107
+ if (match !== undefined) {
108
+ return Stdlib_JsError.throwWithMessage("expected verifyAndDecode to reject tampered signature");
109
+ }
110
+ });
111
+
112
+ globalThis.test("verifyAndDecode rejects a malformed token (no dot)", async () => {
113
+ resetAll();
114
+ let match = LocalAuth$ReventlessLocal.Login.verifyAndDecode("not-a-real-token");
115
+ if (match !== undefined) {
116
+ return Stdlib_JsError.throwWithMessage("expected None for malformed token");
117
+ }
118
+ });
119
+
120
+ globalThis.test("authenticate accepts a valid Bearer token", async () => {
121
+ resetAll();
122
+ LocalAuth$ReventlessLocal.Login.setCredentials("bob", "bob-pw", bob);
123
+ let t = await LocalAuth$ReventlessLocal.Login.issue("bob", "bob-pw");
124
+ let token;
125
+ token = t.TAG === "Ok" ? t._0 : Stdlib_JsError.throwWithMessage("issue failed: " + t._0);
126
+ let result = await LocalAuth$ReventlessLocal.authenticate(buildContext([[
127
+ "authorization",
128
+ "Bearer " + token
129
+ ]]));
130
+ if (typeof result !== "object") {
131
+ return Stdlib_JsError.throwWithMessage("expected Authenticated(bob)");
132
+ }
133
+ if (result.TAG !== "Authenticated") {
134
+ return Stdlib_JsError.throwWithMessage("expected Authenticated(bob)");
135
+ }
136
+ let identity = result._0;
137
+ globalThis.expect(identity.username).toEqual("bob");
138
+ globalThis.expect(identity.groups).toEqual(["Viewer"]);
139
+ });
140
+
141
+ globalThis.test("authenticate rejects invalid Bearer even when X-User is present", async () => {
142
+ resetAll();
143
+ let result = await LocalAuth$ReventlessLocal.authenticate(buildContext([
144
+ [
145
+ "authorization",
146
+ "Bearer not-a-token"
147
+ ],
148
+ [
149
+ "x-user",
150
+ "admin"
151
+ ]
152
+ ]));
153
+ if (typeof result !== "object" || result.TAG === "Authenticated") {
154
+ return Stdlib_JsError.throwWithMessage("expected AuthError for invalid Bearer");
155
+ }
156
+ });
157
+
158
+ globalThis.test("Bearer outranks X-User when both present and Bearer is valid", async () => {
159
+ resetAll();
160
+ LocalAuth$ReventlessLocal.Login.setCredentials("bob", "bob-pw", bob);
161
+ let t = await LocalAuth$ReventlessLocal.Login.issue("bob", "bob-pw");
162
+ let token;
163
+ token = t.TAG === "Ok" ? t._0 : Stdlib_JsError.throwWithMessage("issue failed: " + t._0);
164
+ let result = await LocalAuth$ReventlessLocal.authenticate(buildContext([
165
+ [
166
+ "authorization",
167
+ "Bearer " + token
168
+ ],
169
+ [
170
+ "x-user",
171
+ "admin"
172
+ ]
173
+ ]));
174
+ if (typeof result !== "object") {
175
+ return Stdlib_JsError.throwWithMessage("expected Bearer to outrank X-User");
176
+ }
177
+ if (result.TAG !== "Authenticated") {
178
+ return Stdlib_JsError.throwWithMessage("expected Bearer to outrank X-User");
179
+ }
180
+ globalThis.expect(result._0.username).toEqual("bob");
181
+ });
182
+
183
+ globalThis.test("setCredentials mirrors identity into the X-User registry", async () => {
184
+ resetAll();
185
+ LocalAuth$ReventlessLocal.Login.setCredentials("alice", "alice-pw", alice);
186
+ let result = await LocalAuth$ReventlessLocal.authenticate(buildContext([[
187
+ "x-user",
188
+ "alice"
189
+ ]]));
190
+ if (typeof result !== "object") {
191
+ return Stdlib_JsError.throwWithMessage("expected Authenticated(alice) via X-User");
192
+ }
193
+ if (result.TAG !== "Authenticated") {
194
+ return Stdlib_JsError.throwWithMessage("expected Authenticated(alice) via X-User");
195
+ }
196
+ let identity = result._0;
197
+ globalThis.expect(identity.userId).toEqual("u-alice");
198
+ globalThis.expect(identity.groups).toEqual(["Editor"]);
199
+ });
200
+
201
+ export {
202
+ alice,
203
+ bob,
204
+ buildContext,
205
+ resetAll,
206
+ }
207
+ /* Not a pure module */
@@ -0,0 +1,106 @@
1
+ // Extraction matrix for LocalAuth.authenticate. Each test resets the
2
+ // user registry first so cross-test mutation in registerUser doesn't leak.
3
+
4
+ @@warning("-44")
5
+
6
+ open JestGlobals
7
+
8
+ let buildContext = (headers: array<(string, string)>): ReventlessCore.Auth_Adapter.requestContext => {
9
+ headers: Dict.fromArray(headers),
10
+ }
11
+
12
+ testPromise("no X-User, no X-Groups → defaultUser (in-memory dev convenience)", async () => {
13
+ LocalAuth.resetUsers()
14
+ let result = await LocalAuth.authenticate(buildContext([]))
15
+ switch result {
16
+ | Authenticated(identity) =>
17
+ expect(identity.username)->toEqual("user")
18
+ expect(identity.groups)->toEqual(["User"])
19
+ | _ => JsError.throwWithMessage("expected Authenticated(defaultUser)")
20
+ }
21
+ })
22
+
23
+ testPromise("X-User: admin resolves built-in admin identity", async () => {
24
+ LocalAuth.resetUsers()
25
+ let result = await LocalAuth.authenticate(
26
+ buildContext([("x-user", "admin")]),
27
+ )
28
+ switch result {
29
+ | Authenticated(identity) =>
30
+ expect(identity.username)->toEqual("admin")
31
+ expect(identity.groups)->toEqual(["Admin", "User"])
32
+ | _ => JsError.throwWithMessage("expected Authenticated")
33
+ }
34
+ })
35
+
36
+ testPromise("X-User: user resolves built-in default identity", async () => {
37
+ LocalAuth.resetUsers()
38
+ let result = await LocalAuth.authenticate(
39
+ buildContext([("x-user", "user")]),
40
+ )
41
+ switch result {
42
+ | Authenticated(identity) =>
43
+ expect(identity.username)->toEqual("user")
44
+ expect(identity.groups)->toEqual(["User"])
45
+ | _ => JsError.throwWithMessage("expected Authenticated")
46
+ }
47
+ })
48
+
49
+ testPromise("unknown X-User falls back to Anonymous", async () => {
50
+ LocalAuth.resetUsers()
51
+ let result = await LocalAuth.authenticate(
52
+ buildContext([("x-user", "unknown")]),
53
+ )
54
+ switch result {
55
+ | Anonymous => ()
56
+ | _ => JsError.throwWithMessage("expected Anonymous")
57
+ }
58
+ })
59
+
60
+ testPromise("X-Groups overrides the resolved identity's groups", async () => {
61
+ LocalAuth.resetUsers()
62
+ let result = await LocalAuth.authenticate(
63
+ buildContext([("x-user", "admin"), ("x-groups", "Editor, Viewer")]),
64
+ )
65
+ switch result {
66
+ | Authenticated(identity) =>
67
+ expect(identity.username)->toEqual("admin")
68
+ expect(identity.groups)->toEqual(["Editor", "Viewer"])
69
+ | _ => JsError.throwWithMessage("expected Authenticated")
70
+ }
71
+ })
72
+
73
+ testPromise("X-Groups alone yields defaultUser tagged with the override groups", async () => {
74
+ LocalAuth.resetUsers()
75
+ let result = await LocalAuth.authenticate(
76
+ buildContext([("x-groups", "Tester")]),
77
+ )
78
+ switch result {
79
+ | Authenticated(identity) =>
80
+ expect(identity.userId)->toEqual("local-user")
81
+ expect(identity.groups)->toEqual(["Tester"])
82
+ | _ => JsError.throwWithMessage("expected Authenticated")
83
+ }
84
+ })
85
+
86
+ testPromise("registerUser injects a custom identity", async () => {
87
+ LocalAuth.resetUsers()
88
+ LocalAuth.registerUser(
89
+ ~username="alice",
90
+ ~identity={
91
+ userId: "alice-id",
92
+ username: "alice",
93
+ groups: ["Editor"],
94
+ provider: InMemory,
95
+ },
96
+ )
97
+ let result = await LocalAuth.authenticate(
98
+ buildContext([("x-user", "alice")]),
99
+ )
100
+ switch result {
101
+ | Authenticated(identity) =>
102
+ expect(identity.userId)->toEqual("alice-id")
103
+ expect(identity.groups)->toEqual(["Editor"])
104
+ | _ => JsError.throwWithMessage("expected Authenticated")
105
+ }
106
+ })
@@ -0,0 +1,145 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
4
+ import * as LocalAuth$ReventlessLocal from "../../src/adapter/Auth/LocalAuth.res.mjs";
5
+
6
+ function buildContext(headers) {
7
+ return {
8
+ headers: Object.fromEntries(headers)
9
+ };
10
+ }
11
+
12
+ globalThis.test("no X-User, no X-Groups → defaultUser (in-memory dev convenience)", async () => {
13
+ LocalAuth$ReventlessLocal.resetUsers();
14
+ let result = await LocalAuth$ReventlessLocal.authenticate(buildContext([]));
15
+ if (typeof result !== "object") {
16
+ return Stdlib_JsError.throwWithMessage("expected Authenticated(defaultUser)");
17
+ }
18
+ if (result.TAG !== "Authenticated") {
19
+ return Stdlib_JsError.throwWithMessage("expected Authenticated(defaultUser)");
20
+ }
21
+ let identity = result._0;
22
+ globalThis.expect(identity.username).toEqual("user");
23
+ globalThis.expect(identity.groups).toEqual(["User"]);
24
+ });
25
+
26
+ globalThis.test("X-User: admin resolves built-in admin identity", async () => {
27
+ LocalAuth$ReventlessLocal.resetUsers();
28
+ let result = await LocalAuth$ReventlessLocal.authenticate(buildContext([[
29
+ "x-user",
30
+ "admin"
31
+ ]]));
32
+ if (typeof result !== "object") {
33
+ return Stdlib_JsError.throwWithMessage("expected Authenticated");
34
+ }
35
+ if (result.TAG !== "Authenticated") {
36
+ return Stdlib_JsError.throwWithMessage("expected Authenticated");
37
+ }
38
+ let identity = result._0;
39
+ globalThis.expect(identity.username).toEqual("admin");
40
+ globalThis.expect(identity.groups).toEqual([
41
+ "Admin",
42
+ "User"
43
+ ]);
44
+ });
45
+
46
+ globalThis.test("X-User: user resolves built-in default identity", async () => {
47
+ LocalAuth$ReventlessLocal.resetUsers();
48
+ let result = await LocalAuth$ReventlessLocal.authenticate(buildContext([[
49
+ "x-user",
50
+ "user"
51
+ ]]));
52
+ if (typeof result !== "object") {
53
+ return Stdlib_JsError.throwWithMessage("expected Authenticated");
54
+ }
55
+ if (result.TAG !== "Authenticated") {
56
+ return Stdlib_JsError.throwWithMessage("expected Authenticated");
57
+ }
58
+ let identity = result._0;
59
+ globalThis.expect(identity.username).toEqual("user");
60
+ globalThis.expect(identity.groups).toEqual(["User"]);
61
+ });
62
+
63
+ globalThis.test("unknown X-User falls back to Anonymous", async () => {
64
+ LocalAuth$ReventlessLocal.resetUsers();
65
+ let result = await LocalAuth$ReventlessLocal.authenticate(buildContext([[
66
+ "x-user",
67
+ "unknown"
68
+ ]]));
69
+ if (typeof result !== "object") {
70
+ return;
71
+ } else {
72
+ return Stdlib_JsError.throwWithMessage("expected Anonymous");
73
+ }
74
+ });
75
+
76
+ globalThis.test("X-Groups overrides the resolved identity's groups", async () => {
77
+ LocalAuth$ReventlessLocal.resetUsers();
78
+ let result = await LocalAuth$ReventlessLocal.authenticate(buildContext([
79
+ [
80
+ "x-user",
81
+ "admin"
82
+ ],
83
+ [
84
+ "x-groups",
85
+ "Editor, Viewer"
86
+ ]
87
+ ]));
88
+ if (typeof result !== "object") {
89
+ return Stdlib_JsError.throwWithMessage("expected Authenticated");
90
+ }
91
+ if (result.TAG !== "Authenticated") {
92
+ return Stdlib_JsError.throwWithMessage("expected Authenticated");
93
+ }
94
+ let identity = result._0;
95
+ globalThis.expect(identity.username).toEqual("admin");
96
+ globalThis.expect(identity.groups).toEqual([
97
+ "Editor",
98
+ "Viewer"
99
+ ]);
100
+ });
101
+
102
+ globalThis.test("X-Groups alone yields defaultUser tagged with the override groups", async () => {
103
+ LocalAuth$ReventlessLocal.resetUsers();
104
+ let result = await LocalAuth$ReventlessLocal.authenticate(buildContext([[
105
+ "x-groups",
106
+ "Tester"
107
+ ]]));
108
+ if (typeof result !== "object") {
109
+ return Stdlib_JsError.throwWithMessage("expected Authenticated");
110
+ }
111
+ if (result.TAG !== "Authenticated") {
112
+ return Stdlib_JsError.throwWithMessage("expected Authenticated");
113
+ }
114
+ let identity = result._0;
115
+ globalThis.expect(identity.userId).toEqual("local-user");
116
+ globalThis.expect(identity.groups).toEqual(["Tester"]);
117
+ });
118
+
119
+ globalThis.test("registerUser injects a custom identity", async () => {
120
+ LocalAuth$ReventlessLocal.resetUsers();
121
+ LocalAuth$ReventlessLocal.registerUser("alice", {
122
+ userId: "alice-id",
123
+ username: "alice",
124
+ groups: ["Editor"],
125
+ provider: "InMemory"
126
+ });
127
+ let result = await LocalAuth$ReventlessLocal.authenticate(buildContext([[
128
+ "x-user",
129
+ "alice"
130
+ ]]));
131
+ if (typeof result !== "object") {
132
+ return Stdlib_JsError.throwWithMessage("expected Authenticated");
133
+ }
134
+ if (result.TAG !== "Authenticated") {
135
+ return Stdlib_JsError.throwWithMessage("expected Authenticated");
136
+ }
137
+ let identity = result._0;
138
+ globalThis.expect(identity.userId).toEqual("alice-id");
139
+ globalThis.expect(identity.groups).toEqual(["Editor"]);
140
+ });
141
+
142
+ export {
143
+ buildContext,
144
+ }
145
+ /* Not a pure module */
@@ -0,0 +1,172 @@
1
+ // Stage A4 — UserStore.load resolution order and YAML parsing.
2
+
3
+ @@warning("-44")
4
+
5
+ open JestGlobals
6
+
7
+ @module("node:fs") external _writeFileSync: (string, string, string) => unit = "writeFileSync"
8
+ @module("node:fs") external _mkdtempSync: string => string = "mkdtempSync"
9
+ @module("node:fs") external _rmSync: (string, {..}) => unit = "rmSync"
10
+ @module("node:path") external _join: (string, string) => string = "join"
11
+ @module("node:os") external _tmpdir: unit => string = "tmpdir"
12
+
13
+ let resetAll = () => {
14
+ LocalAuth.resetUsers()
15
+ LocalAuth.Login.resetStore()
16
+ UserStore.resetResolution()
17
+ }
18
+
19
+ let _writeYamlFile = (contents: string): string => {
20
+ let dir = _mkdtempSync(_join(_tmpdir(), "reventless-userstore-"))
21
+ let path = _join(dir, "users.yaml")
22
+ _writeFileSync(path, contents, "utf8")
23
+ path
24
+ }
25
+
26
+ testPromise("parseString decodes a well-formed YAML document", async () => {
27
+ let yaml = `
28
+ - username: alice
29
+ password: alice-pw
30
+ groups: [Editor]
31
+ - username: bob
32
+ password: bob-pw
33
+ groups: []
34
+ `
35
+ switch UserStore.parseString(yaml) {
36
+ | Ok(entries) =>
37
+ expect(entries->Array.length)->toEqual(2)
38
+ let first = entries->Array.getUnsafe(0)
39
+ expect(first.username)->toEqual("alice")
40
+ expect(first.password)->toEqual("alice-pw")
41
+ expect(first.groups)->toEqual(["Editor"])
42
+ | Error(msg) => JsError.throwWithMessage("parseString failed: " ++ msg)
43
+ }
44
+ })
45
+
46
+ testPromise("parseString rejects malformed YAML", async () => {
47
+ switch UserStore.parseString("not: valid: yaml: at all: ::") {
48
+ | Error(_) => ()
49
+ | Ok(_) => JsError.throwWithMessage("expected Error for malformed YAML")
50
+ }
51
+ })
52
+
53
+ testPromise("parseString rejects entries missing required fields", async () => {
54
+ let yaml = `
55
+ - username: alice
56
+ groups: [Editor]
57
+ `
58
+ switch UserStore.parseString(yaml) {
59
+ | Error(_) => ()
60
+ | Ok(_) => JsError.throwWithMessage("expected Error for missing password")
61
+ }
62
+ })
63
+
64
+ testPromise("load(~users) registers credentials and returns InlineUsers", async () => {
65
+ resetAll()
66
+ let entries: array<UserStore.entry> = [
67
+ {username: "alice", password: "alice-pw", groups: ["Editor"]},
68
+ {username: "bob", password: "bob-pw", groups: [], userId: "u-bob"},
69
+ ]
70
+ switch UserStore.load(~users=entries, ()) {
71
+ | Ok(InlineUsers) => ()
72
+ | Ok(_) => JsError.throwWithMessage("expected InlineUsers")
73
+ | Error(msg) => JsError.throwWithMessage("load failed: " ++ msg)
74
+ }
75
+ // Verify Login.issue accepts the credentials.
76
+ let result = await LocalAuth.Login.issue(~username="alice", ~password="alice-pw")
77
+ switch result {
78
+ | Ok(_) => ()
79
+ | Error(msg) => JsError.throwWithMessage("issue rejected loaded credentials: " ++ msg)
80
+ }
81
+ // userId override is honored.
82
+ switch LocalAuth.lookupUser("bob") {
83
+ | Some(identity) => expect(identity.userId)->toEqual("u-bob")
84
+ | None => JsError.throwWithMessage("bob not in user registry")
85
+ }
86
+ })
87
+
88
+ testPromise("load(~usersFile) reads a YAML file from disk", async () => {
89
+ resetAll()
90
+ let path = _writeYamlFile(`
91
+ - username: carol
92
+ password: carol-pw
93
+ groups: [Admin]
94
+ `)
95
+ switch UserStore.load(~usersFile=path, ()) {
96
+ | Ok(UsersFile(p)) => expect(p)->toEqual(path)
97
+ | Ok(_) => JsError.throwWithMessage("expected UsersFile resolution")
98
+ | Error(msg) => JsError.throwWithMessage("load failed: " ++ msg)
99
+ }
100
+ let result = await LocalAuth.Login.issue(~username="carol", ~password="carol-pw")
101
+ switch result {
102
+ | Ok(_) => ()
103
+ | Error(msg) => JsError.throwWithMessage("issue rejected file-loaded credentials: " ++ msg)
104
+ }
105
+ _rmSync(path, {"force": true})
106
+ })
107
+
108
+ testPromise("load(~usersFile) returns Error when the file is missing", async () => {
109
+ resetAll()
110
+ switch UserStore.load(~usersFile="/tmp/does-not-exist-reventless.yaml", ()) {
111
+ | Error(_) => ()
112
+ | Ok(_) => JsError.throwWithMessage("expected Error for missing file")
113
+ }
114
+ })
115
+
116
+ testPromise("load() with no args + no default file returns Empty", async () => {
117
+ resetAll()
118
+ // The test's cwd has no .reventless/users.yaml — verify Empty is returned.
119
+ // (The package's own cwd is reventless-local/ during `pnpm test`, where
120
+ // no such file exists.)
121
+ switch UserStore.load() {
122
+ | Ok(Empty) => ()
123
+ | Ok(DefaultFile(_)) =>
124
+ // If a .reventless/users.yaml happens to exist in CI, accept it.
125
+ ()
126
+ | Ok(_) => JsError.throwWithMessage("expected Empty or DefaultFile")
127
+ | Error(msg) => JsError.throwWithMessage("load failed: " ++ msg)
128
+ }
129
+ })
130
+
131
+ testPromise("inline users win over a file path", async () => {
132
+ resetAll()
133
+ let path = _writeYamlFile(`
134
+ - username: file-user
135
+ password: file-pw
136
+ groups: []
137
+ `)
138
+ let inline: array<UserStore.entry> = [
139
+ {username: "inline-user", password: "inline-pw", groups: []},
140
+ ]
141
+ switch UserStore.load(~users=inline, ~usersFile=path, ()) {
142
+ | Ok(InlineUsers) => ()
143
+ | Ok(_) => JsError.throwWithMessage("expected InlineUsers (inline should win)")
144
+ | Error(msg) => JsError.throwWithMessage("load failed: " ++ msg)
145
+ }
146
+ // file-user must NOT be in the registry.
147
+ switch LocalAuth.lookupUser("file-user") {
148
+ | None => ()
149
+ | Some(_) => JsError.throwWithMessage("file-user leaked into registry despite inline override")
150
+ }
151
+ switch LocalAuth.lookupUser("inline-user") {
152
+ | Some(_) => ()
153
+ | None => JsError.throwWithMessage("inline-user missing from registry")
154
+ }
155
+ _rmSync(path, {"force": true})
156
+ })
157
+
158
+ testPromise("autoLoadOnce is a no-op after an explicit load", async () => {
159
+ resetAll()
160
+ let _ = UserStore.load(
161
+ ~users=[{username: "x", password: "x", groups: []}],
162
+ (),
163
+ )
164
+ // Second call should not crash even if .reventless/users.yaml doesn't
165
+ // exist; resolved=true prevents the default-discovery branch.
166
+ UserStore.autoLoadOnce()
167
+ // Sanity: explicit-load registration persists.
168
+ switch LocalAuth.lookupUser("x") {
169
+ | Some(_) => ()
170
+ | None => JsError.throwWithMessage("autoLoadOnce wiped explicit registration")
171
+ }
172
+ })