@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,41 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Aws from "@pulumi/aws";
4
+ import * as IAM$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/IAM.res.mjs";
5
+ import * as Pulumi from "@pulumi/pulumi";
6
+ import * as AWS$ReventlessAws from "../AWS.res.mjs";
7
+ import * as Adapter$ReventlessInfra from "@reventlessdev/reventless-infra/src/adapter/Adapter.res.mjs";
8
+ import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/PolicyDocument.res.mjs";
9
+ import * as ScheduledPublisher_CloudWatchEvents_Runtime$ReventlessAws from "./ScheduledPublisher_CloudWatchEvents_Runtime.res.mjs";
10
+
11
+ function make(name, opts) {
12
+ let role = IAM$PulumiAws.Role.makeWithDefaultPolicy("CloudWatchEventsRole", Pulumi.output(AWS$ReventlessAws.CloudwatchEventRule.principal), opts);
13
+ new (Aws.iam.Policy)(name + "CloudWatchEventsPolicy", {
14
+ policy: role.arn.apply(roleArn => PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, name + "CloudWatchEventsPolicy", [
15
+ {
16
+ Sid: "AllowCloudWatchEvents",
17
+ Effect: "Allow",
18
+ Action: "events:*",
19
+ Resource: "*"
20
+ },
21
+ {
22
+ Sid: "AllowPassRole",
23
+ Effect: "Allow",
24
+ Action: "iam:PassRole",
25
+ Resource: roleArn
26
+ }
27
+ ])))
28
+ }, opts);
29
+ return {
30
+ resource: Adapter$ReventlessInfra.make(role.name, role.id, role.arn, Pulumi.output("CloudWatchEvents"), undefined, undefined, undefined, Pulumi.output("aws:cloudwatch:EventRule"), undefined, undefined),
31
+ operations: role.arn.apply(roleArn => ({
32
+ createSchedule: ScheduledPublisher_CloudWatchEvents_Runtime$ReventlessAws.createSchedule(roleArn),
33
+ deleteSchedule: ScheduledPublisher_CloudWatchEvents_Runtime$ReventlessAws.deleteSchedule
34
+ }))
35
+ };
36
+ }
37
+
38
+ export {
39
+ make,
40
+ }
41
+ /* @pulumi/aws Not a pure module */
@@ -0,0 +1,97 @@
1
+ open Reventless.Schedule
2
+ open AwsSdk.CloudWatchEvents
3
+
4
+ let plural = count => count == 1 ? "" : "s"
5
+
6
+ let toScheduleExpression = x =>
7
+ switch x {
8
+ | Single(year, month, day, hour, minute) =>
9
+ `cron(${minute->Int.toString} ${hour->Int.toString} ${day->Int.toString} ${month->Int.toString} ? ${year->Int.toString})`
10
+ | Minutes(minutes) =>
11
+ let plural = minutes->plural
12
+ `rate(${minutes->Int.toString} minute${plural})`
13
+ | Hours(hours) =>
14
+ let plural = hours->plural
15
+ `rate(${hours->Int.toString} hour${plural})`
16
+ | Days(days) =>
17
+ let plural = days->plural
18
+ `rate(${days->Int.toString} day${plural})`
19
+ | Daily(hour, minute) => `cron(${minute->Int.toString} ${hour->Int.toString} * * * *)`
20
+ | Weekdays(hour, minute) => `cron(${minute->Int.toString} ${hour->Int.toString} ? * MON-FRI *)`
21
+ | WeekdaysAndSaturday(hour, minute) =>
22
+ `cron(${minute->Int.toString} ${hour->Int.toString} ? * MON-SAT *)`
23
+ }
24
+
25
+ let createSchedule = (~roleArn: string): ReventlessCore.Scheduler.createSchedule =>
26
+ (queueResources, schedule) =>
27
+ switch queueResources {
28
+ | [] =>
29
+ let err = "createSchedule not possible: no Queue configured !"
30
+ ReventlessCore.EffectLogger.logError(~comp=__MODULE__, err)
31
+ ->Effect.flatMap(_ =>
32
+ Effect.sync(() => JsError.throwWithMessage(err))
33
+ )
34
+ ->Effect.runPromise
35
+ | resources =>
36
+ let resource = resources->Array.getUnsafe(0) // FIXME
37
+ Effect.promise(() =>
38
+ PutRuleCommand.send(
39
+ PutRuleCommand.make({
40
+ name: schedule.name,
41
+ scheduleExpression: schedule.rate->toScheduleExpression,
42
+ roleArn,
43
+ state: "ENABLED",
44
+ }),
45
+ )
46
+ )
47
+ ->Effect.flatMap(_ =>
48
+ Effect.promise(() =>
49
+ PutTargetsCommand.send(
50
+ PutTargetsCommand.make({
51
+ rule: schedule.name,
52
+ targets: [
53
+ {
54
+ arn: resource.urn,
55
+ id: resource.name,
56
+ input: schedule.payload,
57
+ },
58
+ ],
59
+ }),
60
+ )
61
+ )
62
+ )
63
+ ->Effect.map(_ => ())
64
+ ->Effect.runPromise
65
+ }
66
+
67
+ let deleteSchedule: ReventlessCore.Scheduler.deleteSchedule = (queueResources, name) =>
68
+ switch queueResources {
69
+ | [] =>
70
+ let err = "deleteSchedule not possible: no Queue configured !"
71
+ ReventlessCore.EffectLogger.logError(~comp=__MODULE__, err)
72
+ ->Effect.flatMap(_ =>
73
+ Effect.sync(() => JsError.throwWithMessage(err))
74
+ )
75
+ ->Effect.runPromise
76
+ | resources =>
77
+ let resource = resources->Array.getUnsafe(0) // FIXME
78
+ Effect.promise(() =>
79
+ RemoveTargetsCommand.send(
80
+ RemoveTargetsCommand.make({
81
+ rule: name,
82
+ ids: [resource.name],
83
+ }),
84
+ )
85
+ )
86
+ ->Effect.flatMap(_ =>
87
+ Effect.promise(() =>
88
+ DeleteRuleCommand.send(
89
+ DeleteRuleCommand.make({
90
+ name: name,
91
+ }),
92
+ )
93
+ )
94
+ )
95
+ ->Effect.map(_ => ())
96
+ ->Effect.runPromise
97
+ }
@@ -0,0 +1,85 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Effect from "effect/Effect";
4
+ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
5
+ import * as CloudWatchEvents$AwsSdk from "@reventlessdev/rescript-aws-sdk/src/CloudWatchEvents.res.mjs";
6
+ import * as EffectLogger$ReventlessCore from "@reventlessdev/reventless-core/src/util/EffectLogger.res.mjs";
7
+ import * as ClientCloudwatchEvents from "@aws-sdk/client-cloudwatch-events";
8
+
9
+ function plural(count) {
10
+ if (count === 1) {
11
+ return "";
12
+ } else {
13
+ return "s";
14
+ }
15
+ }
16
+
17
+ function toScheduleExpression(x) {
18
+ switch (x.TAG) {
19
+ case "Single" :
20
+ return `cron(` + x._4.toString() + ` ` + x._3.toString() + ` ` + x._2.toString() + ` ` + x._1.toString() + ` ? ` + x._0.toString() + `)`;
21
+ case "Minutes" :
22
+ let minutes = x._0;
23
+ let plural$1 = plural(minutes);
24
+ return `rate(` + minutes.toString() + ` minute` + plural$1 + `)`;
25
+ case "Hours" :
26
+ let hours = x._0;
27
+ let plural$2 = plural(hours);
28
+ return `rate(` + hours.toString() + ` hour` + plural$2 + `)`;
29
+ case "Days" :
30
+ let days = x._0;
31
+ let plural$3 = plural(days);
32
+ return `rate(` + days.toString() + ` day` + plural$3 + `)`;
33
+ case "Daily" :
34
+ return `cron(` + x._1.toString() + ` ` + x._0.toString() + ` * * * *)`;
35
+ case "Weekdays" :
36
+ return `cron(` + x._1.toString() + ` ` + x._0.toString() + ` ? * MON-FRI *)`;
37
+ case "WeekdaysAndSaturday" :
38
+ return `cron(` + x._1.toString() + ` ` + x._0.toString() + ` ? * MON-SAT *)`;
39
+ }
40
+ }
41
+
42
+ function createSchedule(roleArn) {
43
+ return (queueResources, schedule) => {
44
+ if (queueResources.length !== 0) {
45
+ let resource = queueResources[0];
46
+ return Effect.runPromise(Effect.map(Effect.flatMap(Effect.promise(() => CloudWatchEvents$AwsSdk.PutRuleCommand.send(new ClientCloudwatchEvents.PutRuleCommand({
47
+ Name: schedule.name,
48
+ ScheduleExpression: toScheduleExpression(schedule.rate),
49
+ RoleArn: roleArn,
50
+ State: "ENABLED"
51
+ }))), param => Effect.promise(() => CloudWatchEvents$AwsSdk.PutTargetsCommand.send(new ClientCloudwatchEvents.PutTargetsCommand({
52
+ Rule: schedule.name,
53
+ Targets: [{
54
+ Arn: resource.urn,
55
+ Id: resource.name,
56
+ Input: schedule.payload
57
+ }]
58
+ })))), param => {}));
59
+ }
60
+ let err = "createSchedule not possible: no Queue configured !";
61
+ return Effect.runPromise(Effect.flatMap(EffectLogger$ReventlessCore.logError("ScheduledPublisher_CloudWatchEvents_Runtime-ReventlessAws", undefined, err), () => Effect.sync(() => Stdlib_JsError.throwWithMessage(err))));
62
+ };
63
+ }
64
+
65
+ function deleteSchedule(queueResources, name) {
66
+ if (queueResources.length !== 0) {
67
+ let resource = queueResources[0];
68
+ return Effect.runPromise(Effect.map(Effect.flatMap(Effect.promise(() => CloudWatchEvents$AwsSdk.RemoveTargetsCommand.send(new ClientCloudwatchEvents.RemoveTargetsCommand({
69
+ Rule: name,
70
+ Ids: [resource.name]
71
+ }))), param => Effect.promise(() => CloudWatchEvents$AwsSdk.DeleteRuleCommand.send(new ClientCloudwatchEvents.DeleteRuleCommand({
72
+ Name: name
73
+ })))), param => {}));
74
+ }
75
+ let err = "deleteSchedule not possible: no Queue configured !";
76
+ return Effect.runPromise(Effect.flatMap(EffectLogger$ReventlessCore.logError("ScheduledPublisher_CloudWatchEvents_Runtime-ReventlessAws", undefined, err), () => Effect.sync(() => Stdlib_JsError.throwWithMessage(err))));
77
+ }
78
+
79
+ export {
80
+ plural,
81
+ toScheduleExpression,
82
+ createSchedule,
83
+ deleteSchedule,
84
+ }
85
+ /* effect/Effect Not a pure module */
@@ -0,0 +1,418 @@
1
+ // StateTopic_AppSync.res
2
+ // Source B: state-change subscriptions (QueryDb DynamoDB Stream → AppSync Events API).
3
+ //
4
+ // One shared Lambda per events API handles every stream-enabled QueryDb in the
5
+ // platform (admin RMs, user-plugin `ReadModelStream`s, and `StateViewSliceStream`s
6
+ // alike). `make` registers an entry in a per-events-API registry; `finish` is
7
+ // called once at the end of platform construction and builds the shared
8
+ // Lambda + IAM role/policy + per-stream EventSourceMappings from the
9
+ // accumulated registry entries.
10
+ //
11
+ // Channel layout: /default/{topicName}/{pathSegment(entityKey)}
12
+ // topicName = plugin-prefixed query LIST field name (e.g. "Catalog-Products").
13
+ // MUST match the AutoUI manifest's queryableDef.queryField,
14
+ // which the host-shell's AutoLive subscribes on. Using the
15
+ // singular return type here would orphan the channel.
16
+ // entityKey = the table's primary-key value(s) extracted from record.Keys.
17
+ // Single-key tables: just the `id` attribute value.
18
+ // Composite-key tables: `{id}-{subIdValue}` where subId is the
19
+ // table's sort-key attribute.
20
+ // pathSegment = AppSync Events channel segments allow only [A-Za-z0-9-];
21
+ // every other char (`@`, `.`, `:`, `_`, `/`, …) collapses to
22
+ // `-`. The DESCRIPTOR BODY carries the ORIGINAL entityKey so
23
+ // the UI's inPage / selectedRowId comparisons against the
24
+ // GraphQL row.id still match — only the URL path is sanitised.
25
+ //
26
+ // Clients subscribe to one of:
27
+ // {topic}/{entityKey} detail view (one row)
28
+ // {topic}/* list view (all rows in the read model)
29
+ //
30
+ // Prerequisites:
31
+ // - The QueryDb must use QueryDbStorage_DynamoDbStream so its resource has a
32
+ // streamArn (StreamSource resourceInfo).
33
+ //
34
+ // Usage in the plugin / admin builder (after allQueryDbs are assembled):
35
+ // StateTopic_AppSync.make(
36
+ // ~readModelName="Products", // QueryDb key = ReadModel Spec.name
37
+ // ~topicName="Catalog_Products", // AppSync Events channel root = list field name
38
+ // ~allQueryDbs,
39
+ // ~eventsApi,
40
+ // ~opts,
41
+ // )
42
+ //
43
+ // Usage in Platform.res (once, after admin + plugins have wired):
44
+ // StateTopic_AppSync.finish(~eventsApi, ~opts)
45
+
46
+ open PulumiAws
47
+
48
+ // ── Registry ──────────────────────────────────────────────────────────────────
49
+
50
+ type streamEntry = {
51
+ tableName: Pulumi.Output.t<string>,
52
+ streamArn: Pulumi.Output.t<string>,
53
+ topicName: string,
54
+ }
55
+
56
+ // One registry per events API, keyed by `eventsApi.name` (the static Pulumi
57
+ // resource name, e.g. "DomainEventsApi"). `make` appends; `finish` drains.
58
+ let registry: dict<array<streamEntry>> = Dict.make()
59
+
60
+ // ── Handler code ─────────────────────────────────────────────────────────────
61
+ //
62
+ // Processes DynamoDB Stream records → publishes one event per row-change to
63
+ // AppSync Events. Channel path: /default/{topicRoot}/{entityKey}.
64
+ //
65
+ // Topic routing is per-record: `STATE_TOPIC_MAP` is `{ "<ddbTableName>":
66
+ // "<topicName>" }`; the handler extracts the table name from
67
+ // `record.eventSourceARN` (`…:table/<TableName>/stream/…`) and looks up the
68
+ // matching topic. This is what lets a single shared Lambda fan out to every
69
+ // stream-enabled RM in the platform.
70
+ //
71
+ // AppSync Events channel segments allow only [A-Za-z0-9-]. `topicRoot` and the
72
+ // channel-path entityKey both run through `pathSegment` (collapses every other
73
+ // char to `-`); the descriptor body's `id` keeps the ORIGINAL value so the UI
74
+ // can match it against GraphQL row ids.
75
+ //
76
+ // Payload is a change descriptor (NOT the full row):
77
+ // { changeKind: "Added" | "Updated" | "Removed",
78
+ // id: <entityKey>,
79
+ // sortKeyValue?: <updatedAt | createdAt if present in the image> }
80
+ //
81
+ // Uses native Node.js crypto + fetch with SigV4 — no extra SDK packages needed.
82
+ // Credentials come from the Lambda execution role via the process.env AWS_* variables.
83
+
84
+ let handlerCode: string = `
85
+ import { createHmac, createHash } from "node:crypto";
86
+ import { unmarshall } from "@aws-sdk/util-dynamodb";
87
+
88
+ const TOPIC_MAP = JSON.parse(process.env.STATE_TOPIC_MAP || "{}");
89
+ const APPSYNC_ENDPOINT = process.env.APPSYNC_ENDPOINT; // "https://{eventsApi.dns.http}"
90
+ const AWS_REGION = process.env.AWS_REGION ?? "eu-west-1";
91
+
92
+ function sha256hex(data) {
93
+ return createHash("sha256").update(typeof data === "string" ? data : JSON.stringify(data)).digest("hex");
94
+ }
95
+ function hmacBuf(key, data) {
96
+ return createHmac("sha256", key).update(data).digest();
97
+ }
98
+
99
+ async function signedHeaders(host, path, body) {
100
+ const accessKeyId = process.env.AWS_ACCESS_KEY_ID;
101
+ const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
102
+ const sessionToken = process.env.AWS_SESSION_TOKEN;
103
+ const now = new Date();
104
+ const amzDate = now.toISOString().replace(/[:\\-]|\\..../g, "").slice(0, 15) + "Z";
105
+ const dateStamp = now.toISOString().slice(0, 10).replace(/-/g, "");
106
+ const headers = { host, "x-amz-date": amzDate, ...(sessionToken ? { "x-amz-security-token": sessionToken } : {}) };
107
+ const canonH = Object.entries(headers).sort(([a],[b]) => a.localeCompare(b)).map(([k,v]) => \`\${k}:\${v}\\n\`).join("");
108
+ const signH = Object.keys(headers).sort().join(";");
109
+ const cr = ["POST", path, "", canonH, signH, sha256hex(body)].join("\\n");
110
+ const scope = \`\${dateStamp}/\${AWS_REGION}/appsync/aws4_request\`;
111
+ const sts = ["AWS4-HMAC-SHA256", amzDate, scope, sha256hex(cr)].join("\\n");
112
+ const kDate = hmacBuf("AWS4" + secretAccessKey, dateStamp);
113
+ const kSigning = hmacBuf(hmacBuf(hmacBuf(kDate, AWS_REGION), "appsync"), "aws4_request");
114
+ const sig = createHmac("sha256", kSigning).update(sts).digest("hex");
115
+ return { ...headers, Authorization: \`AWS4-HMAC-SHA256 Credential=\${accessKeyId}/\${scope}, SignedHeaders=\${signH}, Signature=\${sig}\` };
116
+ }
117
+
118
+ // AppSync Events channel segments allow only [A-Za-z0-9-]. Anything else
119
+ // (\`@\`, \`.\`, \`:\`, \`_\`, \`/\`, …) collapses to \`-\` so the segment is a
120
+ // legal channel-path token. The UI's AutoLive.normalizeSegment uses the same
121
+ // rule for Entity-scoped subscribes.
122
+ function pathSegment(value) {
123
+ return String(value).replace(/[^A-Za-z0-9-]/g, "-");
124
+ }
125
+
126
+ // Map record.eventSourceARN → topicRoot via STATE_TOPIC_MAP.
127
+ // Stream ARNs look like: arn:aws:dynamodb:<region>:<acct>:table/<TableName>/stream/<ts>
128
+ function topicRootFromEventSourceArn(arn) {
129
+ const m = arn && arn.match(/:table\\/([^/]+)\\/stream\\//);
130
+ if (!m) return undefined;
131
+ const topicName = TOPIC_MAP[m[1]];
132
+ if (topicName === undefined) return undefined;
133
+ return pathSegment(topicName);
134
+ }
135
+
136
+ // Build entityKey from record.dynamodb.Keys. Framework convention:
137
+ // partition-key attr is named "id"; composite tables have one extra attr (the
138
+ // sort key) whose name comes from the projection's subIdConfig.subIdField.
139
+ // Returns the ORIGINAL key string — the descriptor body uses this verbatim so
140
+ // AutoListView can match it against the GraphQL row.id. The channel path
141
+ // segment is derived later via pathSegment().
142
+ function entityKeyFromRecord(record) {
143
+ const keys = unmarshall(record.dynamodb.Keys ?? {});
144
+ const id = keys.id;
145
+ if (id === undefined) {
146
+ // Defensive: framework always names the partition key "id". If a future
147
+ // table breaks the convention, fall back to a stable sort-join.
148
+ const names = Object.keys(keys).sort();
149
+ return names.map((n) => String(keys[n])).join("-");
150
+ }
151
+ const subIdName = Object.keys(keys).find((k) => k !== "id");
152
+ return subIdName === undefined
153
+ ? String(id)
154
+ : String(id) + "-" + String(keys[subIdName]);
155
+ }
156
+
157
+ // Map DDB stream eventName → descriptor changeKind.
158
+ function changeKindFor(eventName) {
159
+ switch (eventName) {
160
+ case "INSERT": return "Added";
161
+ case "MODIFY": return "Updated";
162
+ case "REMOVE": return "Removed";
163
+ default: return "Updated"; // defensive default — shouldn't fire
164
+ }
165
+ }
166
+
167
+ // Pick the "natural" sort timestamp from an unmarshalled image. Conventions
168
+ // in this codebase prefer updatedAt; createdAt is the fallback for views that
169
+ // never mutate after insert. Returns undefined if neither is present.
170
+ function pickSortKeyValue(image) {
171
+ if (image && typeof image.updatedAt === "string") return image.updatedAt;
172
+ if (image && typeof image.createdAt === "string") return image.createdAt;
173
+ return undefined;
174
+ }
175
+
176
+ export async function handler(event) {
177
+ const url = new URL(APPSYNC_ENDPOINT);
178
+ let transientErr;
179
+ for (const record of event.Records) {
180
+ const topicRoot = topicRootFromEventSourceArn(record.eventSourceARN);
181
+ if (topicRoot === undefined) {
182
+ console.warn("StateTopic: unknown table for ARN", record.eventSourceARN);
183
+ continue;
184
+ }
185
+ const channelRoot = "/default/" + topicRoot;
186
+ const image =
187
+ record.eventName === "REMOVE"
188
+ ? record.dynamodb.OldImage
189
+ : record.dynamodb.NewImage;
190
+ if (image === undefined) continue;
191
+ const entityKey = entityKeyFromRecord(record);
192
+ const channel = channelRoot + "/" + pathSegment(entityKey);
193
+ const unmarshalled = unmarshall(image);
194
+ const descriptor = {
195
+ changeKind: changeKindFor(record.eventName),
196
+ id: entityKey,
197
+ };
198
+ const sortKeyValue = pickSortKeyValue(unmarshalled);
199
+ if (sortKeyValue !== undefined) descriptor.sortKeyValue = sortKeyValue;
200
+ const body = JSON.stringify({ id: record.eventID, channel, events: [JSON.stringify(descriptor)] });
201
+ const auth = await signedHeaders(url.hostname, "/event", body);
202
+ try {
203
+ const res = await fetch(APPSYNC_ENDPOINT + "/event", {
204
+ method: "POST",
205
+ headers: { accept: "application/json, text/javascript", "content-encoding": "amz-1.0", "content-type": "application/json; charset=UTF-8", ...auth },
206
+ body,
207
+ });
208
+ if (!res.ok) {
209
+ const txt = await res.text();
210
+ // 4xx: permanent (bad request, auth, channel format) — retrying won't
211
+ // help. Log structurally so a CloudWatch metric filter on
212
+ // "STATE_TOPIC_PUBLISH_FAILED type=permanent" surfaces it; continue
213
+ // processing so one bad record doesn't block the rest of the batch.
214
+ // 5xx / network: transient — record it and throw at the end so the
215
+ // ESM retries (bounded by maximumRetryAttempts + bisectBatch).
216
+ const isTransient = res.status >= 500;
217
+ const tag = isTransient ? "transient" : "permanent";
218
+ console.error(
219
+ "STATE_TOPIC_PUBLISH_FAILED type=" + tag +
220
+ " status=" + res.status +
221
+ " channel=" + channel +
222
+ " body=" + txt.slice(0, 500)
223
+ );
224
+ if (isTransient) transientErr = new Error("StateTopic publish failed: " + res.status);
225
+ }
226
+ } catch (e) {
227
+ // Network-level error (DNS, connection refused, timeout) — always transient.
228
+ console.error(
229
+ "STATE_TOPIC_PUBLISH_FAILED type=transient status=network" +
230
+ " channel=" + channel +
231
+ " err=" + (e && e.message)
232
+ );
233
+ transientErr = e;
234
+ }
235
+ }
236
+ // Throw after the loop so a single transient error doesn't stop us from
237
+ // emitting other records' descriptors first. The ESM will retry the batch;
238
+ // bisectBatchOnFunctionError isolates the bad record across retries.
239
+ if (transientErr) throw transientErr;
240
+ }
241
+ `
242
+
243
+ // ── Registration ──────────────────────────────────────────────────────────────
244
+
245
+ let make = (
246
+ ~readModelName: string,
247
+ ~topicName: string,
248
+ ~allQueryDbs: ReventlessCore.QueryDb.allOutputs,
249
+ ~eventsApi: AppSync_EventsApi.t,
250
+ ~opts as _: Pulumi.CustomResourceOptions.t,
251
+ ) => {
252
+ // Look up the QueryDb resources by ReadModel Spec.name, then find the
253
+ // DynamoDB stream resource within them. Requires QueryDbStorage_DynamoDbStream.
254
+ let streamResource =
255
+ allQueryDbs
256
+ ->ReventlessCore.Util.ReadModel.queryDbStorageResources(readModelName)
257
+ ->Util_DynamoDbStream.findResource
258
+
259
+ let streamArn = Util_DynamoDbStream.streamArnFromDynamoDbTableResource(streamResource)
260
+ let tableName = streamResource.name
261
+
262
+ let key = eventsApi.name
263
+ let entries = registry->Dict.get(key)->Option.getOr([])
264
+ registry->Dict.set(key, entries->Array.concat([{tableName, streamArn, topicName}]))
265
+ }
266
+
267
+ // ── Finalize: build the shared Lambda + IAM + ESMs ────────────────────────────
268
+
269
+ let finish = (
270
+ ~eventsApi: AppSync_EventsApi.t,
271
+ ~opts: Pulumi.CustomResourceOptions.t,
272
+ ) => {
273
+ let key = eventsApi.name
274
+ switch registry->Dict.get(key) {
275
+ | None => ()
276
+ | Some([]) => ()
277
+ | Some(entries) =>
278
+ let name = eventsApi.name
279
+
280
+ // IAM role for the shared Lambda (Lambda service principal).
281
+ let lambdaRole = IAM.Role.makeWithDefaultPolicy(
282
+ ~name=name ++ "StateTopicRole",
283
+ ~servicePrincipal=AWS.Lambda.principal->Pulumi.Output.make,
284
+ ~opts,
285
+ )
286
+
287
+ // IAM policy: read from every stream + publish to the events API.
288
+ let streamArnsOutput =
289
+ entries->Array.map(e => e.streamArn)->Pulumi.Output.all
290
+ let _ =
291
+ (streamArnsOutput, eventsApi.api.apiArn)
292
+ ->Pulumi.Output.all2
293
+ ->Pulumi.Output.apply(((streamArns, apiArn)) => {
294
+ open PolicyDocument
295
+ let _rolePolicy = IAM.RolePolicy.make(
296
+ ~name=name ++ "StateTopicPolicy",
297
+ ~args={
298
+ IAM.RolePolicy.policy: PolicyDocument.make(
299
+ ~id=name ++ "StateTopicPolicy",
300
+ ~statements=[
301
+ {
302
+ sid: "AllowLambdaLogging",
303
+ effect: Allow,
304
+ actions: Action("logs:*"),
305
+ resources: Resource("arn:aws:logs:*:*:*"),
306
+ },
307
+ {
308
+ sid: "AllowReadDynamoDbStream",
309
+ effect: Allow,
310
+ actions: Actions([
311
+ "dynamodb:DescribeStream",
312
+ "dynamodb:GetRecords",
313
+ "dynamodb:GetShardIterator",
314
+ "dynamodb:ListStreams",
315
+ ]),
316
+ resources: Resources(streamArns),
317
+ },
318
+ {
319
+ sid: "AllowPublishAppSyncEvents",
320
+ effect: Allow,
321
+ actions: Action("appsync:EventPublish"),
322
+ resources: Resource(apiArn ++ "/*"),
323
+ },
324
+ ],
325
+ )
326
+ ->PolicyDocument.toJsonString
327
+ ->Pulumi.Input.make,
328
+ role: lambdaRole.id->Pulumi.Output.asInput,
329
+ },
330
+ ~opts,
331
+ )
332
+ })
333
+
334
+ // STATE_TOPIC_MAP env var — `{ <tableName>: <topicName> }` JSON object.
335
+ // `topicName` is already a resolved string at deploy time; `tableName`
336
+ // is an Output, so we await all of them and build the JSON inside apply.
337
+ let topicMapJson =
338
+ entries
339
+ ->Array.map(e => e.tableName)
340
+ ->Pulumi.Output.all
341
+ ->Pulumi.Output.apply(tableNames => {
342
+ let dict = Dict.make()
343
+ tableNames->Array.forEachWithIndex((tableName, i) => {
344
+ let topicName = (entries->Array.getUnsafe(i)).topicName
345
+ dict->Dict.set(tableName, topicName->JSON.Encode.string)
346
+ })
347
+ dict->JSON.Encode.object->JSON.stringify
348
+ })
349
+
350
+ // Shared Lambda — handler code is identical for every stream-enabled RM;
351
+ // routing is per-record via STATE_TOPIC_MAP env var.
352
+ let archiveContents: dict<Pulumi.Archive.assetOrArchive> = Dict.make()
353
+ archiveContents->Dict.set(
354
+ "index.mjs",
355
+ Pulumi.Asset.stringAsset(handlerCode)->Pulumi.Archive.assetToAssetOrArchive,
356
+ )
357
+ let code = Pulumi.Archive.assetArchive(archiveContents)
358
+ let sourceCodeHash = Util_Bundle.hashString(handlerCode)
359
+
360
+ let layers =
361
+ Lambda.reventlessLayerArn
362
+ ->Option.map(arn => [arn->Pulumi.Input.make])
363
+ ->Option.getOr([])
364
+ ->Pulumi.Input.make
365
+
366
+ let appsyncEndpoint = AppSync_EventsApi.httpEndpoint(eventsApi)
367
+
368
+ let lambda = Lambda.Function.make(
369
+ ~name=name ++ "StateTopicPublisher",
370
+ ~args={
371
+ handler: "index.handler"->Pulumi.Input.make,
372
+ runtime: "nodejs22.x"->Pulumi.Input.make,
373
+ code: code->Pulumi.Input.make,
374
+ sourceCodeHash: sourceCodeHash->Pulumi.Input.make,
375
+ role: lambdaRole.arn->Pulumi.Output.asInput,
376
+ memorySize: 256->Pulumi.Input.make,
377
+ timeout: 30->Pulumi.Input.make,
378
+ layers,
379
+ tags: AWS.Tags.make(~name=name ++ "StateTopic", ReventlessCore.QueryDb.componentType),
380
+ environment: (
381
+ {
382
+ Lambda.Function.variables: Dict.fromArray([
383
+ ("Environment", Pulumi.Pulumi.getStackName()->Pulumi.Input.make),
384
+ ("APPSYNC_ENDPOINT", appsyncEndpoint->Pulumi.Output.asInput),
385
+ ("STATE_TOPIC_MAP", topicMapJson->Pulumi.Output.asInput),
386
+ ]),
387
+ }: Lambda.Function.functionEnvironment
388
+ )->Pulumi.Input.make,
389
+ },
390
+ ~opts,
391
+ )
392
+
393
+ // One EventSourceMapping per stream, all targeting the shared Lambda.
394
+ // Retry posture: the handler throws on 5xx / network errors and logs-and-
395
+ // continues on 4xx. `maximumRetryAttempts: 3` bounds the per-record
396
+ // retries so a permanently broken record (default DDB stream behaviour is
397
+ // -1 → forever, which would wedge the shard); `bisectBatchOnFunctionError`
398
+ // isolates the bad record across those retries instead of redelivering
399
+ // the whole batch each time.
400
+ entries->Array.forEach(entry => {
401
+ let _esm = EventSourceMapping.make(
402
+ ~name=entry.topicName ++ "Stream2" ++ name ++ "StateTopic",
403
+ ~args={
404
+ EventSourceMapping.functionName: lambda.arn->Pulumi.Output.asInput,
405
+ eventSourceArn: entry.streamArn->Pulumi.Output.asInput,
406
+ startingPosition: LATEST,
407
+ maximumRetryAttempts: 3,
408
+ bisectBatchOnFunctionError: true,
409
+ },
410
+ ~opts=Some(opts),
411
+ )
412
+ })
413
+
414
+ // Clear the registry so a second platform construction in the same process
415
+ // (tests, dev hot reload) starts fresh.
416
+ registry->Dict.set(key, [])
417
+ }
418
+ }