@reventlessdev/reventless-aws 3.0.0-alpha.167

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 (394) hide show
  1. package/CHANGELOG.md +1564 -0
  2. package/README.md +62 -0
  3. package/__mocks__/emptyModule.js +1 -0
  4. package/all-licenses.csv +565 -0
  5. package/docker-compose.dynamodb-local.yml +12 -0
  6. package/jest.setup.cjs +5 -0
  7. package/licenses-analysis.ods +0 -0
  8. package/package.json +73 -0
  9. package/rescript.json +39 -0
  10. package/reventless-licenses-analysis.xls +0 -0
  11. package/scripts/run-integration-tests.sh +43 -0
  12. package/src/Platform.res +1940 -0
  13. package/src/Platform.res.mjs +2281 -0
  14. package/src/Platform_Casts.res +106 -0
  15. package/src/Platform_Casts.res.mjs +13 -0
  16. package/src/Platform_Stack.res +153 -0
  17. package/src/Platform_Stack.res.mjs +121 -0
  18. package/src/Types.res +39 -0
  19. package/src/Types.res.mjs +21 -0
  20. package/src/adapter/AWS.res +96 -0
  21. package/src/adapter/AWS.res.mjs +186 -0
  22. package/src/adapter/AWS_Tags.res +16 -0
  23. package/src/adapter/AWS_Tags.res.mjs +48 -0
  24. package/src/adapter/Adapter_Helpers.res +19 -0
  25. package/src/adapter/Adapter_Helpers.res.mjs +37 -0
  26. package/src/adapter/Api/AppSync_EventsApi.res +172 -0
  27. package/src/adapter/Api/AppSync_EventsApi.res.mjs +96 -0
  28. package/src/adapter/Api/AppSync_Resolver_Native.res +93 -0
  29. package/src/adapter/Api/AppSync_Resolver_Native.res.mjs +67 -0
  30. package/src/adapter/Api/AppSync_Resolver_Retrying.res +653 -0
  31. package/src/adapter/Api/AppSync_Resolver_Retrying.res.mjs +462 -0
  32. package/src/adapter/Api/CommandSubscriptionResolvers_AppSync.res +29 -0
  33. package/src/adapter/Api/CommandSubscriptionResolvers_AppSync.res.mjs +15 -0
  34. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +238 -0
  35. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +179 -0
  36. package/src/adapter/Api/Platform_UIFragments_Lambda.res +229 -0
  37. package/src/adapter/Api/Platform_UIFragments_Lambda.res.mjs +245 -0
  38. package/src/adapter/Auth/Auth_Cognito.res +209 -0
  39. package/src/adapter/Auth/Auth_Cognito.res.mjs +135 -0
  40. package/src/adapter/CommandGenerator/CommandGeneratorResolvers.res +2 -0
  41. package/src/adapter/CommandGenerator/CommandGeneratorResolvers.res.mjs +12 -0
  42. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync.res +251 -0
  43. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync.res.mjs +126 -0
  44. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync_NoOp.res +20 -0
  45. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync_NoOp.res.mjs +23 -0
  46. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync_Runtime.res +1 -0
  47. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync_Runtime.res.mjs +11 -0
  48. package/src/adapter/CommandGenerator/InboundTranslationResolvers_AppSync.res +95 -0
  49. package/src/adapter/CommandGenerator/InboundTranslationResolvers_AppSync.res.mjs +50 -0
  50. package/src/adapter/CommandTopic/CommandTopicChannel.res +6 -0
  51. package/src/adapter/CommandTopic/CommandTopicChannel.res.mjs +21 -0
  52. package/src/adapter/CommandTopic/CommandTopicChannel_Helpers.res +196 -0
  53. package/src/adapter/CommandTopic/CommandTopicChannel_Helpers.res.mjs +145 -0
  54. package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res +91 -0
  55. package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res.mjs +74 -0
  56. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res +69 -0
  57. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res.mjs +62 -0
  58. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res +60 -0
  59. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res.mjs +56 -0
  60. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res +73 -0
  61. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs +65 -0
  62. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res +88 -0
  63. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res.mjs +76 -0
  64. package/src/adapter/CommandTopic/CommandTopicRemoteChannel.res +1 -0
  65. package/src/adapter/CommandTopic/CommandTopicRemoteChannel.res.mjs +9 -0
  66. package/src/adapter/CommandTopic/CommandTopicRemoteChannel_SQS.res +7 -0
  67. package/src/adapter/CommandTopic/CommandTopicRemoteChannel_SQS.res.mjs +17 -0
  68. package/src/adapter/Counter/CounterHandler_DynamoDbStream.res +103 -0
  69. package/src/adapter/Counter/CounterHandler_DynamoDbStream.res.mjs +68 -0
  70. package/src/adapter/Counter/CounterHandler_DynamoDbStream_Runtime.res +107 -0
  71. package/src/adapter/Counter/CounterHandler_DynamoDbStream_Runtime.res.mjs +145 -0
  72. package/src/adapter/DcbEventLog/DcbEventLogStorage.res +1 -0
  73. package/src/adapter/DcbEventLog/DcbEventLogStorage.res.mjs +9 -0
  74. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb.res +84 -0
  75. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb.res.mjs +52 -0
  76. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res +1436 -0
  77. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res.mjs +1143 -0
  78. package/src/adapter/EventCollector/EventCollectorChannel.res +6 -0
  79. package/src/adapter/EventCollector/EventCollectorChannel.res.mjs +21 -0
  80. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res +63 -0
  81. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs +56 -0
  82. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res +20 -0
  83. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res.mjs +32 -0
  84. package/src/adapter/EventCollector/EventCollectorChannel_Helpers.res +266 -0
  85. package/src/adapter/EventCollector/EventCollectorChannel_Helpers.res.mjs +166 -0
  86. package/src/adapter/EventCollector/EventCollectorChannel_SQS.res +81 -0
  87. package/src/adapter/EventCollector/EventCollectorChannel_SQS.res.mjs +54 -0
  88. package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res +59 -0
  89. package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res.mjs +45 -0
  90. package/src/adapter/EventCollector/EventCollectorChannel_SQS_Runtime.res +91 -0
  91. package/src/adapter/EventCollector/EventCollectorChannel_SQS_Runtime.res.mjs +89 -0
  92. package/src/adapter/EventLog/EventLogStorage.res +3 -0
  93. package/src/adapter/EventLog/EventLogStorage.res.mjs +15 -0
  94. package/src/adapter/EventLog/EventLogStorage_DynamoDb.res +25 -0
  95. package/src/adapter/EventLog/EventLogStorage_DynamoDb.res.mjs +34 -0
  96. package/src/adapter/EventLog/EventLogStorage_DynamoDbStream.res +26 -0
  97. package/src/adapter/EventLog/EventLogStorage_DynamoDbStream.res.mjs +35 -0
  98. package/src/adapter/EventLog/EventLogStorage_DynamoDb_Runtime.res +121 -0
  99. package/src/adapter/EventLog/EventLogStorage_DynamoDb_Runtime.res.mjs +137 -0
  100. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +265 -0
  101. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs +185 -0
  102. package/src/adapter/EventTopic/EventTopicPublisher.res +6 -0
  103. package/src/adapter/EventTopic/EventTopicPublisher.res.mjs +21 -0
  104. package/src/adapter/EventTopic/EventTopicPublisher_DynamoDbStream.res +25 -0
  105. package/src/adapter/EventTopic/EventTopicPublisher_DynamoDbStream.res.mjs +28 -0
  106. package/src/adapter/EventTopic/EventTopicPublisher_SNS.res +40 -0
  107. package/src/adapter/EventTopic/EventTopicPublisher_SNS.res.mjs +39 -0
  108. package/src/adapter/EventTopic/EventTopicPublisher_SNS_FIFO.res +39 -0
  109. package/src/adapter/EventTopic/EventTopicPublisher_SNS_FIFO.res.mjs +38 -0
  110. package/src/adapter/EventTopic/EventTopicPublisher_SNS_Runtime.res +5 -0
  111. package/src/adapter/EventTopic/EventTopicPublisher_SNS_Runtime.res.mjs +18 -0
  112. package/src/adapter/Mcp/MCP_Lambda.res +502 -0
  113. package/src/adapter/Mcp/MCP_Lambda.res.mjs +289 -0
  114. package/src/adapter/QueryDb/NodeResolver_AppSync.res +71 -0
  115. package/src/adapter/QueryDb/NodeResolver_AppSync.res.mjs +44 -0
  116. package/src/adapter/QueryDb/QueryDbResolvers.res +2 -0
  117. package/src/adapter/QueryDb/QueryDbResolvers.res.mjs +12 -0
  118. package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res +434 -0
  119. package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res.mjs +243 -0
  120. package/src/adapter/QueryDb/QueryDbResolvers_AppSync_NoOp.res +21 -0
  121. package/src/adapter/QueryDb/QueryDbResolvers_AppSync_NoOp.res.mjs +14 -0
  122. package/src/adapter/QueryDb/QueryDbResolvers_NoOp.res +21 -0
  123. package/src/adapter/QueryDb/QueryDbResolvers_NoOp.res.mjs +14 -0
  124. package/src/adapter/QueryDb/QueryDbStorage.res +5 -0
  125. package/src/adapter/QueryDb/QueryDbStorage.res.mjs +18 -0
  126. package/src/adapter/QueryDb/QueryDbStorage_DynamoDb.res +115 -0
  127. package/src/adapter/QueryDb/QueryDbStorage_DynamoDb.res.mjs +100 -0
  128. package/src/adapter/QueryDb/QueryDbStorage_DynamoDbStream.res +52 -0
  129. package/src/adapter/QueryDb/QueryDbStorage_DynamoDbStream.res.mjs +38 -0
  130. package/src/adapter/QueryDb/QueryDbStorage_DynamoDb_Runtime.res +271 -0
  131. package/src/adapter/QueryDb/QueryDbStorage_DynamoDb_Runtime.res.mjs +306 -0
  132. package/src/adapter/QueryDb/QueryInterceptor_Lambda.res +20 -0
  133. package/src/adapter/QueryDb/QueryInterceptor_Lambda.res.mjs +22 -0
  134. package/src/adapter/QueryEngine/QueryEngine.res +3 -0
  135. package/src/adapter/QueryEngine/QueryEngine.res.mjs +12 -0
  136. package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res +184 -0
  137. package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res.mjs +221 -0
  138. package/src/adapter/Runtime/AdminEventCollectorEntryPoint.mjs +879 -0
  139. package/src/adapter/Runtime/AggregateEntryPoint.mjs +313 -0
  140. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +327 -0
  141. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res.mjs +239 -0
  142. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res +331 -0
  143. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res.mjs +240 -0
  144. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res +247 -0
  145. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res.mjs +185 -0
  146. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate_Async.res +244 -0
  147. package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate_Async.res.mjs +185 -0
  148. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +347 -0
  149. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +244 -0
  150. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +285 -0
  151. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs +212 -0
  152. package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +124 -0
  153. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +187 -0
  154. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +147 -0
  155. package/src/adapter/Runtime/CounterEntryPoint.mjs +91 -0
  156. package/src/adapter/Runtime/DcbCommandTopicEntryPoint.mjs +265 -0
  157. package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res +122 -0
  158. package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res.mjs +89 -0
  159. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +193 -0
  160. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +137 -0
  161. package/src/adapter/Runtime/EventMapperEntryPoint.mjs +97 -0
  162. package/src/adapter/Runtime/ExtensionPointEntryPoint.mjs +101 -0
  163. package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res +83 -0
  164. package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res.mjs +59 -0
  165. package/src/adapter/Runtime/HandlerFactoryHelpers.mjs +142 -0
  166. package/src/adapter/Runtime/HeartbeatEntryPoint.mjs +38 -0
  167. package/src/adapter/Runtime/PluginExtensionPointEntryPoint.mjs +137 -0
  168. package/src/adapter/Runtime/ReadModelEntryPoint.mjs +143 -0
  169. package/src/adapter/Runtime/RuntimeEnvironment.res +1 -0
  170. package/src/adapter/Runtime/RuntimeEnvironment.res.mjs +9 -0
  171. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +254 -0
  172. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs +169 -0
  173. package/src/adapter/Runtime/SideEffectEntryPoint.mjs +81 -0
  174. package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res +178 -0
  175. package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res.mjs +129 -0
  176. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res +166 -0
  177. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res.mjs +87 -0
  178. package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +136 -0
  179. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +344 -0
  180. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +242 -0
  181. package/src/adapter/Runtime/TaskBucketEntryPoint.mjs +150 -0
  182. package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res +123 -0
  183. package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res.mjs +74 -0
  184. package/src/adapter/ScheduledPublisher/ScheduledPublisher.res +2 -0
  185. package/src/adapter/ScheduledPublisher/ScheduledPublisher.res.mjs +12 -0
  186. package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents.res +54 -0
  187. package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents.res.mjs +41 -0
  188. package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents_Runtime.res +97 -0
  189. package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents_Runtime.res.mjs +85 -0
  190. package/src/adapter/StateTopic/StateTopic_AppSync.res +418 -0
  191. package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +295 -0
  192. package/src/adapter/Task/TaskBucket.res +1 -0
  193. package/src/adapter/Task/TaskBucket.res.mjs +9 -0
  194. package/src/adapter/Task/TaskBucket_S3.res +157 -0
  195. package/src/adapter/Task/TaskBucket_S3.res.mjs +108 -0
  196. package/src/adapter/Task/TaskBucket_S3_Runtime.res +11 -0
  197. package/src/adapter/Task/TaskBucket_S3_Runtime.res.mjs +15 -0
  198. package/src/components/Aggregate_Builder.res +1 -0
  199. package/src/components/Aggregate_Builder.res.mjs +25 -0
  200. package/src/components/Aggregate_Builder_Micro.res +63 -0
  201. package/src/components/Aggregate_Builder_Micro.res.mjs +80 -0
  202. package/src/components/Aggregate_Builder_NoResolver.res +61 -0
  203. package/src/components/Aggregate_Builder_NoResolver.res.mjs +80 -0
  204. package/src/components/Aggregate_Builder_PerAggregate.res +57 -0
  205. package/src/components/Aggregate_Builder_PerAggregate.res.mjs +80 -0
  206. package/src/components/Aggregate_Builder_Single.res +59 -0
  207. package/src/components/Aggregate_Builder_Single.res.mjs +80 -0
  208. package/src/components/Aggregate_Builder_Single_Async.res +59 -0
  209. package/src/components/Aggregate_Builder_Single_Async.res.mjs +80 -0
  210. package/src/components/Api/AppSync_Adapter.res +500 -0
  211. package/src/components/Api/AppSync_Adapter.res.mjs +396 -0
  212. package/src/components/AutomationSlice_Builder.res +52 -0
  213. package/src/components/AutomationSlice_Builder.res.mjs +72 -0
  214. package/src/components/AutomationSlice_Builder_Bundled.res.mjs +70 -0
  215. package/src/components/Counter_Builder.res +10 -0
  216. package/src/components/Counter_Builder.res.mjs +16 -0
  217. package/src/components/DataCleaner.res +170 -0
  218. package/src/components/DataCleaner.res.mjs +2 -0
  219. package/src/components/DcbEventLog_Builder.res +8 -0
  220. package/src/components/DcbEventLog_Builder.res.mjs +14 -0
  221. package/src/components/ExtensionPoint_Builder.res +16 -0
  222. package/src/components/ExtensionPoint_Builder.res.mjs +37 -0
  223. package/src/components/ForeignReadModel_Builder.res +19 -0
  224. package/src/components/ForeignReadModel_Builder.res.mjs +48 -0
  225. package/src/components/InboundTranslationSlice_Builder.res +25 -0
  226. package/src/components/InboundTranslationSlice_Builder.res.mjs +31 -0
  227. package/src/components/OutboundTranslationSlice_Builder.res +60 -0
  228. package/src/components/OutboundTranslationSlice_Builder.res.mjs +76 -0
  229. package/src/components/OutboundTranslationSlice_Builder_Bundled.res.mjs +75 -0
  230. package/src/components/Plugin.res +29 -0
  231. package/src/components/Plugin.res.mjs +63 -0
  232. package/src/components/ReadModel_Builder.res +1 -0
  233. package/src/components/ReadModel_Builder.res.mjs +19 -0
  234. package/src/components/ReadModel_Builder_NoResolver.res +55 -0
  235. package/src/components/ReadModel_Builder_NoResolver.res.mjs +64 -0
  236. package/src/components/ReadModel_Builder_NoResolver_Stream.res +60 -0
  237. package/src/components/ReadModel_Builder_NoResolver_Stream.res.mjs +64 -0
  238. package/src/components/ReadModel_Builder_PerReadModel.res +53 -0
  239. package/src/components/ReadModel_Builder_PerReadModel.res.mjs +66 -0
  240. package/src/components/ReadModel_Builder_Single.res +52 -0
  241. package/src/components/ReadModel_Builder_Single.res.mjs +66 -0
  242. package/src/components/ReadModel_Builder_Single_Stream.res +60 -0
  243. package/src/components/ReadModel_Builder_Single_Stream.res.mjs +66 -0
  244. package/src/components/Scheduler.res +1 -0
  245. package/src/components/Scheduler.res.mjs +13 -0
  246. package/src/components/SideEffectHandler.res +1 -0
  247. package/src/components/SideEffectHandler.res.mjs +19 -0
  248. package/src/components/SideEffectHandler_PerSideEffectHandler.res +13 -0
  249. package/src/components/SideEffectHandler_PerSideEffectHandler.res.mjs +52 -0
  250. package/src/components/SideEffectHandler_Single.res +54 -0
  251. package/src/components/SideEffectHandler_Single.res.mjs +60 -0
  252. package/src/components/StateChangeSlice_Builder.res +17 -0
  253. package/src/components/StateChangeSlice_Builder.res.mjs +23 -0
  254. package/src/components/StateViewSlice_Builder.res +46 -0
  255. package/src/components/StateViewSlice_Builder.res.mjs +68 -0
  256. package/src/components/StateViewSlice_Builder_Bundled.res.mjs +69 -0
  257. package/src/components/StateViewSlice_Builder_Stream.res +50 -0
  258. package/src/components/StateViewSlice_Builder_Stream.res.mjs +68 -0
  259. package/src/components/Task_Builder.res +1 -0
  260. package/src/components/Task_Builder.res.mjs +28 -0
  261. package/src/components/Task_Builder_PerBucket.res +22 -0
  262. package/src/components/Task_Builder_PerBucket.res.mjs +63 -0
  263. package/src/components/Vpc.res +214 -0
  264. package/src/components/Vpc.res.mjs +117 -0
  265. package/src/components/Vpc.resi +22 -0
  266. package/src/errors/AppSync_Error.res +36 -0
  267. package/src/errors/AppSync_Error.res.mjs +38 -0
  268. package/src/errors/Cognito_Error.res +36 -0
  269. package/src/errors/Cognito_Error.res.mjs +38 -0
  270. package/src/errors/DynamoDb_Error.res +75 -0
  271. package/src/errors/DynamoDb_Error.res.mjs +86 -0
  272. package/src/errors/Kinesis_Error.res +36 -0
  273. package/src/errors/Kinesis_Error.res.mjs +38 -0
  274. package/src/errors/SNS_Error.res +34 -0
  275. package/src/errors/SNS_Error.res.mjs +38 -0
  276. package/src/errors/SQS_Error.res +40 -0
  277. package/src/errors/SQS_Error.res.mjs +41 -0
  278. package/src/plugin/cloner/ClonerRunner.res +1 -0
  279. package/src/plugin/cloner/ClonerRunner.res.mjs +9 -0
  280. package/src/plugin/cloner/ClonerRunner_Fargate.res +362 -0
  281. package/src/plugin/cloner/ClonerRunner_Fargate.res.mjs +246 -0
  282. package/src/plugin/cloner/ClonerRunner_Fargate_Runtime.res +42 -0
  283. package/src/plugin/cloner/ClonerRunner_Fargate_Runtime.res.mjs +45 -0
  284. package/src/plugin/heartbeat/HeartbeatRunner.res +1 -0
  285. package/src/plugin/heartbeat/HeartbeatRunner.res.mjs +9 -0
  286. package/src/plugin/heartbeat/HeartbeatRunner_CloudWatchEvents.res +96 -0
  287. package/src/plugin/heartbeat/HeartbeatRunner_CloudWatchEvents.res.mjs +69 -0
  288. package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res +115 -0
  289. package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res.mjs +88 -0
  290. package/src/plugin/runtime/PluginRuntimeOperations.res +5 -0
  291. package/src/plugin/runtime/PluginRuntimeOperations.res.mjs +14 -0
  292. package/src/plugin/runtime/PluginRuntime_Builder.res +769 -0
  293. package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +464 -0
  294. package/src/plugin/runtime/Util_PluginMessage_Runtime.res +13 -0
  295. package/src/plugin/runtime/Util_PluginMessage_Runtime.res.mjs +19 -0
  296. package/src/plugin/stack/Plugin_ExtensionPoint_Builder.res +28 -0
  297. package/src/plugin/stack/Plugin_ExtensionPoint_Builder.res.mjs +67 -0
  298. package/src/plugin/stack/Plugin_Stack.res +439 -0
  299. package/src/plugin/stack/Plugin_Stack.res.mjs +263 -0
  300. package/src/util/Util.res +29 -0
  301. package/src/util/Util.res.mjs +93 -0
  302. package/src/util/Util_AppSync.res +36 -0
  303. package/src/util/Util_AppSync.res.mjs +35 -0
  304. package/src/util/Util_AppSync_Caller.res +213 -0
  305. package/src/util/Util_AppSync_Caller.res.mjs +170 -0
  306. package/src/util/Util_Bundle.res +255 -0
  307. package/src/util/Util_Bundle.res.mjs +179 -0
  308. package/src/util/Util_Cloudwatch.res +14 -0
  309. package/src/util/Util_Cloudwatch.res.mjs +19 -0
  310. package/src/util/Util_CognitoGroupUser.res +39 -0
  311. package/src/util/Util_CognitoGroupUser.res.mjs +32 -0
  312. package/src/util/Util_CognitoGroupUser_Runtime.res +65 -0
  313. package/src/util/Util_CognitoGroupUser_Runtime.res.mjs +50 -0
  314. package/src/util/Util_Cognito_Runtime.res +52 -0
  315. package/src/util/Util_Cognito_Runtime.res.mjs +35 -0
  316. package/src/util/Util_DeadLetterQueue.res +154 -0
  317. package/src/util/Util_DeadLetterQueue.res.mjs +158 -0
  318. package/src/util/Util_DynamoDb.res +210 -0
  319. package/src/util/Util_DynamoDb.res.mjs +218 -0
  320. package/src/util/Util_DynamoDbStream.res +138 -0
  321. package/src/util/Util_DynamoDbStream.res.mjs +134 -0
  322. package/src/util/Util_DynamoDbStream_Runtime.res +94 -0
  323. package/src/util/Util_DynamoDbStream_Runtime.res.mjs +131 -0
  324. package/src/util/Util_DynamoDb_Runtime.res +216 -0
  325. package/src/util/Util_DynamoDb_Runtime.res.mjs +257 -0
  326. package/src/util/Util_DynamoDb_TableManager.res +23 -0
  327. package/src/util/Util_DynamoDb_TableManager.res.mjs +35 -0
  328. package/src/util/Util_DynamoDb_TableManager.resi +3 -0
  329. package/src/util/Util_Env.res +5 -0
  330. package/src/util/Util_Env.res.mjs +2 -0
  331. package/src/util/Util_EventSourceMapping.res +49 -0
  332. package/src/util/Util_EventSourceMapping.res.mjs +32 -0
  333. package/src/util/Util_HostUiDomain.res +28 -0
  334. package/src/util/Util_HostUiDomain.res.mjs +26 -0
  335. package/src/util/Util_IAM_Role.res +14 -0
  336. package/src/util/Util_IAM_Role.res.mjs +28 -0
  337. package/src/util/Util_Kinesis_Runtime.res +20 -0
  338. package/src/util/Util_Kinesis_Runtime.res.mjs +24 -0
  339. package/src/util/Util_Lambda.res +39 -0
  340. package/src/util/Util_Lambda.res.mjs +45 -0
  341. package/src/util/Util_LocalConfig.res +119 -0
  342. package/src/util/Util_LocalConfig.res.mjs +112 -0
  343. package/src/util/Util_ResourceNaming.res +15 -0
  344. package/src/util/Util_ResourceNaming.res.mjs +26 -0
  345. package/src/util/Util_S3.res +17 -0
  346. package/src/util/Util_S3.res.mjs +30 -0
  347. package/src/util/Util_S3_Runtime.res +11 -0
  348. package/src/util/Util_S3_Runtime.res.mjs +15 -0
  349. package/src/util/Util_SNS.res +33 -0
  350. package/src/util/Util_SNS.res.mjs +50 -0
  351. package/src/util/Util_SNS_FIFO.res +21 -0
  352. package/src/util/Util_SNS_FIFO.res.mjs +27 -0
  353. package/src/util/Util_SNS_Runtime.res +33 -0
  354. package/src/util/Util_SNS_Runtime.res.mjs +27 -0
  355. package/src/util/Util_SQS.res +70 -0
  356. package/src/util/Util_SQS.res.mjs +83 -0
  357. package/src/util/Util_SQS_FIFO.res +16 -0
  358. package/src/util/Util_SQS_FIFO.res.mjs +28 -0
  359. package/src/util/Util_SQS_Runtime.res +169 -0
  360. package/src/util/Util_SQS_Runtime.res.mjs +150 -0
  361. package/src/util/Util_SesPolicy_Runtime.res +54 -0
  362. package/src/util/Util_SesPolicy_Runtime.res.mjs +55 -0
  363. package/src/util/Util_StaticBundle.res +124 -0
  364. package/src/util/Util_StaticBundle.res.mjs +132 -0
  365. package/src/util/Util_Vpc.res +22 -0
  366. package/src/util/Util_Vpc.res.mjs +27 -0
  367. package/tests/AppSync_AdapterTest.res +396 -0
  368. package/tests/AppSync_AdapterTest.res.mjs +326 -0
  369. package/tests/AppSync_Resolver_RetryingTest.res +59 -0
  370. package/tests/AppSync_Resolver_RetryingTest.res.mjs +66 -0
  371. package/tests/Auth_CognitoTest.res +203 -0
  372. package/tests/Auth_CognitoTest.res.mjs +248 -0
  373. package/tests/DcbEventLogStorage_DynamoDb_RuntimeTest.res +602 -0
  374. package/tests/DcbEventLogStorage_DynamoDb_RuntimeTest.res.mjs +742 -0
  375. package/tests/EventLogStorage_DynamoDb_RuntimeTest.res +57 -0
  376. package/tests/EventLogStorage_DynamoDb_RuntimeTest.res.mjs +68 -0
  377. package/tests/MCP_LambdaTest.res +101 -0
  378. package/tests/MCP_LambdaTest.res.mjs +56 -0
  379. package/tests/Util_HostUiDomainTest.res +90 -0
  380. package/tests/Util_HostUiDomainTest.res.mjs +50 -0
  381. package/tests/Util_SQS_RuntimeTest.res +46 -0
  382. package/tests/Util_SQS_RuntimeTest.res.mjs +45 -0
  383. package/tests/Util_StaticBundleTest.res +136 -0
  384. package/tests/Util_StaticBundleTest.res.mjs +149 -0
  385. package/tests/integration/DcbCommandTopicEntryPoint_IntegrationTest.res +101 -0
  386. package/tests/integration/DcbCommandTopicEntryPoint_IntegrationTest.res.mjs +91 -0
  387. package/tests/integration/DcbEventLogStorage_DynamoDb_IntegrationTest.res +315 -0
  388. package/tests/integration/DcbEventLogStorage_DynamoDb_IntegrationTest.res.mjs +435 -0
  389. package/tests/integration/DcbIntegrationHarness.res +102 -0
  390. package/tests/integration/DcbIntegrationHarness.res.mjs +163 -0
  391. package/tests/integration/EpTestSlice.res +28 -0
  392. package/tests/integration/EpTestSlice.res.mjs +43 -0
  393. package/tests/integration/EpTestSliceBehavior.res +18 -0
  394. package/tests/integration/EpTestSliceBehavior.res.mjs +35 -0
