@reventlessdev/reventless-core 3.0.0-alpha.134

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 (502) hide show
  1. package/CHANGELOG.md +1355 -0
  2. package/LICENSE +202 -0
  3. package/README.md +173 -0
  4. package/__mocks__/emptyModule.js +2 -0
  5. package/jest.setup.cjs +5 -0
  6. package/package.json +73 -0
  7. package/rescript.json +41 -0
  8. package/scripts/deploy.sh +6 -0
  9. package/scripts/post-deploy.sh +5 -0
  10. package/scripts/pre-deploy.sh +18 -0
  11. package/scripts/pulumi-up.sh +30 -0
  12. package/src/Behavior.res +18 -0
  13. package/src/Behavior.res.mjs +2 -0
  14. package/src/ComponentType.res +125 -0
  15. package/src/ComponentType.res.mjs +199 -0
  16. package/src/Env.res +5 -0
  17. package/src/Env.res.mjs +2 -0
  18. package/src/Mapper.res +36 -0
  19. package/src/Mapper.res.mjs +18 -0
  20. package/src/Mapper1toN.res +107 -0
  21. package/src/Mapper1toN.res.mjs +2 -0
  22. package/src/MapperNto1.res +78 -0
  23. package/src/MapperNto1.res.mjs +35 -0
  24. package/src/Message.res +353 -0
  25. package/src/Message.res.mjs +418 -0
  26. package/src/PluginRuntimeOperations.res +15 -0
  27. package/src/PluginRuntimeOperations.res.mjs +2 -0
  28. package/src/Projection.res +447 -0
  29. package/src/Projection.res.mjs +712 -0
  30. package/src/ProjectionMapper.res +44 -0
  31. package/src/ProjectionMapper.res.mjs +51 -0
  32. package/src/RequestContext.res +42 -0
  33. package/src/RequestContext.res.mjs +39 -0
  34. package/src/adapter/Adapter.res +178 -0
  35. package/src/adapter/Adapter.res.mjs +201 -0
  36. package/src/adapter/AdapterDeploytime.res +56 -0
  37. package/src/adapter/AdapterDeploytime.res.mjs +42 -0
  38. package/src/adapter/Auth/Auth_Adapter.res +42 -0
  39. package/src/adapter/Auth/Auth_Adapter.res.mjs +2 -0
  40. package/src/adapter/Runtime/AggregateRuntime_Builder.res +22 -0
  41. package/src/adapter/Runtime/AggregateRuntime_Builder.res.mjs +2 -0
  42. package/src/adapter/Runtime/AggregateRuntime_Builder_Common.res +259 -0
  43. package/src/adapter/Runtime/AggregateRuntime_Builder_Common.res.mjs +190 -0
  44. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +92 -0
  45. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res.mjs +59 -0
  46. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res +52 -0
  47. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res.mjs +49 -0
  48. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +64 -0
  49. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +59 -0
  50. package/src/adapter/Runtime/EventCollectorRuntime_Builder.res +11 -0
  51. package/src/adapter/Runtime/EventCollectorRuntime_Builder.res.mjs +2 -0
  52. package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res +46 -0
  53. package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res.mjs +40 -0
  54. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +204 -0
  55. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +150 -0
  56. package/src/adapter/Runtime/ExtensionPointRuntime_Builder.res +18 -0
  57. package/src/adapter/Runtime/ExtensionPointRuntime_Builder.res.mjs +2 -0
  58. package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res +36 -0
  59. package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res.mjs +28 -0
  60. package/src/adapter/Runtime/PluginRuntime_Builder.res +23 -0
  61. package/src/adapter/Runtime/PluginRuntime_Builder.res.mjs +2 -0
  62. package/src/adapter/Runtime/PluginRuntime_Builder_Micro.res +97 -0
  63. package/src/adapter/Runtime/PluginRuntime_Builder_Micro.res.mjs +62 -0
  64. package/src/adapter/Runtime/PluginRuntime_Builder_Single.res +89 -0
  65. package/src/adapter/Runtime/PluginRuntime_Builder_Single.res.mjs +62 -0
  66. package/src/adapter/Runtime/Runtime.res +106 -0
  67. package/src/adapter/Runtime/Runtime.res.mjs +22 -0
  68. package/src/adapter/Runtime/TaskRuntime_Builder.res +32 -0
  69. package/src/adapter/Runtime/TaskRuntime_Builder.res.mjs +2 -0
  70. package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res +42 -0
  71. package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res.mjs +29 -0
  72. package/src/admin/AdminApi.res +88 -0
  73. package/src/admin/AdminApi.res.mjs +89 -0
  74. package/src/admin/Admin_Callback.res +27 -0
  75. package/src/admin/Admin_Callback.res.mjs +20 -0
  76. package/src/admin/Platform_Admin.res +402 -0
  77. package/src/admin/Platform_Admin.res.mjs +250 -0
  78. package/src/admin/Platform_Admin_Structure.res +125 -0
  79. package/src/admin/Platform_Admin_Structure.res.mjs +182 -0
  80. package/src/admin/Platform_ComponentDefinitionsApi.res +160 -0
  81. package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +299 -0
  82. package/src/admin/Platform_UIFragmentsApi.res +55 -0
  83. package/src/admin/Platform_UIFragmentsApi.res.mjs +124 -0
  84. package/src/admin/UIFragmentRegistryProjection.res +43 -0
  85. package/src/admin/UIFragmentRegistryProjection.res.mjs +81 -0
  86. package/src/admin/UIFragmentRegistryReadModelSpec.res +13 -0
  87. package/src/admin/UIFragmentRegistryReadModelSpec.res.mjs +40 -0
  88. package/src/components/Aggregate/Aggregate.res +96 -0
  89. package/src/components/Aggregate/Aggregate.res.mjs +82 -0
  90. package/src/components/Aggregate/Aggregate_Builder.res +175 -0
  91. package/src/components/Aggregate/Aggregate_Builder.res.mjs +119 -0
  92. package/src/components/Aggregate/Aggregate_Callback.res +273 -0
  93. package/src/components/Aggregate/Aggregate_Callback.res.mjs +208 -0
  94. package/src/components/Api/ApiAllowedStatesHelpers.res +19 -0
  95. package/src/components/Api/ApiAllowedStatesHelpers.res.mjs +16 -0
  96. package/src/components/Api/ApiNoApiHelpers.res +13 -0
  97. package/src/components/Api/ApiNoApiHelpers.res.mjs +29 -0
  98. package/src/components/Api/Api_Builder.res +40 -0
  99. package/src/components/Api/Api_Builder.res.mjs +33 -0
  100. package/src/components/Api/Api_Naming.res +92 -0
  101. package/src/components/Api/Api_Naming.res.mjs +103 -0
  102. package/src/components/Api/Api_Operations.res +13 -0
  103. package/src/components/Api/Api_Operations.res.mjs +12 -0
  104. package/src/components/Api/GraphQL_FragmentGenerator.res +612 -0
  105. package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +503 -0
  106. package/src/components/Api/GraphQL_SchemaInspector.res +156 -0
  107. package/src/components/Api/GraphQL_SchemaInspector.res.mjs +123 -0
  108. package/src/components/Api/GraphQL_Stitcher.res +271 -0
  109. package/src/components/Api/GraphQL_Stitcher.res.mjs +229 -0
  110. package/src/components/Api/MCP_SchemaGenerator.res +185 -0
  111. package/src/components/Api/MCP_SchemaGenerator.res.mjs +146 -0
  112. package/src/components/Api/SchemaType.res +126 -0
  113. package/src/components/Api/SchemaType.res.mjs +143 -0
  114. package/src/components/Api/SuryToJsonSchema.res +142 -0
  115. package/src/components/Api/SuryToJsonSchema.res.mjs +210 -0
  116. package/src/components/AutomationSlice/AutomationSlice.res +39 -0
  117. package/src/components/AutomationSlice/AutomationSlice.res.mjs +24 -0
  118. package/src/components/AutomationSlice/AutomationSlice_Builder.res +207 -0
  119. package/src/components/AutomationSlice/AutomationSlice_Builder.res.mjs +123 -0
  120. package/src/components/AutomationSlice/AutomationSlice_Callback.res +212 -0
  121. package/src/components/AutomationSlice/AutomationSlice_Callback.res.mjs +189 -0
  122. package/src/components/Builder_Helpers.res +196 -0
  123. package/src/components/Builder_Helpers.res.mjs +142 -0
  124. package/src/components/Cloner.res +84 -0
  125. package/src/components/Cloner.res.mjs +88 -0
  126. package/src/components/CommandGenerator/CommandGenerator.res +54 -0
  127. package/src/components/CommandGenerator/CommandGenerator.res.mjs +15 -0
  128. package/src/components/CommandGenerator/CommandGenerator_Adapter.res +22 -0
  129. package/src/components/CommandGenerator/CommandGenerator_Adapter.res.mjs +2 -0
  130. package/src/components/CommandGenerator/CommandGenerator_Builder.res +70 -0
  131. package/src/components/CommandGenerator/CommandGenerator_Builder.res.mjs +68 -0
  132. package/src/components/CommandGenerator/CommandGenerator_Callback.res +191 -0
  133. package/src/components/CommandGenerator/CommandGenerator_Callback.res.mjs +161 -0
  134. package/src/components/CommandTopic/CommandTopic.res +79 -0
  135. package/src/components/CommandTopic/CommandTopic.res.mjs +68 -0
  136. package/src/components/CommandTopic/CommandTopic_Adapter.res +53 -0
  137. package/src/components/CommandTopic/CommandTopic_Adapter.res.mjs +2 -0
  138. package/src/components/CommandTopic/CommandTopic_Builder.res +146 -0
  139. package/src/components/CommandTopic/CommandTopic_Builder.res.mjs +127 -0
  140. package/src/components/CommandTopic/CommandTopic_Callback.res +38 -0
  141. package/src/components/CommandTopic/CommandTopic_Callback.res.mjs +49 -0
  142. package/src/components/CommandTopic/CommandTopic_Helpers.res +176 -0
  143. package/src/components/CommandTopic/CommandTopic_Helpers.res.mjs +195 -0
  144. package/src/components/CommandTopic/CommandTopic_Operations.res +40 -0
  145. package/src/components/CommandTopic/CommandTopic_Operations.res.mjs +49 -0
  146. package/src/components/Component.res +1 -0
  147. package/src/components/Component.res.mjs +34 -0
  148. package/src/components/Component.resi +1 -0
  149. package/src/components/Counter/Counter.res +47 -0
  150. package/src/components/Counter/Counter.res.mjs +55 -0
  151. package/src/components/Counter/Counter_Adapter.res +18 -0
  152. package/src/components/Counter/Counter_Adapter.res.mjs +2 -0
  153. package/src/components/Counter/Counter_Builder.res +142 -0
  154. package/src/components/Counter/Counter_Builder.res.mjs +112 -0
  155. package/src/components/Counter/Counter_Callback.res +94 -0
  156. package/src/components/Counter/Counter_Callback.res.mjs +70 -0
  157. package/src/components/Counter/Counter_Operations.res +70 -0
  158. package/src/components/Counter/Counter_Operations.res.mjs +91 -0
  159. package/src/components/Dcb/Dcb_Builder.res +971 -0
  160. package/src/components/Dcb/Dcb_Builder.res.mjs +572 -0
  161. package/src/components/DcbEventLog/DcbEventLog.res +43 -0
  162. package/src/components/DcbEventLog/DcbEventLog.res.mjs +24 -0
  163. package/src/components/DcbEventLog/DcbEventLog_Adapter.res +57 -0
  164. package/src/components/DcbEventLog/DcbEventLog_Adapter.res.mjs +2 -0
  165. package/src/components/DcbEventLog/DcbEventLog_Builder.res +72 -0
  166. package/src/components/DcbEventLog/DcbEventLog_Builder.res.mjs +67 -0
  167. package/src/components/DcbEventLog/DcbEventLog_Operations.res +160 -0
  168. package/src/components/DcbEventLog/DcbEventLog_Operations.res.mjs +121 -0
  169. package/src/components/EventCollector/EventCollector.res +34 -0
  170. package/src/components/EventCollector/EventCollector.res.mjs +9 -0
  171. package/src/components/EventCollector/EventCollector_Adapter.res +45 -0
  172. package/src/components/EventCollector/EventCollector_Adapter.res.mjs +2 -0
  173. package/src/components/EventCollector/EventCollector_Builder.res +91 -0
  174. package/src/components/EventCollector/EventCollector_Builder.res.mjs +64 -0
  175. package/src/components/EventLog/EventLog.res +27 -0
  176. package/src/components/EventLog/EventLog.res.mjs +13 -0
  177. package/src/components/EventLog/EventLog_Adapter.res +15 -0
  178. package/src/components/EventLog/EventLog_Adapter.res.mjs +2 -0
  179. package/src/components/EventLog/EventLog_Builder.res +65 -0
  180. package/src/components/EventLog/EventLog_Builder.res.mjs +57 -0
  181. package/src/components/EventLog/EventLog_Operations.res +209 -0
  182. package/src/components/EventLog/EventLog_Operations.res.mjs +202 -0
  183. package/src/components/EventLog/EventPublish_Callback.res +29 -0
  184. package/src/components/EventLog/EventPublish_Callback.res.mjs +32 -0
  185. package/src/components/EventMapper/EventMapper.res +72 -0
  186. package/src/components/EventMapper/EventMapper.res.mjs +54 -0
  187. package/src/components/EventMapper/EventMapper_Builder.res +128 -0
  188. package/src/components/EventMapper/EventMapper_Builder.res.mjs +81 -0
  189. package/src/components/EventMapper/EventMapper_Callback.res +323 -0
  190. package/src/components/EventMapper/EventMapper_Callback.res.mjs +231 -0
  191. package/src/components/EventTopic/EventTopic.res +69 -0
  192. package/src/components/EventTopic/EventTopic.res.mjs +50 -0
  193. package/src/components/EventTopic/EventTopic_Adapter.res +15 -0
  194. package/src/components/EventTopic/EventTopic_Adapter.res.mjs +2 -0
  195. package/src/components/EventTopic/EventTopic_Builder.res +53 -0
  196. package/src/components/EventTopic/EventTopic_Builder.res.mjs +46 -0
  197. package/src/components/EventTopic/EventTopic_Operations.res +37 -0
  198. package/src/components/EventTopic/EventTopic_Operations.res.mjs +39 -0
  199. package/src/components/Extension/Extension.res +32 -0
  200. package/src/components/Extension/Extension.res.mjs +9 -0
  201. package/src/components/Extension/Extension_Builder.res +82 -0
  202. package/src/components/Extension/Extension_Builder.res.mjs +57 -0
  203. package/src/components/Extension/Extension_Operations.res +203 -0
  204. package/src/components/Extension/Extension_Operations.res.mjs +154 -0
  205. package/src/components/ExtensionPoint/ExtensionPoint.res +49 -0
  206. package/src/components/ExtensionPoint/ExtensionPoint.res.mjs +25 -0
  207. package/src/components/ExtensionPoint/ExtensionPoint_Builder.res +163 -0
  208. package/src/components/ExtensionPoint/ExtensionPoint_Builder.res.mjs +113 -0
  209. package/src/components/ExtensionPoint/ExtensionPoint_Callback.res +110 -0
  210. package/src/components/ExtensionPoint/ExtensionPoint_Callback.res.mjs +76 -0
  211. package/src/components/ExtensionPoint/ExtensionPoint_Operations.res +146 -0
  212. package/src/components/ExtensionPoint/ExtensionPoint_Operations.res.mjs +122 -0
  213. package/src/components/Heartbeat/Heartbeat.res +25 -0
  214. package/src/components/Heartbeat/Heartbeat.res.mjs +9 -0
  215. package/src/components/Heartbeat/Heartbeat_Adapter.res +14 -0
  216. package/src/components/Heartbeat/Heartbeat_Adapter.res.mjs +2 -0
  217. package/src/components/Heartbeat/Heartbeat_Builder.res +43 -0
  218. package/src/components/Heartbeat/Heartbeat_Builder.res.mjs +47 -0
  219. package/src/components/Heartbeat/Heartbeat_Callback.res +32 -0
  220. package/src/components/Heartbeat/Heartbeat_Callback.res.mjs +40 -0
  221. package/src/components/InboundTranslationSlice/InboundTranslationSlice.res +34 -0
  222. package/src/components/InboundTranslationSlice/InboundTranslationSlice.res.mjs +24 -0
  223. package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res +98 -0
  224. package/src/components/InboundTranslationSlice/InboundTranslationSlice_Builder.res.mjs +87 -0
  225. package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res +191 -0
  226. package/src/components/InboundTranslationSlice/InboundTranslationSlice_Callback.res.mjs +175 -0
  227. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice.res +35 -0
  228. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice.res.mjs +24 -0
  229. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res +181 -0
  230. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Builder.res.mjs +137 -0
  231. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Callback.res +173 -0
  232. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Callback.res.mjs +173 -0
  233. package/src/components/QueryDb/QueryDb.res +74 -0
  234. package/src/components/QueryDb/QueryDb.res.mjs +37 -0
  235. package/src/components/QueryDb/QueryDb_Adapter.res +78 -0
  236. package/src/components/QueryDb/QueryDb_Adapter.res.mjs +17 -0
  237. package/src/components/QueryDb/QueryDb_Builder.res +76 -0
  238. package/src/components/QueryDb/QueryDb_Builder.res.mjs +53 -0
  239. package/src/components/QueryDb/QueryDb_Callback.res +18 -0
  240. package/src/components/QueryDb/QueryDb_Callback.res.mjs +21 -0
  241. package/src/components/QueryDb/QueryDb_Operations.res +116 -0
  242. package/src/components/QueryDb/QueryDb_Operations.res.mjs +165 -0
  243. package/src/components/ReadModel/ReadModel.res +42 -0
  244. package/src/components/ReadModel/ReadModel.res.mjs +27 -0
  245. package/src/components/ReadModel/ReadModel_Builder.res +155 -0
  246. package/src/components/ReadModel/ReadModel_Builder.res.mjs +101 -0
  247. package/src/components/ReadModel/ReadModel_Callback.res +48 -0
  248. package/src/components/ReadModel/ReadModel_Callback.res.mjs +48 -0
  249. package/src/components/Scheduler/Scheduler.res +13 -0
  250. package/src/components/Scheduler/Scheduler.res.mjs +9 -0
  251. package/src/components/Scheduler/Scheduler_Adapter.res +13 -0
  252. package/src/components/Scheduler/Scheduler_Adapter.res.mjs +2 -0
  253. package/src/components/Scheduler/Scheduler_Builder.res +19 -0
  254. package/src/components/Scheduler/Scheduler_Builder.res.mjs +29 -0
  255. package/src/components/SideEffectHandler/SideEffectHandler.res +30 -0
  256. package/src/components/SideEffectHandler/SideEffectHandler.res.mjs +12 -0
  257. package/src/components/SideEffectHandler/SideEffectHandler_Builder.res +117 -0
  258. package/src/components/SideEffectHandler/SideEffectHandler_Builder.res.mjs +64 -0
  259. package/src/components/SideEffectHandler/SideEffectHandler_Callback.res +116 -0
  260. package/src/components/SideEffectHandler/SideEffectHandler_Callback.res.mjs +95 -0
  261. package/src/components/StateChangeSlice/StateChangeSlice.res +38 -0
  262. package/src/components/StateChangeSlice/StateChangeSlice.res.mjs +17 -0
  263. package/src/components/StateChangeSlice/StateChangeSlice_Builder.res +91 -0
  264. package/src/components/StateChangeSlice/StateChangeSlice_Builder.res.mjs +81 -0
  265. package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +412 -0
  266. package/src/components/StateChangeSlice/StateChangeSlice_Callback.res.mjs +299 -0
  267. package/src/components/StateTopic.res +91 -0
  268. package/src/components/StateTopic.res.mjs +41 -0
  269. package/src/components/StateViewSlice/StateViewSlice.res +33 -0
  270. package/src/components/StateViewSlice/StateViewSlice.res.mjs +24 -0
  271. package/src/components/StateViewSlice/StateViewSlice_Builder.res +176 -0
  272. package/src/components/StateViewSlice/StateViewSlice_Builder.res.mjs +134 -0
  273. package/src/components/StateViewSlice/StateViewSlice_Callback.res +64 -0
  274. package/src/components/StateViewSlice/StateViewSlice_Callback.res.mjs +56 -0
  275. package/src/components/Task/Task.res +81 -0
  276. package/src/components/Task/Task.res.mjs +51 -0
  277. package/src/components/Task/Task_Adapter.res +31 -0
  278. package/src/components/Task/Task_Adapter.res.mjs +2 -0
  279. package/src/components/Task/Task_Builder.res +223 -0
  280. package/src/components/Task/Task_Builder.res.mjs +115 -0
  281. package/src/plugin/api/PluginBaseFragment.res +104 -0
  282. package/src/plugin/api/PluginBaseFragment.res.mjs +64 -0
  283. package/src/plugin/component/Plugin.res +100 -0
  284. package/src/plugin/component/Plugin.res.mjs +61 -0
  285. package/src/plugin/component/Plugin_Builder.res +1017 -0
  286. package/src/plugin/component/Plugin_Builder.res.mjs +608 -0
  287. package/src/plugin/component/Plugin_BuiltHook.res +141 -0
  288. package/src/plugin/component/Plugin_BuiltHook.res.mjs +39 -0
  289. package/src/plugin/component/Plugin_Callback.res +98 -0
  290. package/src/plugin/component/Plugin_Callback.res.mjs +55 -0
  291. package/src/plugin/component/Plugin_Helpers.res +1730 -0
  292. package/src/plugin/component/Plugin_Helpers.res.mjs +1010 -0
  293. package/src/plugin/component/Plugin_ResolverError.res +27 -0
  294. package/src/plugin/component/Plugin_ResolverError.res.mjs +21 -0
  295. package/src/plugin/component/Plugin_Structure.res +570 -0
  296. package/src/plugin/component/Plugin_Structure.res.mjs +458 -0
  297. package/src/plugin/component/Plugin_SubscriptionSchema.res +103 -0
  298. package/src/plugin/component/Plugin_SubscriptionSchema.res.mjs +71 -0
  299. package/src/plugin/component/SchemaWalker.res +164 -0
  300. package/src/plugin/component/SchemaWalker.res.mjs +183 -0
  301. package/src/plugin/connect/PluginConnectExtension_Builder.res +14 -0
  302. package/src/plugin/connect/PluginConnectExtension_Builder.res.mjs +36 -0
  303. package/src/plugin/connect/PluginConnectExtension_Mapping.res +76 -0
  304. package/src/plugin/connect/PluginConnectExtension_Mapping.res.mjs +89 -0
  305. package/src/plugin/connect/PluginExtensionPoint_Builder.res +39 -0
  306. package/src/plugin/connect/PluginExtensionPoint_Builder.res.mjs +33 -0
  307. package/src/plugin/connect/PluginExtensionPoint_Plugin.res +231 -0
  308. package/src/plugin/connect/PluginExtensionPoint_Plugin.res.mjs +353 -0
  309. package/src/plugin/lifecycle/PluginBehavior.res +250 -0
  310. package/src/plugin/lifecycle/PluginBehavior.res.mjs +391 -0
  311. package/src/plugin/lifecycle/PluginSpec.res +84 -0
  312. package/src/plugin/lifecycle/PluginSpec.res.mjs +148 -0
  313. package/src/plugin/lifecycle/PluginsProjection.res +154 -0
  314. package/src/plugin/lifecycle/PluginsProjection.res.mjs +230 -0
  315. package/src/plugin/lifecycle/PluginsReadModelSpec.res +66 -0
  316. package/src/plugin/lifecycle/PluginsReadModelSpec.res.mjs +58 -0
  317. package/src/util/AWS.res +7 -0
  318. package/src/util/AWS.res.mjs +20 -0
  319. package/src/util/CSV.res +27 -0
  320. package/src/util/CSV.res.mjs +56 -0
  321. package/src/util/CommandPublisher.res +133 -0
  322. package/src/util/CommandPublisher.res.mjs +161 -0
  323. package/src/util/CsvStream.res +39 -0
  324. package/src/util/CsvStream.res.mjs +42 -0
  325. package/src/util/EffectLogger.res +178 -0
  326. package/src/util/EffectLogger.res.mjs +171 -0
  327. package/src/util/FTP.res +69 -0
  328. package/src/util/FTP.res.mjs +101 -0
  329. package/src/util/FTPHandler.res +142 -0
  330. package/src/util/FTPHandler.res.mjs +136 -0
  331. package/src/util/Hash.res +1 -0
  332. package/src/util/Hash.res.mjs +10 -0
  333. package/src/util/Interstack.res +74 -0
  334. package/src/util/Interstack.res.mjs +126 -0
  335. package/src/util/Interstack.resi +25 -0
  336. package/src/util/LogFormat.res +222 -0
  337. package/src/util/LogFormat.res.mjs +257 -0
  338. package/src/util/Logger.res +196 -0
  339. package/src/util/Logger.res.mjs +238 -0
  340. package/src/util/Lru.res +51 -0
  341. package/src/util/Lru.res.mjs +59 -0
  342. package/src/util/Lru.resi +32 -0
  343. package/src/util/Metrics.res +32 -0
  344. package/src/util/Metrics.res.mjs +42 -0
  345. package/src/util/OutputFailsafeDeploytime.res +16 -0
  346. package/src/util/OutputFailsafeDeploytime.res.mjs +20 -0
  347. package/src/util/OutputFailsafeRuntime.res +6 -0
  348. package/src/util/OutputFailsafeRuntime.res.mjs +15 -0
  349. package/src/util/OutputLogger.res +9 -0
  350. package/src/util/OutputLogger.res.mjs +19 -0
  351. package/src/util/ResourceQuery.res +14 -0
  352. package/src/util/ResourceQuery.res.mjs +17 -0
  353. package/src/util/ResourceQueryRuntime.res +17 -0
  354. package/src/util/ResourceQueryRuntime.res.mjs +18 -0
  355. package/src/util/ScheduleOps.res +124 -0
  356. package/src/util/ScheduleOps.res.mjs +132 -0
  357. package/src/util/Util.res +12 -0
  358. package/src/util/Util.res.mjs +42 -0
  359. package/src/util/Util_Adapter.res +97 -0
  360. package/src/util/Util_Adapter.res.mjs +82 -0
  361. package/src/util/Util_AdapterRuntime.res +39 -0
  362. package/src/util/Util_AdapterRuntime.res.mjs +42 -0
  363. package/src/util/Util_Array.res +2 -0
  364. package/src/util/Util_Array.res.mjs +12 -0
  365. package/src/util/Util_Error.res +16 -0
  366. package/src/util/Util_Error.res.mjs +19 -0
  367. package/src/util/Util_Promise.res +67 -0
  368. package/src/util/Util_Promise.res.mjs +105 -0
  369. package/src/util/Util_Pulumi.res +35 -0
  370. package/src/util/Util_Pulumi.res.mjs +55 -0
  371. package/src/util/Util_Pulumi.resi +21 -0
  372. package/src/util/Util_QueryDb.res +14 -0
  373. package/src/util/Util_QueryDb.res.mjs +21 -0
  374. package/src/util/Util_QueryDbRuntime.res +11 -0
  375. package/src/util/Util_QueryDbRuntime.res.mjs +19 -0
  376. package/src/util/Util_ReadModel.res +2 -0
  377. package/src/util/Util_ReadModel.res.mjs +10 -0
  378. package/src/util/Util_StackRefs.res +24 -0
  379. package/src/util/Util_StackRefs.res.mjs +39 -0
  380. package/src/util/Validation.res +75 -0
  381. package/src/util/Validation.res.mjs +181 -0
  382. package/tests/.gitkeep +0 -0
  383. package/tests/IdentityTest.res +155 -0
  384. package/tests/IdentityTest.res.mjs +174 -0
  385. package/tests/admin/Platform_ComponentDefinitionsApiTest.res +237 -0
  386. package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +298 -0
  387. package/tests/admin/Platform_UIFragmentsApiTest.res +96 -0
  388. package/tests/admin/Platform_UIFragmentsApiTest.res.mjs +101 -0
  389. package/tests/aggregate/AggregateCallbackTest.res +81 -0
  390. package/tests/aggregate/AggregateCallbackTest.res.mjs +140 -0
  391. package/tests/aggregate/AggregateCausationTest.res +49 -0
  392. package/tests/aggregate/AggregateCausationTest.res.mjs +44 -0
  393. package/tests/aggregate/AggregateConflictTest.res +212 -0
  394. package/tests/aggregate/AggregateConflictTest.res.mjs +363 -0
  395. package/tests/aggregate/AggregateFixtures.res +177 -0
  396. package/tests/aggregate/AggregateFixtures.res.mjs +285 -0
  397. package/tests/aggregate/AggregateRejectionTest.res +139 -0
  398. package/tests/aggregate/AggregateRejectionTest.res.mjs +163 -0
  399. package/tests/api/ApiNoApiTest.res +62 -0
  400. package/tests/api/ApiNoApiTest.res.mjs +90 -0
  401. package/tests/api/GraphQL_StitcherTest.res +107 -0
  402. package/tests/api/GraphQL_StitcherTest.res.mjs +99 -0
  403. package/tests/api/SimpleTest.res +7 -0
  404. package/tests/api/SimpleTest.res.mjs +10 -0
  405. package/tests/api/SuryToJsonSchemaTest.res +405 -0
  406. package/tests/api/SuryToJsonSchemaTest.res.mjs +623 -0
  407. package/tests/commandgenerator/CommandGeneratorCallbackTest.res +128 -0
  408. package/tests/commandgenerator/CommandGeneratorCallbackTest.res.mjs +160 -0
  409. package/tests/commandgenerator/CommandGeneratorFixtures.res +139 -0
  410. package/tests/commandgenerator/CommandGeneratorFixtures.res.mjs +197 -0
  411. package/tests/commandtopic/CommandTopicCallbackFixtures.res +87 -0
  412. package/tests/commandtopic/CommandTopicCallbackFixtures.res.mjs +152 -0
  413. package/tests/commandtopic/CommandTopicCallbackTest.res +66 -0
  414. package/tests/commandtopic/CommandTopicCallbackTest.res.mjs +106 -0
  415. package/tests/commandtopic/CommandTopicHelpersRejectionTest.res +83 -0
  416. package/tests/commandtopic/CommandTopicHelpersRejectionTest.res.mjs +109 -0
  417. package/tests/counter/CounterCallbackTest.res +83 -0
  418. package/tests/counter/CounterCallbackTest.res.mjs +140 -0
  419. package/tests/counter/CounterFixtures.res +57 -0
  420. package/tests/counter/CounterFixtures.res.mjs +67 -0
  421. package/tests/dcb/DcbDecodeTest.res +122 -0
  422. package/tests/dcb/DcbDecodeTest.res.mjs +194 -0
  423. package/tests/dcb/DcbEventLogOperationsTest.res +195 -0
  424. package/tests/dcb/DcbEventLogOperationsTest.res.mjs +268 -0
  425. package/tests/dcb/DcbFixtures.res +422 -0
  426. package/tests/dcb/DcbFixtures.res.mjs +534 -0
  427. package/tests/dcb/DcbScopeInferenceTest.res +242 -0
  428. package/tests/dcb/DcbScopeInferenceTest.res.mjs +480 -0
  429. package/tests/dcb/DcbStateChangeSliceTest.res +362 -0
  430. package/tests/dcb/DcbStateChangeSliceTest.res.mjs +546 -0
  431. package/tests/dcb/DcbTagTest.res +978 -0
  432. package/tests/dcb/DcbTagTest.res.mjs +867 -0
  433. package/tests/dcb/DcbValidationTest.res +349 -0
  434. package/tests/dcb/DcbValidationTest.res.mjs +476 -0
  435. package/tests/eventlog/EventLogFixtures.res +113 -0
  436. package/tests/eventlog/EventLogFixtures.res.mjs +166 -0
  437. package/tests/eventlog/EventLogOperationsTest.res +96 -0
  438. package/tests/eventlog/EventLogOperationsTest.res.mjs +192 -0
  439. package/tests/eventlog/EventLogRetryTest.res +121 -0
  440. package/tests/eventlog/EventLogRetryTest.res.mjs +171 -0
  441. package/tests/eventmapper/EventMapperCallbackTest.res +134 -0
  442. package/tests/eventmapper/EventMapperCallbackTest.res.mjs +141 -0
  443. package/tests/eventmapper/EventMapperFixtures.res +255 -0
  444. package/tests/eventmapper/EventMapperFixtures.res.mjs +440 -0
  445. package/tests/eventmapper/EventMapperTest.res +53 -0
  446. package/tests/eventmapper/EventMapperTest.res.mjs +83 -0
  447. package/tests/eventtopic/EventTopicFixtures.res +36 -0
  448. package/tests/eventtopic/EventTopicFixtures.res.mjs +65 -0
  449. package/tests/eventtopic/EventTopicOperationsTest.res +48 -0
  450. package/tests/eventtopic/EventTopicOperationsTest.res.mjs +71 -0
  451. package/tests/extensionpoint/ExtensionPointCallbackTest.res +84 -0
  452. package/tests/extensionpoint/ExtensionPointCallbackTest.res.mjs +94 -0
  453. package/tests/extensionpoint/ExtensionPointFixtures.res +173 -0
  454. package/tests/extensionpoint/ExtensionPointFixtures.res.mjs +205 -0
  455. package/tests/extensionpoint/ExtensionPointOperationsTest.res +261 -0
  456. package/tests/extensionpoint/ExtensionPointOperationsTest.res.mjs +304 -0
  457. package/tests/ftp/FTPTest.res +64 -0
  458. package/tests/ftp/FTPTest.res.mjs +88 -0
  459. package/tests/logger/LogFormatTest.res +348 -0
  460. package/tests/logger/LogFormatTest.res.mjs +309 -0
  461. package/tests/message/MessageTest.res +95 -0
  462. package/tests/message/MessageTest.res.mjs +93 -0
  463. package/tests/message/MetaEnvelopeTest.res +200 -0
  464. package/tests/message/MetaEnvelopeTest.res.mjs +188 -0
  465. package/tests/plugin/ManifestVisibilityTest.res +137 -0
  466. package/tests/plugin/ManifestVisibilityTest.res.mjs +205 -0
  467. package/tests/plugin/PluginSpecExperiment.res +13 -0
  468. package/tests/plugin/PluginSpecExperiment.res.mjs +31 -0
  469. package/tests/plugin/PluginStructureTest.res +298 -0
  470. package/tests/plugin/PluginStructureTest.res.mjs +388 -0
  471. package/tests/plugin/PluginVersionTest.res +34 -0
  472. package/tests/plugin/PluginVersionTest.res.mjs +30 -0
  473. package/tests/plugin/SchemaWalkerTest.res +115 -0
  474. package/tests/plugin/SchemaWalkerTest.res.mjs +160 -0
  475. package/tests/plugin/StateChangeSlice/PsPlaceOrder.res +24 -0
  476. package/tests/plugin/StateChangeSlice/PsPlaceOrder.res.mjs +62 -0
  477. package/tests/plugin/StateChangeSlice/PsShipOrder.res +29 -0
  478. package/tests/plugin/StateChangeSlice/PsShipOrder.res.mjs +69 -0
  479. package/tests/plugin/StateViewSlice/PsAnnotatedView.res +24 -0
  480. package/tests/plugin/StateViewSlice/PsAnnotatedView.res.mjs +94 -0
  481. package/tests/plugin/StateViewSlice/PsAvailableProductsView.res +18 -0
  482. package/tests/plugin/StateViewSlice/PsAvailableProductsView.res.mjs +69 -0
  483. package/tests/plugin/StateViewSlice/PsCustomersView.res +22 -0
  484. package/tests/plugin/StateViewSlice/PsCustomersView.res.mjs +69 -0
  485. package/tests/plugin/StateViewSlice/PsOrdersView.res +20 -0
  486. package/tests/plugin/StateViewSlice/PsOrdersView.res.mjs +81 -0
  487. package/tests/poc/StructuralSubtypingPoc.res +211 -0
  488. package/tests/poc/StructuralSubtypingPoc.res.mjs +61 -0
  489. package/tests/querydb/QueryDbFixtures.res +65 -0
  490. package/tests/querydb/QueryDbFixtures.res.mjs +135 -0
  491. package/tests/querydb/QueryDbOperationsTest.res +94 -0
  492. package/tests/querydb/QueryDbOperationsTest.res.mjs +168 -0
  493. package/tests/sideeffecthandler/SideEffectHandlerCallbackTest.res +56 -0
  494. package/tests/sideeffecthandler/SideEffectHandlerCallbackTest.res.mjs +77 -0
  495. package/tests/sideeffecthandler/SideEffectHandlerFixtures.res +96 -0
  496. package/tests/sideeffecthandler/SideEffectHandlerFixtures.res.mjs +139 -0
  497. package/tests/util/CsvStreamTest.res +54 -0
  498. package/tests/util/CsvStreamTest.res.mjs +51 -0
  499. package/tests/util/LruTest.res +73 -0
  500. package/tests/util/LruTest.res.mjs +105 -0
  501. package/tests/util/MetricsTest.res +36 -0
  502. package/tests/util/MetricsTest.res.mjs +53 -0
