@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,339 @@
1
+ // SQLite-backed QueryDb storage.
2
+ //
3
+ // One table per registered QueryDb (`qdb_<name>`). Columns:
4
+ // partition_key TEXT
5
+ // sub_key TEXT
6
+ // item TEXT (JSON.stringify of the item)
7
+ // expires_at INTEGER NULL (unix epoch seconds; null = never expires)
8
+ // PRIMARY KEY (partition_key, sub_key)
9
+ //
10
+ // TTL: every read clause carries `(expires_at IS NULL OR expires_at > <now>)`.
11
+ // Lazy expiry — no background sweeper. Expired rows linger on disk until they
12
+ // are next read (where they are filtered out) or overwritten by a new save.
13
+ //
14
+ // GSI: each declared `indexConfig` creates a SQLite index on the JSON column
15
+ // using `json_extract(item, '$.<field>')`. Composite keys (`pkFields` /
16
+ // `skFields` joined by `pkSep`/`skSep`) become a single computed expression.
17
+ // The DynamoDB `projectionType` is recorded for documentation but the SQLite
18
+ // covering-index distinction is irrelevant — every column is on the same row.
19
+
20
+ open ReventlessCore
21
+ open Reventless.ReadModel
22
+
23
+ let tableName = (name: string): string => "qdb_" ++ name->String.replaceAll("-", "_")
24
+
25
+ // SQL identifier sanitisation — collapse anything outside [A-Za-z0-9_] to _.
26
+ // Keeps generated index names predictable and injection-safe.
27
+ let sanitizeIdent = (s: string): string =>
28
+ s
29
+ ->String.split("")
30
+ ->Array.map(ch => {
31
+ let code = ch->String.codePointAt(0)->Option.getOr(0)
32
+ let isAlphaNum =
33
+ (code >= 48 && code <= 57) ||
34
+ (code >= 65 && code <= 90) ||
35
+ (code >= 97 && code <= 122) ||
36
+ code == 95
37
+ isAlphaNum ? ch : "_"
38
+ })
39
+ ->Array.join("")
40
+
41
+ let ensureSchema = (~db, ~table) =>
42
+ db->SqliteDriver.exec(
43
+ `CREATE TABLE IF NOT EXISTS ${table} (partition_key TEXT NOT NULL, sub_key TEXT NOT NULL DEFAULT '', item TEXT NOT NULL, expires_at INTEGER, PRIMARY KEY (partition_key, sub_key))`,
44
+ )
45
+
46
+ // Best-effort migration for tables created by an earlier (Phase 2) version
47
+ // that did not have the expires_at column. Safe no-op when column already
48
+ // exists. Wrapped in try/catch since some sqlite versions throw on duplicate.
49
+ let ensureExpiresColumn = (~db, ~table) =>
50
+ try db->SqliteDriver.exec(`ALTER TABLE ${table} ADD COLUMN expires_at INTEGER`) catch {
51
+ | _ => ()
52
+ }
53
+
54
+ // Build the json_extract expression for a single field path.
55
+ let jsonField = (field: string): string => `json_extract(item, '$.${field}')`
56
+
57
+ // Composite-key indexes are concatenations: json_extract(...) || sep || json_extract(...) || ...
58
+ let compositeExpr = (fields: array<string>, sep: string): string => {
59
+ let escapedSep = sep->String.replaceAll("'", "''")
60
+ fields->Array.map(jsonField)->Array.join(` || '${escapedSep}' || `)
61
+ }
62
+
63
+ // Determine the index's partition expression and (optional) sort expression.
64
+ // Uses pkFields if present, else falls back to idField; same for sub key.
65
+ let indexExpressions = (idx: indexConfig): (string, option<string>) => {
66
+ let pkExpr = switch idx.pkFields {
67
+ | Some(fields) if fields->Array.length > 0 =>
68
+ compositeExpr(fields, idx.pkSep->Option.getOr("/"))
69
+ | _ =>
70
+ switch idx.idField {
71
+ | Some(field) => jsonField(field)
72
+ | None => jsonField(idx.index)
73
+ }
74
+ }
75
+ let skExpr = switch idx.skFields {
76
+ | Some(fields) if fields->Array.length > 0 =>
77
+ Some(compositeExpr(fields, idx.skSep->Option.getOr("/")))
78
+ | _ =>
79
+ switch idx.subIdField {
80
+ | Some(field) => Some(jsonField(field))
81
+ | None => None
82
+ }
83
+ }
84
+ (pkExpr, skExpr)
85
+ }
86
+
87
+ // Create one SQLite index per declared GSI. Idempotent via IF NOT EXISTS.
88
+ let ensureIndexes = (~db, ~table, ~indexes: array<indexConfig>) =>
89
+ indexes->Array.forEach(idx => {
90
+ let (pkExpr, skExprOpt) = indexExpressions(idx)
91
+ let indexName = `idx_${table}_${sanitizeIdent(idx.index)}`
92
+ let cols = switch skExprOpt {
93
+ | Some(skExpr) => `${pkExpr}, ${skExpr}`
94
+ | None => pkExpr
95
+ }
96
+ db->SqliteDriver.exec(`CREATE INDEX IF NOT EXISTS ${indexName} ON ${table} (${cols})`)
97
+ })
98
+
99
+ let decodeItem = (row: dict<JSON.t>): JSON.t =>
100
+ switch row->Dict.get("item") {
101
+ | Some(JSON.String(s)) =>
102
+ switch JSON.parseOrThrow(s) {
103
+ | json => json
104
+ | exception _ => JSON.Encode.null
105
+ }
106
+ | _ => JSON.Encode.null
107
+ }
108
+
109
+ let computeSubKey = (item: JSON.t, subIdField: option<string>): string =>
110
+ switch subIdField {
111
+ | None => ""
112
+ | Some(field) =>
113
+ switch item->JSON.Decode.object {
114
+ | Some(obj) =>
115
+ switch obj->Dict.get(field) {
116
+ | Some(JSON.String(s)) => s
117
+ | _ => ""
118
+ }
119
+ | None => ""
120
+ }
121
+ }
122
+
123
+ let withId = (item: JSON.t, partitionKey: string): JSON.t =>
124
+ switch item->JSON.Decode.object {
125
+ | Some(obj) =>
126
+ if obj->Dict.get("id")->Option.isSome {
127
+ item
128
+ } else {
129
+ let copy = Dict.make()
130
+ obj->Dict.toArray->Array.forEach(((k, v)) => copy->Dict.set(k, v))
131
+ copy->Dict.set("id", JSON.Encode.string(partitionKey))
132
+ JSON.Encode.object(copy)
133
+ }
134
+ | None => item
135
+ }
136
+
137
+ type busCallbacks = {
138
+ publishStateChange: (~name: string, ~descriptor: JSON.t) => unit,
139
+ registerQueryDb: (string, QueryDb_Adapter.operations) => unit,
140
+ registerQueryDbScan: (string, unit => array<JSON.t>) => unit,
141
+ registerQueryDbStream: (string, unit => Stream.t<JSON.t, string, unit>) => unit,
142
+ }
143
+
144
+ // SQL fragment that excludes expired rows. Uses unix-epoch seconds via
145
+ // strftime; SQLite evaluates it on every row, which is fine at dev scale.
146
+ let notExpiredClause = "(expires_at IS NULL OR expires_at > strftime('%s','now'))"
147
+
148
+ let makeStorage = (
149
+ ~db: SqliteDriver.t,
150
+ ~bus: busCallbacks,
151
+ ~name,
152
+ ~indexes: array<indexConfig>,
153
+ ~subIdField: option<string>,
154
+ ): QueryDb_Adapter.storage => {
155
+ let table = tableName(name)
156
+ ensureSchema(~db, ~table)
157
+ ensureExpiresColumn(~db, ~table)
158
+ ensureIndexes(~db, ~table, ~indexes)
159
+
160
+ let upsertStmt = db->SqliteDriver.prepare(
161
+ `INSERT INTO ${table}(partition_key, sub_key, item, expires_at) VALUES(?,?,?,?) ON CONFLICT(partition_key, sub_key) DO UPDATE SET item = excluded.item, expires_at = excluded.expires_at`,
162
+ )
163
+ let selectByPartitionStmt = db->SqliteDriver.prepare(
164
+ `SELECT partition_key, sub_key, item FROM ${table} WHERE partition_key = ? AND ${notExpiredClause} ORDER BY sub_key ASC`,
165
+ )
166
+ let deleteByPartitionStmt = db->SqliteDriver.prepare(
167
+ `DELETE FROM ${table} WHERE partition_key = ?`,
168
+ )
169
+ let deleteBySubKeyStmt = db->SqliteDriver.prepare(
170
+ `DELETE FROM ${table} WHERE partition_key = ? AND sub_key = ?`,
171
+ )
172
+ let scanAllStmt = db->SqliteDriver.prepare(
173
+ `SELECT partition_key, sub_key, item FROM ${table} WHERE ${notExpiredClause} ORDER BY partition_key, sub_key`,
174
+ )
175
+
176
+ let rowsFor = (id: string): array<JSON.t> =>
177
+ selectByPartitionStmt
178
+ ->SqliteDriver.all([JSON.Encode.string(id)])
179
+ ->Array.map(decodeItem)
180
+
181
+ let allRows = (): array<JSON.t> =>
182
+ scanAllStmt
183
+ ->SqliteDriver.all([])
184
+ ->Array.map(row => {
185
+ let item = decodeItem(row)
186
+ let partition = switch row->Dict.get("partition_key") {
187
+ | Some(JSON.String(s)) => s
188
+ | _ => ""
189
+ }
190
+ withId(item, partition)
191
+ })
192
+
193
+ let saveOne = (id: string, state: JSON.t, ttl: option<int>) => {
194
+ let subKey = computeSubKey(state, subIdField)
195
+ let expiresAt = switch ttl {
196
+ | Some(t) => JSON.Encode.int(t)
197
+ | None => JSON.Encode.null
198
+ }
199
+ upsertStmt->SqliteDriver.run([
200
+ JSON.Encode.string(id),
201
+ JSON.Encode.string(subKey),
202
+ JSON.Encode.string(JSON.stringify(state)),
203
+ expiresAt,
204
+ ])
205
+ }
206
+
207
+ let load: QueryDb.load<string, JSON.t> = async id => Ok(rowsFor(id))
208
+
209
+ let loadStream: QueryDb.loadStream<string, JSON.t> = id => rowsFor(id)->Stream.fromIterable
210
+
211
+ // Entity key matches the AWS StateTopic Lambda output (Phase 1):
212
+ // single-key tables → partition value; composite tables → `pk-sk`.
213
+ let entityKeyFor = (id: string, subKey: string): string =>
214
+ switch subIdField {
215
+ | Some(_) => id ++ "-" ++ subKey
216
+ | None => id
217
+ }
218
+
219
+ let publishUpdated = (id: string, state: JSON.t) => {
220
+ let subKey = computeSubKey(state, subIdField)
221
+ let descriptor = LocalBus.makeStateChangeDescriptor(
222
+ ~changeKind="Updated",
223
+ ~id=entityKeyFor(id, subKey),
224
+ ~state=Some(state),
225
+ )
226
+ bus.publishStateChange(~name, ~descriptor)
227
+ }
228
+
229
+ let publishRemoved = (id: string, subKey: string) => {
230
+ let descriptor = LocalBus.makeStateChangeDescriptor(
231
+ ~changeKind="Removed",
232
+ ~id=entityKeyFor(id, subKey),
233
+ ~state=None,
234
+ )
235
+ bus.publishStateChange(~name, ~descriptor)
236
+ }
237
+
238
+ // Read all (id, sub_key) tuples that a partition-level delete would remove.
239
+ let rowKeysForPartition = (id: string): array<string> =>
240
+ selectByPartitionStmt
241
+ ->SqliteDriver.all([JSON.Encode.string(id)])
242
+ ->Array.map(row =>
243
+ switch row->Dict.get("sub_key") {
244
+ | Some(JSON.String(s)) => s
245
+ | _ => ""
246
+ }
247
+ )
248
+
249
+ let save: QueryDb.save<string, JSON.t> = async (id, state, _saveMode, ttl) => {
250
+ saveOne(id, state, ttl)
251
+ publishUpdated(id, state)
252
+ Ok()
253
+ }
254
+
255
+ let saveBatch: QueryDb.saveBatch<string, JSON.t> = async batch => {
256
+ db->SqliteDriver.transaction(() =>
257
+ batch->Array.forEach(((id, state, ttl)) => saveOne(id, state, ttl))
258
+ )
259
+ batch->Array.forEach(((id, state, _)) => publishUpdated(id, state))
260
+ Ok()
261
+ }
262
+
263
+ let count: QueryDb.count<string> = async (_id, _fieldName, inc) => Ok(inc)
264
+
265
+ let deleteOne = (id: string, subIdOpt) =>
266
+ switch subIdOpt {
267
+ | None => deleteByPartitionStmt->SqliteDriver.run([JSON.Encode.string(id)])
268
+ | Some((_, subValue)) =>
269
+ deleteBySubKeyStmt->SqliteDriver.run([
270
+ JSON.Encode.string(id),
271
+ JSON.Encode.string(subValue),
272
+ ])
273
+ }
274
+
275
+ let delete: QueryDb.delete<string> = async (id, subIdOpt) => {
276
+ let removedSubKeys = switch subIdOpt {
277
+ | None => rowKeysForPartition(id)
278
+ | Some((_, subValue)) => [subValue]
279
+ }
280
+ deleteOne(id, subIdOpt)
281
+ removedSubKeys->Array.forEach(sk => publishRemoved(id, sk))
282
+ Ok()
283
+ }
284
+
285
+ let deleteBatch: QueryDb.deleteBatch<string> = async ids => {
286
+ let removed: array<(string, string)> = []
287
+ ids->Array.forEach(((id, subIdOpt)) => {
288
+ switch subIdOpt {
289
+ | None => rowKeysForPartition(id)->Array.forEach(sk => removed->Array.push((id, sk)))
290
+ | Some((_, subValue)) => removed->Array.push((id, subValue))
291
+ }
292
+ })
293
+ db->SqliteDriver.transaction(() => ids->Array.forEach(((id, s)) => deleteOne(id, s)))
294
+ removed->Array.forEach(((id, sk)) => publishRemoved(id, sk))
295
+ Ok()
296
+ }
297
+
298
+ let ops: QueryDb_Adapter.operations = {
299
+ load,
300
+ loadStream,
301
+ save,
302
+ saveBatch,
303
+ count,
304
+ delete,
305
+ deleteBatch,
306
+ }
307
+
308
+ bus.registerQueryDb(name, ops)
309
+ bus.registerQueryDbScan(name, () => allRows())
310
+ bus.registerQueryDbStream(name, () => allRows()->Stream.fromIterable)
311
+
312
+ {
313
+ resources: [],
314
+ dataSourceName: ""->Pulumi.Output.make,
315
+ operations: Pulumi.Output.make(ops),
316
+ }
317
+ }
318
+
319
+ module Make = (Bus: LocalBus.T, DbProvider: {let db: SqliteDriver.t}) => {
320
+ type api = unit
321
+ type role = unit
322
+
323
+ let busCallbacks = {
324
+ publishStateChange: Bus.publishStateChange,
325
+ registerQueryDb: Bus.registerQueryDb,
326
+ registerQueryDbScan: Bus.registerQueryDbScan,
327
+ registerQueryDbStream: Bus.registerQueryDbStream,
328
+ }
329
+
330
+ let make: QueryDb_Adapter.storageMaker<unit, unit> = (
331
+ ~name,
332
+ ~indexes,
333
+ ~subIdField=?,
334
+ ~ttl as _=?,
335
+ ~api as _,
336
+ ~apiRole as _,
337
+ ~opts as _,
338
+ ) => makeStorage(~db=DbProvider.db, ~bus=busCallbacks, ~name, ~indexes, ~subIdField)
339
+ }
@@ -0,0 +1,318 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
4
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
+ import * as Stream from "effect/Stream";
6
+ import * as Pulumi from "@pulumi/pulumi";
7
+ import * as LocalBus$ReventlessLocal from "../LocalBus.res.mjs";
8
+ import * as SqliteDriver$ReventlessLocal from "../SqliteDriver.res.mjs";
9
+
10
+ function tableName(name) {
11
+ return "qdb_" + name.replaceAll("-", "_");
12
+ }
13
+
14
+ function sanitizeIdent(s) {
15
+ return s.split("").map(ch => {
16
+ let code = Stdlib_Option.getOr(ch.codePointAt(0), 0);
17
+ let isAlphaNum = code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 95;
18
+ if (isAlphaNum) {
19
+ return ch;
20
+ } else {
21
+ return "_";
22
+ }
23
+ }).join("");
24
+ }
25
+
26
+ function ensureSchema(db, table) {
27
+ SqliteDriver$ReventlessLocal.exec(db, `CREATE TABLE IF NOT EXISTS ` + table + ` (partition_key TEXT NOT NULL, sub_key TEXT NOT NULL DEFAULT '', item TEXT NOT NULL, expires_at INTEGER, PRIMARY KEY (partition_key, sub_key))`);
28
+ }
29
+
30
+ function ensureExpiresColumn(db, table) {
31
+ try {
32
+ return SqliteDriver$ReventlessLocal.exec(db, `ALTER TABLE ` + table + ` ADD COLUMN expires_at INTEGER`);
33
+ } catch (exn) {
34
+ return;
35
+ }
36
+ }
37
+
38
+ function jsonField(field) {
39
+ return `json_extract(item, '$.` + field + `')`;
40
+ }
41
+
42
+ function compositeExpr(fields, sep) {
43
+ let escapedSep = sep.replaceAll("'", "''");
44
+ return fields.map(jsonField).join(` || '` + escapedSep + `' || `);
45
+ }
46
+
47
+ function indexExpressions(idx) {
48
+ let fields = idx.pkFields;
49
+ let pkExpr;
50
+ let exit = 0;
51
+ if (fields !== undefined && fields.length !== 0) {
52
+ pkExpr = compositeExpr(fields, Stdlib_Option.getOr(idx.pkSep, "/"));
53
+ } else {
54
+ exit = 1;
55
+ }
56
+ if (exit === 1) {
57
+ let field = idx.idField;
58
+ pkExpr = field !== undefined ? jsonField(field) : jsonField(idx.index);
59
+ }
60
+ let fields$1 = idx.skFields;
61
+ let skExpr;
62
+ let exit$1 = 0;
63
+ if (fields$1 !== undefined && fields$1.length !== 0) {
64
+ skExpr = compositeExpr(fields$1, Stdlib_Option.getOr(idx.skSep, "/"));
65
+ } else {
66
+ exit$1 = 1;
67
+ }
68
+ if (exit$1 === 1) {
69
+ let field$1 = idx.subIdField;
70
+ skExpr = field$1 !== undefined ? jsonField(field$1) : undefined;
71
+ }
72
+ return [
73
+ pkExpr,
74
+ skExpr
75
+ ];
76
+ }
77
+
78
+ function ensureIndexes(db, table, indexes) {
79
+ indexes.forEach(idx => {
80
+ let match = indexExpressions(idx);
81
+ let skExprOpt = match[1];
82
+ let pkExpr = match[0];
83
+ let indexName = `idx_` + table + `_` + sanitizeIdent(idx.index);
84
+ let cols = skExprOpt !== undefined ? pkExpr + `, ` + skExprOpt : pkExpr;
85
+ SqliteDriver$ReventlessLocal.exec(db, `CREATE INDEX IF NOT EXISTS ` + indexName + ` ON ` + table + ` (` + cols + `)`);
86
+ });
87
+ }
88
+
89
+ function decodeItem(row) {
90
+ let match = row["item"];
91
+ if (match === undefined) {
92
+ return null;
93
+ }
94
+ if (typeof match !== "string") {
95
+ return null;
96
+ }
97
+ try {
98
+ return JSON.parse(match);
99
+ } catch (exn) {
100
+ return null;
101
+ }
102
+ }
103
+
104
+ function computeSubKey(item, subIdField) {
105
+ if (subIdField === undefined) {
106
+ return "";
107
+ }
108
+ let obj = Stdlib_JSON.Decode.object(item);
109
+ if (obj === undefined) {
110
+ return "";
111
+ }
112
+ let match = obj[subIdField];
113
+ if (typeof match === "string") {
114
+ return match;
115
+ } else {
116
+ return "";
117
+ }
118
+ }
119
+
120
+ function withId(item, partitionKey) {
121
+ let obj = Stdlib_JSON.Decode.object(item);
122
+ if (obj === undefined) {
123
+ return item;
124
+ }
125
+ if (Stdlib_Option.isSome(obj["id"])) {
126
+ return item;
127
+ }
128
+ let copy = {};
129
+ Object.entries(obj).forEach(param => {
130
+ copy[param[0]] = param[1];
131
+ });
132
+ copy["id"] = partitionKey;
133
+ return copy;
134
+ }
135
+
136
+ let notExpiredClause = "(expires_at IS NULL OR expires_at > strftime('%s','now'))";
137
+
138
+ function makeStorage(db, bus, name, indexes, subIdField) {
139
+ let table = tableName(name);
140
+ ensureSchema(db, table);
141
+ ensureExpiresColumn(db, table);
142
+ ensureIndexes(db, table, indexes);
143
+ let upsertStmt = SqliteDriver$ReventlessLocal.prepare(db, `INSERT INTO ` + table + `(partition_key, sub_key, item, expires_at) VALUES(?,?,?,?) ON CONFLICT(partition_key, sub_key) DO UPDATE SET item = excluded.item, expires_at = excluded.expires_at`);
144
+ let selectByPartitionStmt = SqliteDriver$ReventlessLocal.prepare(db, `SELECT partition_key, sub_key, item FROM ` + table + ` WHERE partition_key = ? AND ` + notExpiredClause + ` ORDER BY sub_key ASC`);
145
+ let deleteByPartitionStmt = SqliteDriver$ReventlessLocal.prepare(db, `DELETE FROM ` + table + ` WHERE partition_key = ?`);
146
+ let deleteBySubKeyStmt = SqliteDriver$ReventlessLocal.prepare(db, `DELETE FROM ` + table + ` WHERE partition_key = ? AND sub_key = ?`);
147
+ let scanAllStmt = SqliteDriver$ReventlessLocal.prepare(db, `SELECT partition_key, sub_key, item FROM ` + table + ` WHERE ` + notExpiredClause + ` ORDER BY partition_key, sub_key`);
148
+ let rowsFor = id => SqliteDriver$ReventlessLocal.all(selectByPartitionStmt, [id]).map(decodeItem);
149
+ let allRows = () => SqliteDriver$ReventlessLocal.all(scanAllStmt, []).map(row => {
150
+ let item = decodeItem(row);
151
+ let match = row["partition_key"];
152
+ let partition = typeof match === "string" ? match : "";
153
+ return withId(item, partition);
154
+ });
155
+ let saveOne = (id, state, ttl) => {
156
+ let subKey = computeSubKey(state, subIdField);
157
+ let expiresAt = ttl !== undefined ? ttl : null;
158
+ SqliteDriver$ReventlessLocal.run(upsertStmt, [
159
+ id,
160
+ subKey,
161
+ JSON.stringify(state),
162
+ expiresAt
163
+ ]);
164
+ };
165
+ let load = async id => ({
166
+ TAG: "Ok",
167
+ _0: rowsFor(id)
168
+ });
169
+ let loadStream = id => Stream.fromIterable(rowsFor(id));
170
+ let entityKeyFor = (id, subKey) => {
171
+ if (subIdField !== undefined) {
172
+ return id + "-" + subKey;
173
+ } else {
174
+ return id;
175
+ }
176
+ };
177
+ let publishUpdated = (id, state) => {
178
+ let subKey = computeSubKey(state, subIdField);
179
+ let descriptor = LocalBus$ReventlessLocal.makeStateChangeDescriptor("Updated", entityKeyFor(id, subKey), state);
180
+ bus.publishStateChange(name, descriptor);
181
+ };
182
+ let publishRemoved = (id, subKey) => {
183
+ let descriptor = LocalBus$ReventlessLocal.makeStateChangeDescriptor("Removed", entityKeyFor(id, subKey), undefined);
184
+ bus.publishStateChange(name, descriptor);
185
+ };
186
+ let rowKeysForPartition = id => SqliteDriver$ReventlessLocal.all(selectByPartitionStmt, [id]).map(row => {
187
+ let match = row["sub_key"];
188
+ if (typeof match === "string") {
189
+ return match;
190
+ } else {
191
+ return "";
192
+ }
193
+ });
194
+ let save = async (id, state, _saveMode, ttl) => {
195
+ saveOne(id, state, ttl);
196
+ publishUpdated(id, state);
197
+ return {
198
+ TAG: "Ok",
199
+ _0: undefined
200
+ };
201
+ };
202
+ let saveBatch = async batch => {
203
+ SqliteDriver$ReventlessLocal.transaction(db, () => {
204
+ batch.forEach(param => saveOne(param[0], param[1], param[2]));
205
+ });
206
+ batch.forEach(param => publishUpdated(param[0], param[1]));
207
+ return {
208
+ TAG: "Ok",
209
+ _0: undefined
210
+ };
211
+ };
212
+ let count = async (_id, _fieldName, inc) => ({
213
+ TAG: "Ok",
214
+ _0: inc
215
+ });
216
+ let deleteOne = (id, subIdOpt) => {
217
+ if (subIdOpt !== undefined) {
218
+ return SqliteDriver$ReventlessLocal.run(deleteBySubKeyStmt, [
219
+ id,
220
+ subIdOpt[1]
221
+ ]);
222
+ } else {
223
+ return SqliteDriver$ReventlessLocal.run(deleteByPartitionStmt, [id]);
224
+ }
225
+ };
226
+ let $$delete = async (id, subIdOpt) => {
227
+ let removedSubKeys = subIdOpt !== undefined ? [subIdOpt[1]] : rowKeysForPartition(id);
228
+ deleteOne(id, subIdOpt);
229
+ removedSubKeys.forEach(sk => publishRemoved(id, sk));
230
+ return {
231
+ TAG: "Ok",
232
+ _0: undefined
233
+ };
234
+ };
235
+ let deleteBatch = async ids => {
236
+ let removed = [];
237
+ ids.forEach(param => {
238
+ let subIdOpt = param[1];
239
+ let id = param[0];
240
+ if (subIdOpt !== undefined) {
241
+ removed.push([
242
+ id,
243
+ subIdOpt[1]
244
+ ]);
245
+ } else {
246
+ rowKeysForPartition(id).forEach(sk => {
247
+ removed.push([
248
+ id,
249
+ sk
250
+ ]);
251
+ });
252
+ }
253
+ });
254
+ SqliteDriver$ReventlessLocal.transaction(db, () => {
255
+ ids.forEach(param => deleteOne(param[0], param[1]));
256
+ });
257
+ removed.forEach(param => publishRemoved(param[0], param[1]));
258
+ return {
259
+ TAG: "Ok",
260
+ _0: undefined
261
+ };
262
+ };
263
+ let ops = {
264
+ load: load,
265
+ loadStream: loadStream,
266
+ save: save,
267
+ saveBatch: saveBatch,
268
+ count: count,
269
+ delete: $$delete,
270
+ deleteBatch: deleteBatch
271
+ };
272
+ bus.registerQueryDb(name, ops);
273
+ bus.registerQueryDbScan(name, () => allRows());
274
+ bus.registerQueryDbStream(name, () => Stream.fromIterable(allRows()));
275
+ return {
276
+ resources: [],
277
+ dataSourceName: Pulumi.output(""),
278
+ operations: Pulumi.output(ops)
279
+ };
280
+ }
281
+
282
+ function Make(Bus) {
283
+ return DbProvider => {
284
+ let busCallbacks_publishStateChange = Bus.publishStateChange;
285
+ let busCallbacks_registerQueryDb = Bus.registerQueryDb;
286
+ let busCallbacks_registerQueryDbScan = Bus.registerQueryDbScan;
287
+ let busCallbacks_registerQueryDbStream = Bus.registerQueryDbStream;
288
+ let busCallbacks = {
289
+ publishStateChange: busCallbacks_publishStateChange,
290
+ registerQueryDb: busCallbacks_registerQueryDb,
291
+ registerQueryDbScan: busCallbacks_registerQueryDbScan,
292
+ registerQueryDbStream: busCallbacks_registerQueryDbStream
293
+ };
294
+ let make = (name, indexes, subIdField, param, param$1, param$2, param$3) => makeStorage(DbProvider.db, busCallbacks, name, indexes, subIdField);
295
+ return {
296
+ busCallbacks: busCallbacks,
297
+ make: make
298
+ };
299
+ };
300
+ }
301
+
302
+ export {
303
+ tableName,
304
+ sanitizeIdent,
305
+ ensureSchema,
306
+ ensureExpiresColumn,
307
+ jsonField,
308
+ compositeExpr,
309
+ indexExpressions,
310
+ ensureIndexes,
311
+ decodeItem,
312
+ computeSubKey,
313
+ withId,
314
+ notExpiredClause,
315
+ makeStorage,
316
+ Make,
317
+ }
318
+ /* effect/Stream Not a pure module */
@@ -0,0 +1,48 @@
1
+ // Pure sort key filtering for sub-ID queries.
2
+ // Applies DynamoDB-style sort key conditions to an already-sorted item list.
3
+ // Used by QueryDbResolvers_GraphQL for {name}ById queries.
4
+
5
+ type result = {
6
+ items: array<JSON.t>,
7
+ nextToken: option<string>,
8
+ }
9
+
10
+ /** Extract the sort key string from a JSON item using the given field name. */
11
+ let getSk = (item, skField) =>
12
+ item->JSON.Decode.object->Option.flatMap(d => d->Dict.get(skField))->Option.flatMap(JSON.Decode.string)->Option.getOr("")
13
+
14
+ /** Apply sort key conditions, ordering, and pagination to a list of JSON items.
15
+ - `items` must already be sorted ascending by sort key.
16
+ - Returns filtered+paginated items and an optional `nextToken` for the next page. */
17
+ let apply = (
18
+ ~items: array<JSON.t>,
19
+ ~skField: string,
20
+ ~prefix: option<string>=?,
21
+ ~from: option<string>=?,
22
+ ~to_: option<string>=?,
23
+ ~eq: option<string>=?,
24
+ ~reverse: bool=false,
25
+ ~limit: option<int>=?,
26
+ ~offset: int=0,
27
+ ) => {
28
+ let filtered = items->Array.filter(item => {
29
+ let sk = getSk(item, skField)
30
+ let okPrefix = prefix->Option.map(p => sk->String.startsWith(p))->Option.getOr(true)
31
+ let okFrom = from->Option.map(f => sk >= f)->Option.getOr(true)
32
+ let okTo = to_->Option.map(t => sk <= t)->Option.getOr(true)
33
+ let okEq = eq->Option.map(e => sk == e)->Option.getOr(true)
34
+ okPrefix && okFrom && okTo && okEq
35
+ })
36
+ let ordered = if reverse { filtered->Array.toReversed } else { filtered }
37
+ let sliced = ordered->Array.slice(~start=offset, ~end=ordered->Array.length)
38
+ let (page, hasMore) = switch limit {
39
+ | Some(n) => (sliced->Array.slice(~start=0, ~end=n), sliced->Array.length > n)
40
+ | None => (sliced, false)
41
+ }
42
+ let nextToken = if hasMore {
43
+ Some(Int.toString(offset + page->Array.length))
44
+ } else {
45
+ None
46
+ }
47
+ { items: page, nextToken }
48
+ }