@@ -0,0 +1,769 @@
1
+ let log = ReventlessCore.Logger.fromEnv()
2
+
3
+ type adminConfig = {
4
+ eventTopicArn: option<Pulumi.Output.t<string>>,
5
+ pluginReadModelTableName: option<Pulumi.Output.t<string>>,
6
+ // Dedicated PluginSchemaPersistence table holding deploy-time SDL fragments
7
+ // (rows keyed "deploy-schema:<name>"). The runtime schema stitch reads this
8
+ // durable source instead of the lifecycle-volatile Plugin RM Connected rows.
9
+ pluginSchemaPersistenceTableName: option<Pulumi.Output.t<string>>,
10
+ schedulerRoleArn: option<Pulumi.Output.t<string>>,
11
+ schedulerQueueArn: option<Pulumi.Output.t<string>>,
12
+ schedulerQueueName: option<Pulumi.Output.t<string>>,
13
+ appSyncApiId: option<Pulumi.Output.t<string>>,
14
+ clonerEnabled: bool,
15
+ }
16
+
17
+ let configRef: ref<adminConfig> = ref({
18
+ eventTopicArn: None,
19
+ pluginReadModelTableName: None,
20
+ pluginSchemaPersistenceTableName: None,
21
+ schedulerRoleArn: None,
22
+ schedulerQueueArn: None,
23
+ schedulerQueueName: None,
24
+ appSyncApiId: None,
25
+ clonerEnabled: false,
26
+ })
27
+
28
+ type sliceModulePaths = {
29
+ specPath: string,
30
+ behaviorPath: string,
31
+ }
32
+
33
+ type dcbConfig = {
34
+ pluginName: string,
35
+ dcbTableName: option<Pulumi.Output.t<string>>,
36
+ stateChangeSliceModulePaths: array<sliceModulePaths>,
37
+ }
38
+
39
+ let dcbConfigRef: ref<dcbConfig> = ref({
40
+ pluginName: "",
41
+ dcbTableName: None,
42
+ stateChangeSliceModulePaths: [],
43
+ })
44
+
45
+ let registeredSliceModulePaths: array<sliceModulePaths> = []
46
+
47
+ let registerStateChangeSliceSpec = (~specPath: string, ~behaviorPath: string) => {
48
+ let _ = registeredSliceModulePaths->Array.push({specPath, behaviorPath})
49
+ }
50
+
51
+ let registerDcbConfig = (~pluginName, ~dcbTableName=?, ~stateChangeSliceModulePaths=[], ()) => {
52
+ dcbConfigRef := {pluginName, dcbTableName, stateChangeSliceModulePaths}
53
+ stateChangeSliceModulePaths->Array.length
54
+ }
55
+
56
+ let registerDcbTableName = (dcbTableName: Pulumi.Output.t<string>) =>
57
+ dcbConfigRef := {...dcbConfigRef.contents, dcbTableName: Some(dcbTableName)}
58
+
59
+ type heartbeatConfig = {
60
+ pluginId: string,
61
+ heartbeatTimeout: int,
62
+ epQueueUrl: option<Pulumi.Output.t<string>>,
63
+ }
64
+
65
+ let heartbeatConfigRef: ref<heartbeatConfig> = ref({
66
+ pluginId: "",
67
+ heartbeatTimeout: 10,
68
+ epQueueUrl: None,
69
+ })
70
+
71
+ let registerHeartbeatConfig = (~pluginId, ~heartbeatTimeout=10, ~epQueueUrl=?, ()) =>
72
+ heartbeatConfigRef := {pluginId, heartbeatTimeout, epQueueUrl}
73
+
74
+ // Per-flavor commandHandlerConfig override for the two DCB command-handler Lambdas
75
+ // (`<Plugin>DcbCmdHandler` sync / `<Plugin>DcbAsyncCmdHandler` async); populated by
76
+ // Platform.MakeWithConfig from `commandHandlerConfig.stateChanges.{sync,async}`.
77
+ let syncStateChangesConfigRef: ref<ReventlessCore.Runtime.commandHandlerConfig> = ref(
78
+ ({}: ReventlessCore.Runtime.commandHandlerConfig),
79
+ )
80
+ let asyncStateChangesConfigRef: ref<ReventlessCore.Runtime.commandHandlerConfig> = ref(
81
+ ({}: ReventlessCore.Runtime.commandHandlerConfig),
82
+ )
83
+ let setStateChangesConfig = (~sync=?, ~async=?, ()) => {
84
+ sync->Option.forEach(c => syncStateChangesConfigRef := c)
85
+ async->Option.forEach(c => asyncStateChangesConfigRef := c)
86
+ }
87
+
88
+ let registerConfig = (
89
+ ~eventTopicArn=?,
90
+ ~pluginReadModelTableName=?,
91
+ ~pluginSchemaPersistenceTableName=?,
92
+ ~schedulerRoleArn=?,
93
+ ~schedulerQueueArn=?,
94
+ ~schedulerQueueName=?,
95
+ ~appSyncApiId=?,
96
+ ~clonerEnabled=false,
97
+ (),
98
+ ) =>
99
+ configRef := {
100
+ eventTopicArn,
101
+ pluginReadModelTableName,
102
+ pluginSchemaPersistenceTableName,
103
+ schedulerRoleArn,
104
+ schedulerQueueArn,
105
+ schedulerQueueName,
106
+ appSyncApiId,
107
+ clonerEnabled,
108
+ }
109
+
110
+ // PluginRuntime_Builder is a functor so that the caller can inject the EventCollectorChannel
111
+ // implementation. All other runtime builders hardcode EventCollectorChannel.DynamoDbStream, but
112
+ // the Plugin admin handler (AdminEventCollectorEntryPoint) is shared across DCB and aggregate
113
+ // plugins which may use different event log backends. The functor keeps the builder generic
114
+ // while the concrete instantiation picks the right channel at the platform level.
115
+ module Make = (
116
+ EventCollectorChannel: ReventlessCore.EventCollector_Adapter.Channel
117
+ with type runtimeParts = Util.Lambda.runtimeParts,
118
+ ): (
119
+ ReventlessCore.PluginRuntime_Builder.T
120
+ with type context = PulumiAws.Lambda.context
121
+ and type runtimeParts = Util.Lambda.runtimeParts
122
+ and module EventCollectorChannel = EventCollectorChannel
123
+ ) => {
124
+ type context = PulumiAws.Lambda.context
125
+ type runtimeParts = Util.Lambda.runtimeParts
126
+ module EventCollectorChannel = EventCollectorChannel
127
+
128
+ let registerPluginName = (pluginName: string) =>
129
+ dcbConfigRef := {...dcbConfigRef.contents, pluginName}
130
+
131
+ let outputOrPlaceholder = opt =>
132
+ switch opt {
133
+ | Some(v) => v->Pulumi.Output.asInput
134
+ | None => "NOT_AVAILABLE"->Pulumi.Output.make->Pulumi.Output.asInput
135
+ }
136
+
137
+ // Synthesises an admin-flavoured eventCollectorContext when no plugin context
138
+ // is registered for this EventCollector. The admin Lambda processes outgoing
139
+ // events of its own Plugin ExtensionPoint and does not consume the auto-
140
+ // included Connect extension (it IS the Plugin EP). The fake pluginDefinition
141
+ // keeps the JSON shape consistent so the entry point can parse uniformly.
142
+ let synthesizeAdminContext = (): ReventlessCore.Plugin_Helpers.eventCollectorContext => {
143
+ let config = configRef.contents
144
+ // Stable JSON literal — matches Reventless.Plugin.pluginDefinitionSchema
145
+ // (optional fields encoded as null via js_nullable).
146
+ let fakePluginDefinitionJson =
147
+ `{"id":"Admin@INTERNAL","name":"Admin","version":"INTERNAL","extensionPoints":[],"extensions":[],"eventCollector":"NOT-SET","extensionProtocols":[],"apiSchemaFragment":null,"apiTarget":null,"uiFragments":null,"structure":null}`->Pulumi.Output.make
148
+ let adminEpEventTopicArn = switch config.eventTopicArn {
149
+ | Some(arn) => arn
150
+ | None => Pulumi.Output.make("NOT_AVAILABLE")
151
+ }
152
+ {
153
+ pluginDefinitionJson: fakePluginDefinitionJson,
154
+ extensionPoints: [
155
+ {
156
+ specModule: ReventlessCore.Plugin_Helpers.adminPluginExtensionPointSpecModule,
157
+ mappingsModule: ReventlessCore.Plugin_Helpers.adminPluginExtensionPointMappingsModule,
158
+ eventTopicArn: adminEpEventTopicArn,
159
+ aggregateNames: [ReventlessCore.PluginSpec.name],
160
+ },
161
+ ],
162
+ connectExtension: None,
163
+ extensions: [],
164
+ pluginExtensionPointCmdTopicUrl: Pulumi.Output.make(""),
165
+ publishToAggregates: Dict.make(),
166
+ readModelQueueUrls: Dict.make(),
167
+ readModelNamesForSourceName: Dict.make(),
168
+ }
169
+ }
170
+
171
+ let forPluginEventCollector: ReventlessCore.Runtime.forEventCollector<
172
+ ReventlessCore.Runtime.effectHandler<
173
+ EventCollectorChannel.callbackEvent,
174
+ context,
175
+ unit,
176
+ string,
177
+ >,
178
+ ReventlessCore.EventCollector.component,
179
+ > = (
180
+ ~handler as _,
181
+ ~eventTopics,
182
+ ~resources,
183
+ ~memorySize=1024,
184
+ ~timeout=30,
185
+ eventCollector,
186
+ ) => {
187
+ let resource = eventCollector->ReventlessCore.Component.toPulumiResource
188
+ let name = resource.name->ReventlessCore.ComponentType.nameOpt(
189
+ ReventlessCore.EventCollector.componentType,
190
+ )
191
+ let opts = {Pulumi.ComponentResource.parent: resource}
192
+ let config = configRef.contents
193
+
194
+ // Extract the SQS queue from the EventCollector channel
195
+ let channel = eventCollector->ReventlessCore.EventCollector_Adapter.channel
196
+ let channelParts: Util.SQS.channelParts = Obj.magic(channel.parts)
197
+ let queue = channelParts.queue
198
+
199
+ // Pull the per-EventCollector context registered by Plugin_Helpers.connect
200
+ // (plugins) or synthesise an admin one if none registered.
201
+ let context: ReventlessCore.Plugin_Helpers.eventCollectorContext =
202
+ switch ReventlessCore.Plugin_Helpers.eventCollectorContextRef.contents->Dict.get(name) {
203
+ | Some(ctx) => ctx
204
+ | None => synthesizeAdminContext()
205
+ }
206
+
207
+ let envVars: dict<Pulumi.Input.t<string>> = Dict.make()
208
+
209
+ // Lambda env vars carry the actual cmd-topic URLs that publishToAggregates
210
+ // points at (HANDLER_CONFIG holds only the env-var names — same indirection
211
+ // PluginExtensionPointEntryPoint uses).
212
+ context.publishToAggregates
213
+ ->Dict.toArray
214
+ ->Array.forEach(((_aggName, queueUrlOutput)) => {
215
+ // Env var name follows the existing convention: PTA_<aggregate>_QUEUE_URL.
216
+ let envVarName = `PTA_${_aggName}_QUEUE_URL`
217
+ envVars->Dict.set(envVarName, queueUrlOutput->Pulumi.Output.asInput)
218
+ })
219
+
220
+ // Same indirection for ReadModel EventCollector SQS URLs — only populated
221
+ // when user extensions enqueue directly into RMs (Extension_Operations
222
+ // publishToReadModels path). Empty for the admin Lambda and for the
223
+ // current Orders_Extension fixture (its RM is fed via the slice EventLog →
224
+ // RM EventColl subscription chain instead).
225
+ context.readModelQueueUrls
226
+ ->Dict.toArray
227
+ ->Array.forEach(((_rmName, queueUrlOutput)) => {
228
+ let envVarName = `PRM_${_rmName}_QUEUE_URL`
229
+ envVars->Dict.set(envVarName, queueUrlOutput->Pulumi.Output.asInput)
230
+ })
231
+
232
+ // Outgoing arrays first — resolve EP eventTopicArns, then bundle everything
233
+ // into one Output.apply that builds the JSON.
234
+ let epEventTopicArnsOutput =
235
+ context.extensionPoints
236
+ ->Array.map(ep => ep.eventTopicArn)
237
+ ->Pulumi.Output.all
238
+
239
+ // HANDLER_CONFIG no longer carries pluginDefinition — it grew past AWS
240
+ // Lambda's 5120-byte UpdateFunctionConfiguration limit once pluginStructure
241
+ // started landing inline. The full pluginDefinition is shipped as a
242
+ // separate `pluginDefinition.json` asset bundled alongside index.mjs (see
243
+ // the buildCodeArchive call below); the entry point reads it from disk at
244
+ // cold start. Slim HANDLER_CONFIG keeps just the orchestration fields.
245
+ let handlerConfigJson =
246
+ Pulumi.Output.all([
247
+ queue.id,
248
+ context.pluginExtensionPointCmdTopicUrl->Pulumi.Output.asInput->Obj.magic,
249
+ config.eventTopicArn->outputOrPlaceholder->Obj.magic,
250
+ config.pluginReadModelTableName->outputOrPlaceholder->Obj.magic,
251
+ config.schedulerRoleArn->outputOrPlaceholder->Obj.magic,
252
+ config.schedulerQueueArn->outputOrPlaceholder->Obj.magic,
253
+ config.schedulerQueueName->outputOrPlaceholder->Obj.magic,
254
+ config.appSyncApiId->outputOrPlaceholder->Obj.magic,
255
+ epEventTopicArnsOutput->Pulumi.Output.asInput->Obj.magic,
256
+ config.pluginSchemaPersistenceTableName->outputOrPlaceholder->Obj.magic,
257
+ ])
258
+ ->Pulumi.Output.apply(values => {
259
+ let queueUrl = values->Array.getUnsafe(0)
260
+ let pluginEpCmdTopicUrl = values->Array.getUnsafe(1)
261
+ let topLevelEventTopicArn = values->Array.getUnsafe(2)
262
+ let rmTable = values->Array.getUnsafe(3)
263
+ let schedRoleArn = values->Array.getUnsafe(4)
264
+ let schedQueueArn = values->Array.getUnsafe(5)
265
+ let schedQueueName = values->Array.getUnsafe(6)
266
+ let appSyncApiId = values->Array.getUnsafe(7)
267
+ let epEventTopicArns: array<string> = Obj.magic(values->Array.getUnsafe(8))
268
+ let schemaPersistenceTable = values->Array.getUnsafe(9)
269
+
270
+ let dict = Dict.make()
271
+ dict->Dict.set("queueUrl", JSON.Encode.string(queueUrl))
272
+ dict->Dict.set("pluginExtensionPointCmdTopicUrl", JSON.Encode.string(pluginEpCmdTopicUrl))
273
+ dict->Dict.set("eventTopicArn", JSON.Encode.string(topLevelEventTopicArn))
274
+ dict->Dict.set("pluginReadModelTableName", JSON.Encode.string(rmTable))
275
+ dict->Dict.set(
276
+ "pluginSchemaPersistenceTableName",
277
+ JSON.Encode.string(schemaPersistenceTable),
278
+ )
279
+ dict->Dict.set("appSyncApiId", JSON.Encode.string(appSyncApiId))
280
+ dict->Dict.set("clonerEnabled", JSON.Encode.bool(config.clonerEnabled))
281
+ dict->Dict.set("schedulerRoleArn", JSON.Encode.string(schedRoleArn))
282
+ dict->Dict.set("schedulerQueueArn", JSON.Encode.string(schedQueueArn))
283
+ dict->Dict.set("schedulerQueueName", JSON.Encode.string(schedQueueName))
284
+
285
+ // pluginDefinition lives in pluginDefinition.json (see asset bundle
286
+ // below). The entry point reads it via fs.readFileSync at cold start.
287
+
288
+ // Pair each EP with its already-resolved eventTopicArn (parallel arrays)
289
+ // BEFORE sorting, so the (ep, arn) pairing survives the reorder. Then
290
+ // sort by ep.specModule for stable JSON output across deploys — without
291
+ // this, push-order drift in upstream Dict iteration produces pointless
292
+ // Lambda env-var "updates" on every `pulumi up`.
293
+ let extensionPointsArr =
294
+ context.extensionPoints
295
+ ->Array.mapWithIndex((ep, i) => (ep, epEventTopicArns->Array.getUnsafe(i)))
296
+ ->Array.toSorted(((a, _), (b, _)) => String.compare(a.specModule, b.specModule))
297
+ ->Array.map(((ep, eventTopicArn)) => {
298
+ let entryDict = Dict.make()
299
+ entryDict->Dict.set("specModule", JSON.Encode.string(ep.specModule))
300
+ entryDict->Dict.set("mappingsModule", JSON.Encode.string(ep.mappingsModule))
301
+ entryDict->Dict.set("eventTopicArn", JSON.Encode.string(eventTopicArn))
302
+ entryDict->Dict.set(
303
+ "aggregateNames",
304
+ ep.aggregateNames->Array.map(JSON.Encode.string)->JSON.Encode.array,
305
+ )
306
+ JSON.Encode.object(entryDict)
307
+ })
308
+ dict->Dict.set("extensionPoints", JSON.Encode.array(extensionPointsArr))
309
+
310
+ let connectExtensionValue = switch context.connectExtension {
311
+ | Some(ce) =>
312
+ let ceDict = Dict.make()
313
+ ceDict->Dict.set("specModule", JSON.Encode.string(ce.specModule))
314
+ ceDict->Dict.set("mappingsModule", JSON.Encode.string(ce.mappingsModule))
315
+ ceDict->Dict.set("extensionPointName", JSON.Encode.string(ce.extensionPointName))
316
+ JSON.Encode.object(ceDict)
317
+ | None => JSON.Encode.null
318
+ }
319
+ dict->Dict.set("connectExtension", connectExtensionValue)
320
+
321
+ // User-declared extensions — one entry per merged extension group.
322
+ // Each entry carries enough metadata for the bundled handler to
323
+ // dynamic-import the spec / mapping modules and filter the top-level
324
+ // publishToAggregates + readModelQueueUrls maps down to the subset
325
+ // this extension actually uses.
326
+ // Sort by extension name so the serialized JSON is stable across deploys.
327
+ let extensionsArr =
328
+ context.extensions
329
+ ->Array.toSorted((a, b) => String.compare(a.name, b.name))
330
+ ->Array.map(ext => {
331
+ let entryDict = Dict.make()
332
+ entryDict->Dict.set("name", JSON.Encode.string(ext.name))
333
+ entryDict->Dict.set("specModule", JSON.Encode.string(ext.specModule))
334
+ entryDict->Dict.set("mappingsModule", JSON.Encode.string(ext.mappingsModule))
335
+ entryDict->Dict.set("delegateModule", JSON.Encode.string(ext.delegateModule))
336
+ entryDict->Dict.set("extensionPointName", JSON.Encode.string(ext.extensionPointName))
337
+ entryDict->Dict.set(
338
+ "aggregateNames",
339
+ ext.aggregateNames->Array.map(JSON.Encode.string)->JSON.Encode.array,
340
+ )
341
+ entryDict->Dict.set(
342
+ "readModelNames",
343
+ ext.readModelNames->Array.map(JSON.Encode.string)->JSON.Encode.array,
344
+ )
345
+ JSON.Encode.object(entryDict)
346
+ })
347
+ dict->Dict.set("extensions", JSON.Encode.array(extensionsArr))
348
+
349
+ let publishToAggregatesDict = Dict.make()
350
+ context.publishToAggregates
351
+ ->Dict.keysToArray
352
+ ->Array.forEach(aggName =>
353
+ publishToAggregatesDict->Dict.set(
354
+ aggName,
355
+ JSON.Encode.string(`PTA_${aggName}_QUEUE_URL`),
356
+ )
357
+ )
358
+ dict->Dict.set("publishToAggregates", JSON.Encode.object(publishToAggregatesDict))
359
+
360
+ let readModelQueueUrlsDict = Dict.make()
361
+ context.readModelQueueUrls
362
+ ->Dict.keysToArray
363
+ ->Array.forEach(rmName =>
364
+ readModelQueueUrlsDict->Dict.set(
365
+ rmName,
366
+ JSON.Encode.string(`PRM_${rmName}_QUEUE_URL`),
367
+ )
368
+ )
369
+ dict->Dict.set("readModelQueueUrls", JSON.Encode.object(readModelQueueUrlsDict))
370
+
371
+ let rmNamesForSourceDict = Dict.make()
372
+ context.readModelNamesForSourceName
373
+ ->Dict.toArray
374
+ ->Array.forEach(((sourceName, rmNames)) =>
375
+ rmNamesForSourceDict->Dict.set(
376
+ sourceName,
377
+ rmNames->Array.map(JSON.Encode.string)->JSON.Encode.array,
378
+ )
379
+ )
380
+ dict->Dict.set("readModelNamesForSourceName", JSON.Encode.object(rmNamesForSourceDict))
381
+
382
+ JSON.Encode.object(dict)->JSON.stringify
383
+ })
384
+ envVars->Dict.set("HANDLER_CONFIG", handlerConfigJson->Pulumi.Output.asInput)
385
+
386
+ // User-extension packages: each merged extension entry contributes three
387
+ // module specifiers (EP spec, user mapping file, delegate spec). Extract
388
+ // their npm package names and resolve to package roots so buildCodeArchive
389
+ // can include them in the asset zip — the bundled entry point then
390
+ // dynamic-imports each module at cold start. Admin path has no
391
+ // extensions, so packageDirs stays empty and the asset matches the
392
+ // pre-extension-wiring shape.
393
+ let packageDirs: dict<string> = Dict.make()
394
+ let addPackageFor = spec => {
395
+ let pkgName = Util_Bundle.extractPackageName(spec)
396
+ packageDirs->Dict.set(pkgName, Util_Bundle.resolvePackageRoot(pkgName))
397
+ }
398
+ context.extensions->Array.forEach(ext => {
399
+ [ext.specModule, ext.mappingsModule, ext.delegateModule]->Array.forEach(addPackageFor)
400
+ })
401
+ // Outgoing EPs: the entry point dynamic-imports ep.specModule and
402
+ // ep.mappingsModule at cold start (AdminEventCollectorEntryPoint.mjs).
403
+ // Without this walk the EP spec package (e.g. <plugin>-spec) is missing
404
+ // from the asset zip even though HANDLER_CONFIG references it, and the
405
+ // Lambda crashes with MODULE_NOT_FOUND before any event is processed.
406
+ context.extensionPoints->Array.forEach(ep => {
407
+ [ep.specModule, ep.mappingsModule]->Array.forEach(addPackageFor)
408
+ })
409
+ // The auto-included PluginConnectExtension entry is also dynamic-imported
410
+ // at cold start. Its modules live in reventless-infra / reventless-core,
411
+ // which are explicitly bundled below — walking it here is defensive but
412
+ // costs nothing once those entries dedupe in packageDirs.
413
+ switch context.connectExtension {
414
+ | Some(ce) => [ce.specModule, ce.mappingsModule]->Array.forEach(addPackageFor)
415
+ | None => ()
416
+ }
417
+ // Bundle the framework packages alongside the entry point so the deployed
418
+ // Lambda picks up uncommitted local changes without waiting for the Lambda
419
+ // Layer rebuild (the layer fetches @reventlessdev/reventless-* from GitHub
420
+ // Packages, not the local pnpm workspace). Mirrors the DCB asset pattern in
421
+ // forDcbCommandTopic below. Slightly larger function bundle in exchange
422
+ // for matching dev/CI source.
423
+ packageDirs->Dict.set(
424
+ "@reventlessdev/reventless-aws",
425
+ Util_Bundle.resolvePackageRoot("@reventlessdev/reventless-aws"),
426
+ )
427
+ packageDirs->Dict.set(
428
+ "@reventlessdev/reventless-core",
429
+ Util_Bundle.resolvePackageRoot("@reventlessdev/reventless-core"),
430
+ )
431
+
432
+ // pluginDefinition.json must resolve before the asset zip is built;
433
+ // wrap the bundle in an Output.apply over the Output<string>. The
434
+ // resulting Output<codeArchive> threads through to makeFromCodeAsset's
435
+ // Archive.t / string args via Obj.magic — Pulumi's TS API accepts
436
+ // Output<Archive.t> wherever it accepts Archive.t, and Pulumi.Input.make
437
+ // is identity at runtime, so the Output unwraps correctly inside the
438
+ // Lambda Function args.
439
+ let bundleOutput =
440
+ context.pluginDefinitionJson->Pulumi.Output.apply(pluginDefinitionJson => {
441
+ let extraStringAssets = Dict.make()
442
+ extraStringAssets->Dict.set("pluginDefinition.json", pluginDefinitionJson)
443
+ Util_Bundle.buildCodeArchive(
444
+ ~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/AdminEventCollectorEntryPoint.mjs",
445
+ ~packageDirs,
446
+ ~extraStringAssets,
447
+ )
448
+ })
449
+ let codeOutput = bundleOutput->Pulumi.Output.apply(b => b.code)
450
+ let sourceCodeHashOutput = bundleOutput->Pulumi.Output.apply(b => b.sourceCodeHash)
451
+
452
+ let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
453
+ ~name,
454
+ ~code=codeOutput->Obj.magic,
455
+ ~sourceCodeHash=sourceCodeHashOutput->Obj.magic,
456
+ ~envVars,
457
+ ~memorySize,
458
+ ~timeout,
459
+ ~opts,
460
+ )
461
+ let _connectResources = EventCollectorChannel.connect(
462
+ ~name,
463
+ ~channelSpecs=[
464
+ {
465
+ channel: eventCollector->ReventlessCore.EventCollector_Adapter.channel,
466
+ eventTopics,
467
+ resources,
468
+ },
469
+ ],
470
+ ~runtime,
471
+ ~opts,
472
+ )
473
+
474
+ // Admin-only cross-plugin SNS subscription permissions. The admin EC
475
+ // Lambda's manageSubscriptions hook (Phase 3 Step 1) creates SNS
476
+ // subscriptions at runtime as plugins connect/disconnect; that needs
477
+ // sns:Subscribe / sns:Unsubscribe / sns:ListSubscriptionsByTopic on every
478
+ // plugin's EP event topic. Detected via the same context lookup used to
479
+ // synthesize the admin-flavoured handler config: no registered context
480
+ // for this EventCollector name => admin path. Per-plugin EC Lambdas have
481
+ // a registered context and stay on the narrow IAM perimeter from
482
+ // EventCollectorChannel_Helpers.connectLambda.
483
+ let isAdminEventCollector =
484
+ ReventlessCore.Plugin_Helpers.eventCollectorContextRef.contents
485
+ ->Dict.get(name)
486
+ ->Option.isNone
487
+ if isAdminEventCollector {
488
+ let _ = PulumiAws.IAM.RolePolicy.make(
489
+ ~name=`${name}-snsManageSubs`,
490
+ ~args={
491
+ policy: PulumiAws.PolicyDocument.make(
492
+ ~id=`${name}SnsManageSubsPolicy`,
493
+ ~statements=[
494
+ {
495
+ sid: "AllowAdminManageCrossPluginSnsSubscriptions",
496
+ effect: Allow,
497
+ actions: Actions([
498
+ "sns:Subscribe",
499
+ "sns:Unsubscribe",
500
+ "sns:ListSubscriptionsByTopic",
501
+ "sns:GetSubscriptionAttributes",
502
+ ]),
503
+ resources: AllResources,
504
+ },
505
+ // mkUpdateApiSchema runs on each Connect/Disconnect alongside
506
+ // manageSubscriptions — pushes the stitched SDL to AppSync via
507
+ // StartSchemaCreation. AppSync requires both the API-level
508
+ // appsync:StartSchemaCreation and appsync:GetSchemaCreationStatus
509
+ // permissions on the deployed API. GetIntrospectionSchema backs the
510
+ // shrink-guard circuit breaker, which reads the live schema before
511
+ // pushing to detect (and refuse) a catastrophic field-count drop.
512
+ {
513
+ sid: "AllowAdminStartSchemaCreation",
514
+ effect: Allow,
515
+ actions: Actions([
516
+ "appsync:StartSchemaCreation",
517
+ "appsync:GetSchemaCreationStatus",
518
+ "appsync:GetIntrospectionSchema",
519
+ ]),
520
+ resources: AllResources,
521
+ },
522
+ ],
523
+ )
524
+ ->PulumiAws.PolicyDocument.toJsonString
525
+ ->Pulumi.Input.make,
526
+ role: runtime.parts.lambdaRole.id->Pulumi.Output.asInput,
527
+ },
528
+ )
529
+
530
+ // manageSubscriptions / reconcileSubscriptionsOnce / forwardCommand all
531
+ // Scan the Plugin RM table to find connected peers + route forwarded
532
+ // commands. The Plugin RM is owned by a separate ReadModel EC, so the
533
+ // admin EC's default IAM policy does not include any DynamoDB perms on
534
+ // it. Grant Scan only — the admin EC never writes here, projections
535
+ // happen on the dedicated RM Lambda.
536
+ switch config.pluginReadModelTableName {
537
+ | Some(rmTableOutput) =>
538
+ let policyJson =
539
+ rmTableOutput->Pulumi.Output.apply(tableName =>
540
+ PulumiAws.PolicyDocument.make(
541
+ ~id=`${name}PluginRmScanPolicy`,
542
+ ~statements=[
543
+ {
544
+ sid: "AllowAdminScanPluginRm",
545
+ effect: Allow,
546
+ actions: Actions(["dynamodb:Scan"]),
547
+ resources: Resource("arn:aws:dynamodb:*:*:table/" ++ tableName),
548
+ },
549
+ ],
550
+ )->PulumiAws.PolicyDocument.toJsonString
551
+ )
552
+ let _ = PulumiAws.IAM.RolePolicy.make(
553
+ ~name=`${name}-pluginRmScan`,
554
+ ~args={
555
+ policy: policyJson->Pulumi.Output.asInput,
556
+ role: runtime.parts.lambdaRole.id->Pulumi.Output.asInput,
557
+ },
558
+ )
559
+ | None => ()
560
+ }
561
+
562
+ // mkUpdateApiSchema reads each plugin's deploy-time SDL fragment from the
563
+ // dedicated PluginSchemaPersistence table (deploy-schema:<name> rows) to
564
+ // re-stitch the live schema. Grant Scan on that table too — it is owned by
565
+ // Platform.res, so the admin EC's default policy includes no perms on it.
566
+ switch config.pluginSchemaPersistenceTableName {
567
+ | Some(schemaTableOutput) =>
568
+ let policyJson =
569
+ schemaTableOutput->Pulumi.Output.apply(tableName =>
570
+ PulumiAws.PolicyDocument.make(
571
+ ~id=`${name}PluginSchemaScanPolicy`,
572
+ ~statements=[
573
+ {
574
+ sid: "AllowAdminScanPluginSchemaPersistence",
575
+ effect: Allow,
576
+ actions: Actions(["dynamodb:Scan"]),
577
+ resources: Resource("arn:aws:dynamodb:*:*:table/" ++ tableName),
578
+ },
579
+ ],
580
+ )->PulumiAws.PolicyDocument.toJsonString
581
+ )
582
+ let _ = PulumiAws.IAM.RolePolicy.make(
583
+ ~name=`${name}-pluginSchemaScan`,
584
+ ~args={
585
+ policy: policyJson->Pulumi.Output.asInput,
586
+ role: runtime.parts.lambdaRole.id->Pulumi.Output.asInput,
587
+ },
588
+ )
589
+ | None => ()
590
+ }
591
+ }
592
+
593
+ // Plugin EC sqs:SendMessage grants on the aggregate / StateChangeSlice
594
+ // command-topic queues that user extensions publish to. The default
595
+ // AllowLambdaSendSQS only covers CorePluginExtPointCmdTopic (added because
596
+ // the auto-included Connect extension publishes there), so without this
597
+ // grant every cross-plugin extension's first command publish fails with
598
+ // IAM AccessDenied (e.g. Catalog's Orders_Extension → RecordProductDemand).
599
+ //
600
+ // We deliberately walk context.extensions[].aggregateNames rather than
601
+ // Dict.valuesToArray over context.publishToAggregates: the dict is
602
+ // declared as dict<Output<string>> but Plugin_Builder.res:322 also writes
603
+ // Output<publishJsons> (function values) under DCB slice names — the type
604
+ // is a polite lie, and the function-valued entries make a blanket .apply
605
+ // over the values blow up at preview with "queueUrlOutput.apply is not a
606
+ // function". Iterating extensions limits us to the names extensions
607
+ // actually target, which are always backed by real URL Outputs in the
608
+ // dict (mergedAggregateUrls / aggregateQueueUrls — Plugin_Builder.res:629).
609
+ let aggregateNameSet = Dict.make()
610
+ context.extensions->Array.forEach(ext =>
611
+ ext.aggregateNames->Array.forEach(aggName =>
612
+ aggregateNameSet->Dict.set(aggName, ())
613
+ )
614
+ )
615
+ let queueUrlOutputs =
616
+ aggregateNameSet
617
+ ->Dict.keysToArray
618
+ ->Array.filterMap(aggName => context.publishToAggregates->Dict.get(aggName))
619
+ let _ = if queueUrlOutputs->Array.length > 0 {
620
+ // The dict is declared as `dict<Pulumi.Output.t<string>>` but the
621
+ // DCB slice path (Dcb_Builder.dcbCommandTopicQueueUrl) can leak the
622
+ // `{BS_PRIVATE_NESTED_SOME_NONE: 0}` nested-option sentinel as a
623
+ // value when the SQS Queue's .id is still unresolved during early
624
+ // CustomResource construction. Pulumi.all happily wraps & resolves
625
+ // it as Output<sentinel>, so .apply hands us a non-string. Drop
626
+ // anything that isn't actually a string — an extension targeting
627
+ // the affected slice will need a separate grant out-of-band until
628
+ // the resource is fully provisioned and a subsequent preview/up
629
+ // produces a real ARN. We build the policy document inline so an
630
+ // empty filtered ARN array skips RolePolicy creation entirely
631
+ // (PutRolePolicy rejects empty Resource arrays with
632
+ // MalformedPolicyDocument).
633
+ let policyJsonOutput =
634
+ queueUrlOutputs
635
+ ->Pulumi.Output.all
636
+ ->Pulumi.Output.apply(urls => {
637
+ let queueArns =
638
+ urls
639
+ ->Array.filter(url => typeof(url) === #string)
640
+ ->Array.map(url =>
641
+ // queue URL → ARN: https://sqs.<region>.amazonaws.com/<acct>/<name>
642
+ // → arn:aws:sqs:<region>:<acct>:<name>
643
+ switch url->String.split("/") {
644
+ | [_, _, host, acct, name] =>
645
+ let region =
646
+ host->String.split(".")->Array.get(1)->Option.getOr("eu-west-1")
647
+ `arn:aws:sqs:${region}:${acct}:${name}`
648
+ | _ => url
649
+ }
650
+ )
651
+ if queueArns->Array.length == 0 {
652
+ None
653
+ } else {
654
+ Some(
655
+ PulumiAws.PolicyDocument.make(
656
+ ~id=`${name}PublishToAggregatesPolicy`,
657
+ ~statements=[
658
+ {
659
+ sid: "AllowEcPublishToAggregateCmdTopics",
660
+ effect: Allow,
661
+ actions: Action("sqs:SendMessage"),
662
+ resources: Resources(queueArns),
663
+ },
664
+ ],
665
+ )->PulumiAws.PolicyDocument.toJsonString,
666
+ )
667
+ }
668
+ })
669
+ // Pulumi.all wraps a Some(string)/None and resolves to either the
670
+ // policy JSON or undefined — the latter makes PutRolePolicy a no-op
671
+ // by failing fast in the provider, so guard RolePolicy creation on
672
+ // the resolved value being Some.
673
+ let _ =
674
+ policyJsonOutput->Pulumi.Output.apply(policyOpt =>
675
+ switch policyOpt {
676
+ | Some(policyJson) =>
677
+ let _ = PulumiAws.IAM.RolePolicy.make(
678
+ ~name=`${name}-publishToAggregates`,
679
+ ~args={
680
+ policy: policyJson->Pulumi.Input.make,
681
+ role: runtime.parts.lambdaRole.id->Pulumi.Output.asInput,
682
+ },
683
+ )
684
+ | None => ()
685
+ }
686
+ )
687
+ }
688
+ }
689
+
690
+ let forPluginHeartbeat: ReventlessCore.Runtime.forComponent<
691
+ ReventlessCore.Runtime.eventHandler<unit, context, unit>,
692
+ runtimeParts,
693
+ ReventlessCore.Heartbeat.component,
694
+ > = (
695
+ ~handler as _,
696
+ ~connect,
697
+ ~memorySize=1024,
698
+ ~timeout=30,
699
+ heartbeat,
700
+ ) => {
701
+ let hbConfig = heartbeatConfigRef.contents
702
+ switch hbConfig.epQueueUrl {
703
+ | None =>
704
+ log.warn(~comp="PluginRuntime_Builder", "forPluginHeartbeat skipped (no EP queue URL)")
705
+ | Some(epQueueUrl) =>
706
+ let resource = heartbeat->ReventlessCore.Component.toPulumiResource
707
+ let name = resource.name->ReventlessCore.ComponentType.nameOpt(
708
+ ReventlessCore.Heartbeat.componentType,
709
+ )
710
+ let opts = {Pulumi.ComponentResource.parent: resource}
711
+
712
+ let envVars: dict<Pulumi.Input.t<string>> = Dict.make()
713
+ envVars->Dict.set("EP_QUEUE_URL", epQueueUrl->Pulumi.Output.asInput)
714
+ envVars->Dict.set(
715
+ "PLUGIN_ID",
716
+ hbConfig.pluginId->Pulumi.Output.make->Pulumi.Output.asInput,
717
+ )
718
+ envVars->Dict.set(
719
+ "HEARTBEAT_TIMEOUT",
720
+ hbConfig.heartbeatTimeout->Int.toString->Pulumi.Output.make->Pulumi.Output.asInput,
721
+ )
722
+
723
+ // Static re-export from Layer entry point — no esbuild, no user packages
724
+ let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
725
+ ~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/HeartbeatEntryPoint.mjs",
726
+ ~packageDirs=Dict.make(),
727
+ )
728
+
729
+ let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
730
+ ~name,
731
+ ~code,
732
+ ~sourceCodeHash,
733
+ ~envVars,
734
+ ~memorySize,
735
+ ~timeout,
736
+ ~opts,
737
+ )
738
+ connect(~runtime)
739
+ }
740
+ }
741
+
742
+ // Thin delegator: supplies the registered slice paths + per-flavor config to
743
+ // the build in StateChangeSliceRuntime_Builder_Single (kept as a named file so
744
+ // it sits alongside StateViewSliceRuntime_Builder_Single, rather than buried
745
+ // here). Merges slice module paths: auto-registered (from moduleUrl) + manually
746
+ // registered (from registerDcbConfig).
747
+ let forDcbCommandTopic: ReventlessCore.Runtime.forComponent<
748
+ ReventlessCore.Runtime.effectHandler<'callbackEvent, context, unit, string>,
749
+ runtimeParts,
750
+ ReventlessCore.CommandTopic.component<'op>,
751
+ > = (~handler as _, ~connect, ~memorySize as _=1024, ~timeout as _=30, dcbCommandTopic) => {
752
+ let dcbConfig = dcbConfigRef.contents
753
+ let slicePaths =
754
+ registeredSliceModulePaths
755
+ ->Array.concat(dcbConfig.stateChangeSliceModulePaths)
756
+ ->Array.map(({specPath, behaviorPath}) => (specPath, behaviorPath))
757
+ StateChangeSliceRuntime_Builder_Single.forDcbCommandTopic(
758
+ ~slicePaths,
759
+ ~dcbTableName=dcbConfig.dcbTableName,
760
+ ~pluginName=dcbConfig.pluginName,
761
+ ~syncConfig=syncStateChangesConfigRef.contents,
762
+ ~asyncConfig=asyncStateChangesConfigRef.contents,
763
+ ~connect,
764
+ dcbCommandTopic,
765
+ )
766
+ }
767
+
768
+ let finish = () => ()
769
+ }