@@ -0,0 +1,971 @@
1
+ // Converts a file:// module URL to a path relative to the project root (located by lerna.json),
2
+ // resolving symlinks so workspace packages show their source location rather than node_modules.
3
+ // Falls back to the bare URL string on any error.
4
+ let log = Logger.fromEnv()
5
+
6
+ let toRelativePath: string => string = %raw(`function toRelativePath(moduleUrl) {
7
+ try {
8
+ const fs = process.getBuiltinModule('node:fs');
9
+ const path = process.getBuiltinModule('node:path');
10
+ const { fileURLToPath } = process.getBuiltinModule('node:url');
11
+ const builderReal = fs.realpathSync(fileURLToPath(import.meta.url));
12
+ const sliceReal = fs.realpathSync(fileURLToPath(moduleUrl));
13
+ let dir = path.dirname(builderReal);
14
+ while (dir !== path.dirname(dir)) {
15
+ if (fs.existsSync(path.join(dir, 'lerna.json'))) break;
16
+ dir = path.dirname(dir);
17
+ }
18
+ return path.relative(dir, sliceReal).replace(/\.mjs$/, '.res');
19
+ } catch(e) {
20
+ return moduleUrl.replace('file://', '');
21
+ }
22
+ }`)
23
+
24
+ type dcbResult = {
25
+ dcbEventLogOutputs: option<DcbEventLog.outputs>,
26
+ stateChangeSlicesOutputs: dict<StateChangeSlice.outputs>,
27
+ stateViewSlicesOutputs: dict<StateViewSlice.outputs>,
28
+ automationSlicesOutputs: dict<AutomationSlice.outputs>,
29
+ outboundTranslationSlicesOutputs: dict<OutboundTranslationSlice.outputs>,
30
+ inboundTranslationSlicesOutputs: dict<InboundTranslationSlice.outputs>,
31
+ dcbRuntimeSetup: option<unit => unit>,
32
+ // Shared publishJsons for all DCB StateChangeSlices in this plugin (same command topic).
33
+ // Used by Plugin_Builder to register slice names in publishToAggregates so extensions
34
+ // can dispatch commands to DCB slices via the same mechanism as regular aggregates.
35
+ dcbPublishJsons: option<Pulumi.Output.t<CommandTopic.publishJsons>>,
36
+ // SQS URL of the DCB command topic — shared by all StateChangeSlices in this
37
+ // plugin. Surfaced so the bundled Plugin EventCollector Lambda can dispatch
38
+ // PublishStateChangeSliceCommand actions from user extensions to the right queue.
39
+ dcbCommandTopicQueueUrl: option<Pulumi.Output.t<string>>,
40
+ mutationEntries: array<ReventlessInfra.Api.mutationSchemaEntry>,
41
+ queryEntries: array<ReventlessInfra.Api.querySchemaEntry>,
42
+ eventLogEntries: array<ReventlessInfra.Api.eventLogSchemaEntry>,
43
+ }
44
+
45
+ let emptyResult: dcbResult = {
46
+ dcbEventLogOutputs: None,
47
+ stateChangeSlicesOutputs: Dict.make(),
48
+ stateViewSlicesOutputs: Dict.make(),
49
+ automationSlicesOutputs: Dict.make(),
50
+ outboundTranslationSlicesOutputs: Dict.make(),
51
+ inboundTranslationSlicesOutputs: Dict.make(),
52
+ dcbRuntimeSetup: None,
53
+ dcbPublishJsons: None,
54
+ dcbCommandTopicQueueUrl: None,
55
+ mutationEntries: [],
56
+ queryEntries: [],
57
+ eventLogEntries: [],
58
+ }
59
+
60
+ module Make = (
61
+ DcbEventLogStorage: DcbEventLog_Adapter.Storage,
62
+ DcbEventTopicPublisher: EventTopic_Adapter.Publisher,
63
+ DcbCommandTopicChannel: CommandTopic_Adapter.Channel,
64
+ DcbCommandTopicChannelAsync: CommandTopic_Adapter.Channel,
65
+ RuntimeBuilder: PluginRuntime_Builder.T,
66
+ HooksConfig: Plugin_Helpers.HooksConfig,
67
+ ) => {
68
+ let construct = (
69
+ ~name: string,
70
+ ~childName: string,
71
+ ~environment: string="",
72
+ ~platformName: string="",
73
+ ~aggregateEventTopics: EventTopic.allOutputs=Dict.make(),
74
+ ~stateChangeSlices: array<module(StateChangeSlice.T)>,
75
+ ~stateViewSlices: array<module(StateViewSlice.T)>,
76
+ ~automationSlices: array<module(AutomationSlice.T)>,
77
+ ~outboundTranslationSlices: array<module(OutboundTranslationSlice.T)>,
78
+ ~inboundTranslationSlices: array<module(InboundTranslationSlice.T)>,
79
+ ~pluginStructure: option<Reventless.Plugin.pluginStructure>=?,
80
+ ~opts: Pulumi.ComponentResource.options,
81
+ ): dcbResult => {
82
+ // DCB requires at least one StateChangeSlice to produce events to the event log.
83
+ // View/automation/translation slices are consumers and need produced events to exist.
84
+ let hasDcb = stateChangeSlices->Array.length > 0
85
+ if hasDcb {
86
+ // Partition slices by channel mode (set by MakeAsync vs Make)
87
+ let syncSlices = stateChangeSlices->Array.filter((module(M: StateChangeSlice.T)) => !M.isAsync)
88
+ let asyncSlices = stateChangeSlices->Array.filter((module(M: StateChangeSlice.T)) => M.isAsync)
89
+
90
+ // Run validation: check produced vs consumed event compatibility
91
+ let produced =
92
+ stateChangeSlices->Array.map((module(Sc: StateChangeSlice.T)) =>
93
+ (Sc.Spec.name, Sc.Spec.eventSchema->S.castToUnknown)
94
+ )
95
+ let producedNamed =
96
+ stateChangeSlices->Array.map((module(Sc: StateChangeSlice.T)) =>
97
+ (Sc.Spec.name, toRelativePath(Sc.Spec.moduleUrl), Sc.Spec.eventSchema->S.castToUnknown)
98
+ )
99
+ let consumed =
100
+ stateChangeSlices->Array.map((module(Sc: StateChangeSlice.T)) =>
101
+ (Sc.Spec.name, Sc.Spec.consumedEventSchema->S.castToUnknown)
102
+ )
103
+ ->Array.concat(
104
+ stateViewSlices->Array.map((module(V: StateViewSlice.T)) =>
105
+ (V.Spec.name, V.Spec.consumedEventSchema->S.castToUnknown)
106
+ ),
107
+ )
108
+ ->Array.concat(
109
+ // Plan 04: AutomationSlice has per-source Mappings, each carrying
110
+ // its own `sourceEventSchema`. Validation contributes one entry per
111
+ // (slice, source) pair so unknown variants surface against the right
112
+ // mapping context.
113
+ automationSlices->Array.flatMap((module(A: AutomationSlice.T)) =>
114
+ A.Automation.mappings->Array.map((module(M: A.Automation.Mapping)) =>
115
+ (A.Spec.name, M.sourceEventSchema->S.castToUnknown)
116
+ )
117
+ ),
118
+ )
119
+ ->Array.concat(
120
+ outboundTranslationSlices->Array.map((module(O: OutboundTranslationSlice.T)) =>
121
+ (O.Spec.name, O.Spec.consumedEventSchema->S.castToUnknown)
122
+ ),
123
+ )
124
+
125
+ switch Reventless.DcbValidation.validateProducedAndConsumed(~produced, ~consumed) {
126
+ | Error(errors) =>
127
+ errors->Array.forEach(err =>
128
+ log.error(~comp="Dcb_Builder", `DCB validation error (${err.sliceName}): ${err.message}`)
129
+ )
130
+ | Ok() => ()
131
+ }
132
+
133
+ // Extract tagged fields from all produced event schemas for DynamoDB GSI creation
134
+ let producedSchemas = produced->Array.map(((_, schema)) => schema)
135
+ let indexes =
136
+ producedSchemas
137
+ ->Array.flatMap(schema => Reventless.DcbTag.extractTaggedFields(schema))
138
+ ->(arr => {
139
+ let seen = Set.make()
140
+ arr->Array.filter(f => {
141
+ if seen->Set.has(f) {
142
+ false
143
+ } else {
144
+ seen->Set.add(f)
145
+ true
146
+ }
147
+ })
148
+ })
149
+ ->Array.map(tagKey => `tag_${tagKey}`)
150
+ let indexes = if indexes->Array.length > 1 {
151
+ indexes->Array.concat(["tag_composite"])
152
+ } else {
153
+ indexes
154
+ }
155
+
156
+ let partitionTag = Reventless.DcbTag.derivePartitionTag(producedNamed)
157
+
158
+ // Tag keys declared `@crossPartition` across the produced event schemas.
159
+ // Derived once at build time (the scope is a property of the tag key, and
160
+ // must agree across every producer that carries it). Threaded to both the
161
+ // decision-model query builder (fan a cross-partition scalar tag into its
162
+ // own single-tag clause) and the storage adapter (read routing +
163
+ // fence scope), keeping read-scope = fence-scope per tag.
164
+ let crossPartitionTagKeys =
165
+ producedSchemas
166
+ ->Array.flatMap(schema => Reventless.DcbTag.extractCrossPartitionTagKeys(schema))
167
+ ->(arr => {
168
+ let seen = Set.make()
169
+ arr->Array.filter(f => {
170
+ if seen->Set.has(f) {
171
+ false
172
+ } else {
173
+ seen->Set.add(f)
174
+ true
175
+ }
176
+ })
177
+ })
178
+
179
+ // A tag key's scope must agree across every producer that carries it,
180
+ // else the (writer-driven) fence is ambiguous. Non-fatal — surfaces a
181
+ // mismatch loudly without aborting the deploy.
182
+ Reventless.DcbValidation.validateCrossPartitionScope(
183
+ ~producers=produced,
184
+ )->Array.forEach(err =>
185
+ log.error(
186
+ ~comp="Dcb_Builder",
187
+ `DCB cross-partition scope error (${err.sliceName}): ${err.message}`,
188
+ )
189
+ )
190
+
191
+ // Map each produced event type to its full produced tag-key set. Threaded
192
+ // into each StateChangeSlice so the decision-model query drops vacuous
193
+ // (type, tag) clause combinations (a type that can't carry the clause's
194
+ // tag) — pure dead-clause removal, results unchanged. Built from the
195
+ // producer schemas (not consumers' `consumedEventSchema`, which may
196
+ // under-declare tags a slice legitimately reads by).
197
+ let tagKeysByEventType =
198
+ producedSchemas
199
+ ->Array.map(schema => Reventless.DcbTag.extractTagKeysByEventType(schema))
200
+ ->Reventless.DcbTag.mergeTagKeysByEventType
201
+
202
+ // Warn on composite (multi-tag) decision reads that would silently miss an
203
+ // event carrying extra tags (the tag_composite key is the event's full tag
204
+ // set, so composite reads are exact-match). Non-fatal — today's slices are
205
+ // aligned; the guard catches a tag added to a multi-tag event later.
206
+ Reventless.DcbValidation.validateCompositeReads(
207
+ ~slices=stateChangeSlices->Array.map((module(Sc: StateChangeSlice.T)) => (
208
+ Sc.Spec.name,
209
+ Sc.Spec.commandSchema->S.castToUnknown,
210
+ Sc.Spec.consumedEventSchema->S.castToUnknown,
211
+ )),
212
+ ~producedTagKeys=tagKeysByEventType,
213
+ )->Array.forEach(w =>
214
+ log.warn(~comp="Dcb_Builder", `DCB composite-read warning (${w.sliceName}): ${w.message}`)
215
+ )
216
+
217
+ // --- Phase 2 (dcb-tag-scope-inference): derive scope from the global slice
218
+ // graph and THREAD it into the decision-query wiring, replacing the
219
+ // annotation-based extraction. The derived `tagKeysByEventType` is what
220
+ // fixes the sibling leak — a foreign reference key (e.g. `categoryId` on
221
+ // `ProductAdded`) is payload, so the cross-partition read clause no longer
222
+ // sweeps up sibling products. Cross-partition fan-out and capacity reads are
223
+ // preserved (generalised rule 3). Safety guard: if any slice's partition is
224
+ // ambiguous we keep the annotated values for the whole boundary rather than
225
+ // thread a partial inference. See docs/plans/dcb-tag-scope-inference.md.
226
+ let inferenceShapes =
227
+ stateChangeSlices->Array.map((module(Sc: StateChangeSlice.T)) =>
228
+ Reventless.DcbTag.sliceShapeFromSchemas(
229
+ ~name=Sc.Spec.name,
230
+ ~commandSchema=Sc.Spec.commandSchema->S.castToUnknown,
231
+ ~consumedEventSchema=Sc.Spec.consumedEventSchema->S.castToUnknown,
232
+ ~eventSchema=Sc.Spec.eventSchema->S.castToUnknown,
233
+ )
234
+ )
235
+ let inferred = Reventless.DcbScopeInference.infer(inferenceShapes)
236
+ if inferred.crossPartitionTagKeys != crossPartitionTagKeys {
237
+ log.info(
238
+ ~comp="Dcb_Builder",
239
+ `DCB scope-inference diff: crossPartitionTagKeys annotated=[${crossPartitionTagKeys->Array.join(
240
+ ", ",
241
+ )}] inferred=[${inferred.crossPartitionTagKeys->Array.join(", ")}]`,
242
+ )
243
+ }
244
+ inferred.tagKeysByEventType
245
+ ->Dict.toArray
246
+ ->Array.forEach(((eventType, inferredKeys)) => {
247
+ let annotatedKeys = tagKeysByEventType->Dict.get(eventType)->Option.getOr([])
248
+ let dropped = annotatedKeys->Array.filter(k => !(inferredKeys->Array.includes(k)))
249
+ if dropped->Array.length > 0 {
250
+ log.info(
251
+ ~comp="Dcb_Builder",
252
+ `DCB scope-inference diff: ${eventType} indexes [${inferredKeys->Array.join(
253
+ ", ",
254
+ )}] (inferred) vs [${annotatedKeys->Array.join(
255
+ ", ",
256
+ )}] (annotated) — payload now: [${dropped->Array.join(", ")}]`,
257
+ )
258
+ }
259
+ })
260
+ inferred.ambiguities->Array.forEach(((sliceName, reason)) =>
261
+ log.warn(
262
+ ~comp="Dcb_Builder",
263
+ `DCB scope-inference ambiguity (${sliceName}): ${reason}`,
264
+ )
265
+ )
266
+
267
+ // Validate any remaining explicit @crossPartition against the derived scope:
268
+ // contradictions (a key marked cross-partition that is the slice's own
269
+ // partition) are likely bugs; redundant annotations can simply be dropped.
270
+ let scopeAnnotations =
271
+ stateChangeSlices->Array.map((module(Sc: StateChangeSlice.T)) => (
272
+ Sc.Spec.name,
273
+ Reventless.DcbTag.extractCrossPartitionTagKeys(Sc.Spec.eventSchema->S.castToUnknown),
274
+ ))
275
+ let scopeIssues = Reventless.DcbValidation.validateScopeVsInference(
276
+ ~annotations=scopeAnnotations,
277
+ ~inferred,
278
+ )
279
+ scopeIssues.contradictions->Array.forEach(e =>
280
+ log.warn(~comp="Dcb_Builder", `DCB scope contradiction (${e.sliceName}): ${e.message}`)
281
+ )
282
+ scopeIssues.redundancies->Array.forEach(e =>
283
+ log.info(~comp="Dcb_Builder", `DCB scope (${e.sliceName}): ${e.message}`)
284
+ )
285
+
286
+ // All-or-nothing: thread the derived scope only when every slice resolved.
287
+ // An ambiguous boundary keeps the annotated values (no partial inference).
288
+ let useInferred = inferred.ambiguities->Array.length == 0
289
+ let effectiveCrossPartitionTagKeys =
290
+ useInferred ? inferred.crossPartitionTagKeys : crossPartitionTagKeys
291
+ let effectiveTagKeysByEventType =
292
+ useInferred ? inferred.tagKeysByEventType : tagKeysByEventType
293
+
294
+ module DcbEventLog = DcbEventLog_Builder.Make(
295
+ DcbEventLogStorage,
296
+ DcbEventTopicPublisher,
297
+ )
298
+ let dcbEventLog =
299
+ DcbEventLog.make(
300
+ ~name,
301
+ ~indexes,
302
+ ~partitionTag,
303
+ ~crossPartitionTagKeys=effectiveCrossPartitionTagKeys,
304
+ ~opts,
305
+ )
306
+
307
+ // Notify platform hook that DCB EventLog was created (AWS extracts table name)
308
+ HooksConfig.hooks.onDcbEventLogCreated->Option.forEach(hook =>
309
+ hook(dcbEventLog->Obj.magic)
310
+ )
311
+
312
+ // Create sync CommandTopic for sync StateChangeSlices (and all other DCB slice types)
313
+ module DcbCommandTopicSpec = {
314
+ module Id = Reventless.Id.String
315
+ let name = childName
316
+ @schema
317
+ type command = JSON.t
318
+ }
319
+ module DcbCommandTopic = CommandTopic_Builder.Make(
320
+ DcbCommandTopicSpec,
321
+ DcbCommandTopicChannel,
322
+ )
323
+ let dcbCommandTopic = DcbCommandTopic.make(~name=`${name}Dcb`, ~opts)
324
+
325
+ // Notify platform hook that DCB CommandTopic was created (AWS extracts SQS queue URL)
326
+ HooksConfig.hooks.onDcbCommandTopicCreated->Option.forEach(hook =>
327
+ hook(dcbCommandTopic->Obj.magic)
328
+ )
329
+
330
+ let publishJsons =
331
+ dcbCommandTopic
332
+ ->Component.operations
333
+ ->Pulumi.Output.apply(ops => ops.publishJsons)
334
+
335
+ // Create FIFO CommandTopic for async StateChangeSlices (only if any are configured)
336
+ module DcbCommandTopicSpecAsync = {
337
+ module Id = Reventless.Id.String
338
+ let name = childName ++ "Async"
339
+ @schema
340
+ type command = JSON.t
341
+ }
342
+ module DcbAsyncCommandTopic = CommandTopic_Builder.Make(
343
+ DcbCommandTopicSpecAsync,
344
+ DcbCommandTopicChannelAsync,
345
+ )
346
+ let asyncDcbCommandTopicOpt = if asyncSlices->Array.length > 0 {
347
+ let t = DcbAsyncCommandTopic.make(~name=`${name}DcbAsync`, ~opts)
348
+ HooksConfig.hooks.onDcbCommandTopicCreated->Option.forEach(hook => hook(t->Obj.magic))
349
+ Some(t)
350
+ } else {
351
+ None
352
+ }
353
+
354
+ let stateChangeSlicesOutputs =
355
+ syncSlices
356
+ ->Array.map((module(StateChangeSlice: StateChangeSlice.T)) => {
357
+ let ch = StateChangeSlice.make(
358
+ ~dcbEventLog,
359
+ ~publishJsons,
360
+ ~tagKeysByEventType=effectiveTagKeysByEventType,
361
+ ~crossPartitionTagKeys=effectiveCrossPartitionTagKeys,
362
+ ~opts,
363
+ )
364
+ (StateChangeSlice.Spec.name, ch->Component.outputs)
365
+ })
366
+ ->Dict.fromArray
367
+
368
+ // Create async slice components using the FIFO CommandTopic's publishJsons
369
+ let asyncStateChangeSlicesOutputs =
370
+ switch asyncDcbCommandTopicOpt {
371
+ | None => Dict.make()
372
+ | Some(asyncDcbCommandTopic) =>
373
+ let asyncPublishJsons =
374
+ asyncDcbCommandTopic->Component.operations->Pulumi.Output.apply(ops => ops.publishJsons)
375
+ asyncSlices
376
+ ->Array.map((module(StateChangeSlice: StateChangeSlice.T)) => {
377
+ let ch = StateChangeSlice.make(
378
+ ~dcbEventLog,
379
+ ~publishJsons=asyncPublishJsons,
380
+ ~tagKeysByEventType=effectiveTagKeysByEventType,
381
+ ~crossPartitionTagKeys=effectiveCrossPartitionTagKeys,
382
+ ~opts,
383
+ )
384
+ (StateChangeSlice.Spec.name, ch->Component.outputs)
385
+ })
386
+ ->Dict.fromArray
387
+ }
388
+
389
+ // Phase 1: Register DCB mutation SDL + resolver stubs via platform hook
390
+ switch HooksConfig.hooks.mutationResolverHook {
391
+ | Some(registerResolver) =>
392
+ syncSlices->Array.forEach((
393
+ module(S: StateChangeSlice.T),
394
+ ) => {
395
+ let commandSchema = S.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema
396
+ if !(ApiNoApiHelpers.isNoApi(commandSchema)) {
397
+ registerResolver(
398
+ ~kind=Dcb,
399
+ ~fields=[Api_Naming.sliceMutationField(~plugin=name, ~slice=S.Spec.name)],
400
+ ~commandSchema,
401
+ ~commandAuthorization=S.Spec.commandAuthorization->Obj.magic,
402
+ )
403
+ }
404
+ })
405
+ asyncSlices->Array.forEach((
406
+ module(S: StateChangeSlice.T),
407
+ ) => {
408
+ let commandSchema = S.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema
409
+ if !(ApiNoApiHelpers.isNoApi(commandSchema)) {
410
+ registerResolver(
411
+ ~kind=Dcb,
412
+ ~fields=[Api_Naming.sliceMutationField(~plugin=name, ~slice=S.Spec.name)],
413
+ ~commandSchema,
414
+ ~commandAuthorization=S.Spec.commandAuthorization->Obj.magic,
415
+ )
416
+ }
417
+ })
418
+ | None => ()
419
+ }
420
+
421
+ // Phase 2: Bind generateCommand to resolver stubs when publishJsons resolves
422
+ // Sync slices use sync CommandTopic ops (publishJsonsAndWait → CommandAccepted/Rejected)
423
+ // Async slices use async CommandTopic ops (publishJsons only → CommandPending)
424
+ switch HooksConfig.hooks.mutationBindHook {
425
+ | Some(bindHandler) =>
426
+ let _ =
427
+ dcbCommandTopic
428
+ ->Component.operations
429
+ ->Pulumi.Output.apply(ops => {
430
+ syncSlices->Array.forEach((
431
+ module(S: StateChangeSlice.T),
432
+ ) => {
433
+ let commandSchema = S.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema
434
+ if !(ApiNoApiHelpers.isNoApi(commandSchema)) {
435
+ let fieldName = Api_Naming.sliceMutationField(~plugin=name, ~slice=S.Spec.name)
436
+ let generateCommand = CommandGenerator_Callback.makeGenerateCommand(
437
+ ~publishJsons=ops.publishJsons,
438
+ ~publishJsonsAndWait=?ops.publishJsonsAndWait,
439
+ ~serviceName=S.Spec.name,
440
+ ~commandSchema=S.Spec.commandSchema->Obj.magic,
441
+ ~componentKind=CommandGenerator_Callback.StateChangeSlice,
442
+ ~stripIdFromParams=false,
443
+ )
444
+ bindHandler(~field=fieldName, ~generateCommand)
445
+ }
446
+ })
447
+ })
448
+ switch asyncDcbCommandTopicOpt {
449
+ | None => ()
450
+ | Some(asyncDcbCommandTopic) =>
451
+ let _ =
452
+ asyncDcbCommandTopic
453
+ ->Component.operations
454
+ ->Pulumi.Output.apply(asyncOps => {
455
+ asyncSlices->Array.forEach((
456
+ module(S: StateChangeSlice.T),
457
+ ) => {
458
+ let commandSchema = S.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema
459
+ if !(ApiNoApiHelpers.isNoApi(commandSchema)) {
460
+ let fieldName = Api_Naming.sliceMutationField(~plugin=name, ~slice=S.Spec.name)
461
+ let generateCommand = CommandGenerator_Callback.makeGenerateCommand(
462
+ ~publishJsons=asyncOps.publishJsons,
463
+ ~publishJsonsAndWait=?asyncOps.publishJsonsAndWait,
464
+ ~serviceName=S.Spec.name,
465
+ ~commandSchema=S.Spec.commandSchema->Obj.magic,
466
+ ~componentKind=CommandGenerator_Callback.StateChangeSlice,
467
+ ~stripIdFromParams=false,
468
+ )
469
+ bindHandler(~field=fieldName, ~generateCommand)
470
+ }
471
+ })
472
+ })
473
+ }
474
+ | None => ()
475
+ }
476
+
477
+ // Populate query field names registry for all slice types BEFORE creating them
478
+ stateViewSlices->Array.forEach((
479
+ module(V: StateViewSlice.T),
480
+ ) => {
481
+ let qn = Api_Naming.queryFieldNamesForStateView(~plugin=name, ~viewName=V.Spec.name)
482
+ let (labelField, _searchableFields) = Plugin_Structure.labelFieldsFromStateSchema(
483
+ ~entityName=V.Spec.name,
484
+ V.Spec.stateSchema->S.castToUnknown,
485
+ )
486
+ let qn = {
487
+ ...qn,
488
+ labelField,
489
+ connectionFilterTypeName: qn.returnTypeName ++ "Filter",
490
+ }
491
+ let qn = switch V.Spec.subIdConfig {
492
+ | Some(_) =>
493
+ {
494
+ ...qn,
495
+ itemsFieldName: qn.singleFieldName ++ "Items",
496
+ itemsFilterTypeName: qn.returnTypeName ++ "ItemsFilter",
497
+ }
498
+ | None => qn
499
+ }
500
+ Plugin_Helpers.queryFieldNamesRegistry->Dict.set(V.Spec.name, qn)
501
+ Plugin_Helpers.stateSchemaRegistry->Dict.set(
502
+ V.Spec.name,
503
+ V.Spec.stateSchema->S.castToUnknown,
504
+ )
505
+ })
506
+
507
+ automationSlices->Array.forEach((
508
+ module(A: AutomationSlice.T),
509
+ ) => {
510
+ let qn = Api_Naming.queryFieldNamesForSliceQueryDb(~plugin=name, ~queryDbName=A.queryDbName)
511
+ Plugin_Helpers.queryFieldNamesRegistry->Dict.set(A.queryDbName, qn)
512
+ })
513
+
514
+ outboundTranslationSlices->Array.forEach((
515
+ module(O: OutboundTranslationSlice.T),
516
+ ) => {
517
+ let qn = Api_Naming.queryFieldNamesForSliceQueryDb(~plugin=name, ~queryDbName=O.queryDbName)
518
+ Plugin_Helpers.queryFieldNamesRegistry->Dict.set(O.queryDbName, qn)
519
+ })
520
+
521
+ inboundTranslationSlices->Array.forEach((
522
+ module(I: InboundTranslationSlice.T),
523
+ ) => {
524
+ let qn = Api_Naming.queryFieldNamesForSliceQueryDb(~plugin=name, ~queryDbName=I.queryDbName)
525
+ Plugin_Helpers.queryFieldNamesRegistry->Dict.set(I.queryDbName, qn)
526
+ })
527
+
528
+ // Create StateViewSlices
529
+ let stateViewSlicesOutputs =
530
+ stateViewSlices
531
+ ->Array.map((module(StateViewSlice: StateViewSlice.T)) => {
532
+ let sv = StateViewSlice.make(~dcbEventLog, ~opts)
533
+ (StateViewSlice.Spec.name, sv->Component.outputs)
534
+ })
535
+ ->Dict.fromArray
536
+
537
+ // Build the full topic dict: aggregate event topics + this plugin's
538
+ // own DCB event topic (keyed by `<pluginName>DcbEventLog` per Plan 03
539
+ // convention). AutomationSlice mappings reference topics by these keys.
540
+ let allEventTopics = aggregateEventTopics->Dict.copy
541
+ allEventTopics->Dict.set(
542
+ name ++ "DcbEventLog",
543
+ (dcbEventLog->Component.outputs).eventTopic,
544
+ )
545
+
546
+ // Create AutomationSlices
547
+ let automationSlicesOutputs =
548
+ automationSlices
549
+ ->Array.map((module(AutoSlice: AutomationSlice.T)) => {
550
+ let context: Reventless.AutomationSlice.context = {
551
+ environment,
552
+ platformName,
553
+ pluginName: name,
554
+ sliceName: AutoSlice.Spec.name,
555
+ }
556
+ let as_ = AutoSlice.make(~allEventTopics, ~publishJsons, ~context, ~opts)
557
+ (AutoSlice.Spec.name, as_->Component.outputs)
558
+ })
559
+ ->Dict.fromArray
560
+
561
+ // Create OutboundTranslationSlices
562
+ let outboundTranslationSlicesOutputs =
563
+ outboundTranslationSlices
564
+ ->Array.map((module(OTS: OutboundTranslationSlice.T)) => {
565
+ let ots = OTS.make(~dcbEventLog, ~publishJsons, ~opts)
566
+ (OTS.Spec.name, ots->Component.outputs)
567
+ })
568
+ ->Dict.fromArray
569
+
570
+ // Create InboundTranslationSlices
571
+ let inboundTranslationSliceData =
572
+ inboundTranslationSlices->Array.map((
573
+ module(ITS: InboundTranslationSlice.T),
574
+ ) => {
575
+ let its = ITS.make(~publishJsons, ~opts)
576
+ let fieldName = Api_Naming.sliceMutationField(~plugin=name, ~slice=ITS.Spec.name)
577
+
578
+ switch HooksConfig.hooks.inboundMutationResolverHook {
579
+ | Some(registerResolver) =>
580
+ registerResolver(
581
+ ~fieldName,
582
+ ~externalInputSchema=ITS.Spec.externalInputSchema->S.castToUnknown,
583
+ )
584
+ | None => ()
585
+ }
586
+
587
+ switch HooksConfig.hooks.inboundMutationBindReceiveHook {
588
+ | Some(bindReceive) =>
589
+ let _ =
590
+ its
591
+ ->Component.operations
592
+ ->Pulumi.Output.apply(ops => bindReceive(~fieldName, ~receive=ops.receive))
593
+ | None => ()
594
+ }
595
+
596
+ (ITS.Spec.name, fieldName, its, ITS.Spec.externalInputSchema->S.castToUnknown)
597
+ })
598
+
599
+ let inboundTranslationSlicesOutputs =
600
+ inboundTranslationSliceData
601
+ ->Array.map(((specName, _, its, _)) => (specName, its->Component.outputs))
602
+ ->Dict.fromArray
603
+
604
+ // Collect InboundTranslationSlice receive functions for composite handler routing
605
+ let inboundReceiversOutput =
606
+ inboundTranslationSliceData
607
+ ->Array.map(((_, fieldName, its, _)) =>
608
+ its
609
+ ->Component.operations
610
+ ->Pulumi.Output.apply(ops => (fieldName, ops.receive))
611
+ )
612
+ ->Pulumi.Output.all
613
+ ->Pulumi.Output.apply(pairs => pairs->Dict.fromArray)
614
+
615
+ // Composite handler: SQS commands (StateChangeSlice) + direct invocations (InboundTranslation)
616
+ // + AppSync direct invocations (CommandGenerator.payload format)
617
+ let dcbHandlerBase = DcbCommandTopic.makeFilteringHandler(dcbCommandTopic)
618
+
619
+ // Shared generateCommand for AppSync direct invocations — all sync StateChangeSlices
620
+ // share the same DCB CommandTopic's publishJsons, so a single function suffices.
621
+ // commandSchema validation is skipped (permissive JSON.t schema) because AppSync
622
+ // already validates input against the SDL.
623
+ let dcbGenerateCommandOutput =
624
+ dcbCommandTopic
625
+ ->Component.operations
626
+ ->Pulumi.Output.apply(ops =>
627
+ CommandGenerator_Callback.makeGenerateCommand(
628
+ ~publishJsons=ops.publishJsons,
629
+ ~publishJsonsAndWait=?ops.publishJsonsAndWait,
630
+ ~serviceName=name,
631
+ ~commandSchema=S.json->S.castToUnknown,
632
+ ~componentKind=CommandGenerator_Callback.StateChangeSlice,
633
+ ~stripIdFromParams=false,
634
+ )
635
+ )
636
+
637
+ let dcbHandler =
638
+ (dcbHandlerBase, inboundReceiversOutput, dcbGenerateCommandOutput)
639
+ ->Pulumi.Output.all3
640
+ ->Pulumi.Output.apply(((baseHandler, receivers, generateCommand)) => {
641
+ let composite = (event, ctx) => {
642
+ let raw: dict<JSON.t> = event->Obj.magic
643
+ switch raw->Dict.get("__inboundTranslation") {
644
+ | Some(_) =>
645
+ let fieldName =
646
+ raw
647
+ ->Dict.get("fieldName")
648
+ ->Option.flatMap(JSON.Decode.string)
649
+ ->Option.getOr("")
650
+ let args = raw->Dict.get("arguments")->Option.getOr(JSON.Encode.null)
651
+ switch receivers->Dict.get(fieldName) {
652
+ | Some(receiveFn) =>
653
+ Effect.promise(async () => {
654
+ let result = await receiveFn(args)
655
+ let response = switch result {
656
+ | Ok(targetIds) =>
657
+ targetIds->Array.map(JSON.Encode.string)->JSON.Encode.array
658
+ | Error(msg) => msg->JSON.Encode.string
659
+ }
660
+ response->Obj.magic
661
+ })
662
+ | None => baseHandler(event, ctx)
663
+ }
664
+ | None =>
665
+ // Check for CommandGenerator.payload format (AppSync direct invocation)
666
+ switch (raw->Dict.get("command"), raw->Dict.get("arguments")) {
667
+ | (Some(JSON.String(_)), Some(_)) =>
668
+ let payload: CommandGenerator.payload = event->Obj.magic
669
+ (generateCommand(payload)->Effect.map(msgId => msgId->Obj.magic))->Obj.magic
670
+ | _ => baseHandler(event, ctx)
671
+ }
672
+ }
673
+ }
674
+ composite->Obj.magic
675
+ })
676
+
677
+ // Resources the sync Lambda needs access to
678
+ let dcbResources = Array.concat(
679
+ stateChangeSlicesOutputs->Dict.valuesToArray->Array.flatMap(outputs => outputs.resources),
680
+ inboundTranslationSlicesOutputs
681
+ ->Dict.valuesToArray
682
+ ->Array.flatMap(outputs => outputs.resources),
683
+ )
684
+
685
+ // Resources the async Lambda needs access to
686
+ let asyncDcbResources =
687
+ asyncStateChangeSlicesOutputs->Dict.valuesToArray->Array.flatMap(outputs => outputs.resources)
688
+
689
+ let inboundFieldNames =
690
+ inboundTranslationSliceData->Array.map(((_, fieldName, _, _)) => fieldName)
691
+ let inboundSchemas = inboundTranslationSliceData->Array.map(((_, _, _, schema)) => schema)
692
+
693
+ // Collect DCB mutation field names + TAGs for AppSync resolver creation
694
+ // (excludes @noApi slices — includes both sync and async)
695
+ let dcbMutationData =
696
+ stateChangeSlices->Array.filterMap((
697
+ module(S: StateChangeSlice.T),
698
+ ) => {
699
+ let commandSchema = S.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema
700
+ if ApiNoApiHelpers.isNoApi(commandSchema) {
701
+ None
702
+ } else {
703
+ let fieldName = Api_Naming.sliceMutationField(~plugin=name, ~slice=S.Spec.name)
704
+ let constructorNames =
705
+ Reventless.DcbTag.extractAllVariantNames(S.Spec.commandSchema->Obj.magic)
706
+ let tag = constructorNames->Array.get(0)->Option.getOr(S.Spec.name)
707
+ Some((fieldName, tag))
708
+ }
709
+ })
710
+ let dcbFieldNames = dcbMutationData->Array.map(((f, _)) => f)
711
+ let dcbTags = dcbMutationData->Array.map(((_, t)) => t)
712
+
713
+ let dcbConnectFn = (~runtime) => {
714
+ DcbCommandTopic.connect(~runtime, ~resources=dcbResources, dcbCommandTopic)
715
+
716
+ if inboundFieldNames->Array.length > 0 {
717
+ switch HooksConfig.hooks.inboundAppSyncResolverHook {
718
+ | Some(hook) =>
719
+ hook({
720
+ runtime: runtime->Obj.magic,
721
+ fieldNames: inboundFieldNames,
722
+ externalInputSchemas: inboundSchemas,
723
+ opts,
724
+ })
725
+ | None => ()
726
+ }
727
+ }
728
+
729
+ if dcbFieldNames->Array.length > 0 {
730
+ switch HooksConfig.hooks.dcbAppSyncResolverHook {
731
+ | Some(hook) =>
732
+ hook({
733
+ runtime: runtime->Obj.magic,
734
+ fieldNames: dcbFieldNames,
735
+ tags: dcbTags,
736
+ opts,
737
+ })
738
+ | None => ()
739
+ }
740
+ }
741
+ }
742
+
743
+ // Notify platform hook that all DCB slices are created (AWS calls finish on bundled builders).
744
+ // Pass dcbEventLog so the platform can wait for its operations to resolve before calling finish().
745
+ HooksConfig.hooks.onDcbSlicesCreated->Option.forEach(hook =>
746
+ hook(dcbEventLog->Obj.magic)
747
+ )
748
+
749
+ let dcbRuntimeSetup = () => {
750
+ dcbCommandTopic->RuntimeBuilder.forDcbCommandTopic(
751
+ ~handler=dcbHandler,
752
+ ~connect=dcbConnectFn,
753
+ )
754
+ // Set up async CommandTopic Lambda if any async slices are configured
755
+ asyncDcbCommandTopicOpt->Option.forEach(asyncDcbCommandTopic => {
756
+ let asyncDcbHandler = DcbAsyncCommandTopic.makeFilteringHandler(asyncDcbCommandTopic)
757
+ let asyncDcbConnectFn = (~runtime) =>
758
+ DcbAsyncCommandTopic.connect(
759
+ ~runtime,
760
+ ~resources=asyncDcbResources,
761
+ asyncDcbCommandTopic,
762
+ )
763
+ asyncDcbCommandTopic->RuntimeBuilder.forDcbCommandTopic(
764
+ ~handler=asyncDcbHandler->Obj.magic,
765
+ ~connect=asyncDcbConnectFn,
766
+ )
767
+ })
768
+ }
769
+
770
+ // DCB-specific API schema entries
771
+ //
772
+ // Stage E2: a DCB StateChangeSlice has a single GraphQL field but its
773
+ // command type may declare multiple constructors with different
774
+ // authorization rules. The @aws_auth directive operates at field
775
+ // granularity, so we read the auth for the first constructor (matching
776
+ // the existing dcbTags convention at line 549 above). When all
777
+ // constructors share the file-level default, this is exact; when they
778
+ // differ, resolver-level enforcement still fires inside the per-slice
779
+ // handler (the file-level rule applied here is the least-restrictive
780
+ // bound).
781
+ let permissionForFirstConstructor = (
782
+ ~commandSchema: S.t<unknown>,
783
+ ~commandAuthorization: unknown => Reventless.Authorization.permission,
784
+ ): option<Reventless.Authorization.permission> => {
785
+ let names = Reventless.DcbTag.extractAllVariantNames(commandSchema->Obj.magic)
786
+ switch names->Array.get(0) {
787
+ | None => None
788
+ | Some(first) =>
789
+ let hasPayload =
790
+ Reventless.DcbTag.isVariantPayloadBearing(commandSchema->Obj.magic, first)
791
+ let syntheticCmd: unknown =
792
+ hasPayload ? {"TAG": first}->Obj.magic : first->Obj.magic
793
+ Some(commandAuthorization(syntheticCmd))
794
+ }
795
+ }
796
+ let mutationEntriesFromSlices =
797
+ stateChangeSlices->Array.filterMap((
798
+ module(S: StateChangeSlice.T),
799
+ ) => {
800
+ let commandSchema = S.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema
801
+ if ApiNoApiHelpers.isNoApi(commandSchema) {
802
+ None
803
+ } else {
804
+ let sliceDef =
805
+ pluginStructure->Option.flatMap(s =>
806
+ s.stateChangeSlices->Array.find(d => d.name == S.Spec.name)
807
+ )
808
+ let fieldName = Api_Naming.sliceMutationField(~plugin=name, ~slice=S.Spec.name)
809
+ let fieldPermissions = Dict.make()
810
+ switch permissionForFirstConstructor(
811
+ ~commandSchema,
812
+ ~commandAuthorization=S.Spec.commandAuthorization->Obj.magic,
813
+ ) {
814
+ | Some(rule) => fieldPermissions->Dict.set(fieldName, rule)
815
+ | None => ()
816
+ }
817
+ Some({
818
+ ReventlessInfra.Api.fieldNames: [fieldName],
819
+ commandSchema,
820
+ fieldPermissions,
821
+ linkedViews: ?sliceDef->Option.map(d => d.linkedViews),
822
+ consistencyRead: ?sliceDef->Option.flatMap(d => d.consistencyRead),
823
+ })
824
+ }
825
+ })
826
+
827
+ let mutationEntriesFromInboundSlices =
828
+ inboundTranslationSlices->Array.map((
829
+ module(ITS: InboundTranslationSlice.T),
830
+ ) => {
831
+ let fieldName = Api_Naming.sliceMutationField(~plugin=name, ~slice=ITS.Spec.name)
832
+ let fieldPermissions = Dict.make()
833
+ switch permissionForFirstConstructor(
834
+ ~commandSchema=ITS.Spec.commandSchema->Reventless.DcbTag.toUnknownSchema,
835
+ ~commandAuthorization=ITS.Spec.commandAuthorization->Obj.magic,
836
+ ) {
837
+ | Some(rule) => fieldPermissions->Dict.set(fieldName, rule)
838
+ | None => ()
839
+ }
840
+ {
841
+ ReventlessInfra.Api.fieldNames: [fieldName],
842
+ commandSchema: ITS.Spec.externalInputSchema->S.castToUnknown,
843
+ fieldPermissions,
844
+ }
845
+ })
846
+
847
+ let stateViewEntries = stateViewSlices->Array.map((
848
+ module(V: StateViewSlice.T),
849
+ ) => {
850
+ let qn = Api_Naming.queryFieldNamesForStateView(~plugin=name, ~viewName=V.Spec.name)
851
+ let subIdField = V.Spec.subIdConfig->Option.map(c => c.subIdField)
852
+ let indexes = V.Spec.config.indexes
853
+ let indexQueries = if indexes->Array.length > 0 {Some(indexes)} else {None}
854
+ {
855
+ ReventlessInfra.Api.singleFieldName: qn.singleFieldName,
856
+ listFieldName: qn.listFieldName,
857
+ returnTypeName: qn.returnTypeName,
858
+ stateSchema: V.Spec.stateSchema->Reventless.DcbTag.toUnknownSchema,
859
+ authorization: None,
860
+ permission: V.Spec.authorization,
861
+ includeIdParam: qn.includeIdParam,
862
+ connectionSpec: true,
863
+ subIdField: ?subIdField,
864
+ indexQueries: ?indexQueries,
865
+ }
866
+ })
867
+
868
+ let automationEntries = automationSlices->Array.map((
869
+ module(A: AutomationSlice.T),
870
+ ) => {
871
+ let qn = Api_Naming.queryFieldNamesForSliceQueryDb(~plugin=name, ~queryDbName=A.queryDbName)
872
+ {
873
+ ReventlessInfra.Api.singleFieldName: qn.singleFieldName,
874
+ listFieldName: qn.listFieldName,
875
+ returnTypeName: qn.returnTypeName,
876
+ stateSchema: AutomationSlice_Callback.todoRowSchema->S.castToUnknown,
877
+ authorization: None,
878
+ connectionSpec: true,
879
+ }
880
+ })
881
+
882
+ let outboundEntries = outboundTranslationSlices->Array.map((
883
+ module(O: OutboundTranslationSlice.T),
884
+ ) => {
885
+ let qn = Api_Naming.queryFieldNamesForSliceQueryDb(~plugin=name, ~queryDbName=O.queryDbName)
886
+ {
887
+ ReventlessInfra.Api.singleFieldName: qn.singleFieldName,
888
+ listFieldName: qn.listFieldName,
889
+ returnTypeName: qn.returnTypeName,
890
+ stateSchema: OutboundTranslationSlice_Callback.todoRowSchema->S.castToUnknown,
891
+ authorization: None,
892
+ connectionSpec: true,
893
+ }
894
+ })
895
+
896
+ let inboundEntries = inboundTranslationSlices->Array.map((
897
+ module(I: InboundTranslationSlice.T),
898
+ ) => {
899
+ let qn = Api_Naming.queryFieldNamesForSliceQueryDb(~plugin=name, ~queryDbName=I.queryDbName)
900
+ {
901
+ ReventlessInfra.Api.singleFieldName: qn.singleFieldName,
902
+ listFieldName: qn.listFieldName,
903
+ returnTypeName: qn.returnTypeName,
904
+ stateSchema: InboundTranslationSlice_Callback.auditRowSchema->S.castToUnknown,
905
+ authorization: None,
906
+ connectionSpec: true,
907
+ }
908
+ })
909
+
910
+ // Collect all event schemas from produced events for eventLogEntries
911
+ let allProducedSchemas =
912
+ stateChangeSlices->Array.map((module(Sc: StateChangeSlice.T)) =>
913
+ Sc.Spec.eventSchema->S.castToUnknown
914
+ )
915
+
916
+ // NB: don't use `Option.map(r => r.id)` here — Stdlib_Option.map has a
917
+ // generic `'a => 'b` signature, so it wraps the lambda's return value
918
+ // via Caml_option.some(...). The static type of r.id is
919
+ // Pulumi.Output.t<string>, but a Pulumi Output is a JS object whose
920
+ // .id getter can resolve to undefined during early CustomResource
921
+ // construction; Caml_option.some(undefined) returns the nested-
922
+ // option sentinel `{BS_PRIVATE_NESTED_SOME_NONE: 0}`, which then
923
+ // flows through publishToAggregates into Lambda env vars and
924
+ // Pulumi's type checker rejects it (expected string, got object).
925
+ // The explicit switch keeps r.id's static type (non-option), so the
926
+ // `Some(r.id)` construction is identity-compiled — no wrapping.
927
+ let dcbCommandTopicQueueUrl = {
928
+ let outputs: CommandTopic.outputs = dcbCommandTopic->Component.outputs->Obj.magic
929
+ switch outputs.resources->Array.get(0) {
930
+ | Some(r) => Some(r.id)
931
+ | None => None
932
+ }
933
+ }
934
+ {
935
+ dcbEventLogOutputs: Some(dcbEventLog->Component.outputs),
936
+ stateChangeSlicesOutputs: Dict.fromArray(
937
+ Array.concat(
938
+ stateChangeSlicesOutputs->Dict.toArray,
939
+ asyncStateChangeSlicesOutputs->Dict.toArray,
940
+ ),
941
+ ),
942
+ stateViewSlicesOutputs,
943
+ automationSlicesOutputs,
944
+ outboundTranslationSlicesOutputs,
945
+ inboundTranslationSlicesOutputs,
946
+ dcbRuntimeSetup: Some(dcbRuntimeSetup),
947
+ dcbPublishJsons: Some(publishJsons),
948
+ dcbCommandTopicQueueUrl,
949
+ mutationEntries: Array.concat(mutationEntriesFromSlices, mutationEntriesFromInboundSlices),
950
+ queryEntries: stateViewEntries
951
+ ->Array.concat(automationEntries)
952
+ ->Array.concat(outboundEntries)
953
+ ->Array.concat(inboundEntries),
954
+ eventLogEntries: if allProducedSchemas->Array.length > 0 {
955
+ // Use the first produced event schema as representative for the event log
956
+ [
957
+ {
958
+ ReventlessInfra.Api.busKey: name ++ "DcbEventLog",
959
+ displayName: name,
960
+ eventSchema: allProducedSchemas->Array.getUnsafe(0),
961
+ },
962
+ ]
963
+ } else {
964
+ []
965
+ },
966
+ }
967
+ } else {
968
+ emptyResult
969
+ }
970
+ }
971
+ }