@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.
- package/CHANGELOG.md +1564 -0
- package/README.md +62 -0
- package/__mocks__/emptyModule.js +1 -0
- package/all-licenses.csv +565 -0
- package/docker-compose.dynamodb-local.yml +12 -0
- package/jest.setup.cjs +5 -0
- package/licenses-analysis.ods +0 -0
- package/package.json +73 -0
- package/rescript.json +39 -0
- package/reventless-licenses-analysis.xls +0 -0
- package/scripts/run-integration-tests.sh +43 -0
- package/src/Platform.res +1940 -0
- package/src/Platform.res.mjs +2281 -0
- package/src/Platform_Casts.res +106 -0
- package/src/Platform_Casts.res.mjs +13 -0
- package/src/Platform_Stack.res +153 -0
- package/src/Platform_Stack.res.mjs +121 -0
- package/src/Types.res +39 -0
- package/src/Types.res.mjs +21 -0
- package/src/adapter/AWS.res +96 -0
- package/src/adapter/AWS.res.mjs +186 -0
- package/src/adapter/AWS_Tags.res +16 -0
- package/src/adapter/AWS_Tags.res.mjs +48 -0
- package/src/adapter/Adapter_Helpers.res +19 -0
- package/src/adapter/Adapter_Helpers.res.mjs +37 -0
- package/src/adapter/Api/AppSync_EventsApi.res +172 -0
- package/src/adapter/Api/AppSync_EventsApi.res.mjs +96 -0
- package/src/adapter/Api/AppSync_Resolver_Native.res +93 -0
- package/src/adapter/Api/AppSync_Resolver_Native.res.mjs +67 -0
- package/src/adapter/Api/AppSync_Resolver_Retrying.res +653 -0
- package/src/adapter/Api/AppSync_Resolver_Retrying.res.mjs +462 -0
- package/src/adapter/Api/CommandSubscriptionResolvers_AppSync.res +29 -0
- package/src/adapter/Api/CommandSubscriptionResolvers_AppSync.res.mjs +15 -0
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +238 -0
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +179 -0
- package/src/adapter/Api/Platform_UIFragments_Lambda.res +229 -0
- package/src/adapter/Api/Platform_UIFragments_Lambda.res.mjs +245 -0
- package/src/adapter/Auth/Auth_Cognito.res +209 -0
- package/src/adapter/Auth/Auth_Cognito.res.mjs +135 -0
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers.res +2 -0
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers.res.mjs +12 -0
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync.res +251 -0
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync.res.mjs +126 -0
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync_NoOp.res +20 -0
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync_NoOp.res.mjs +23 -0
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync_Runtime.res +1 -0
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync_Runtime.res.mjs +11 -0
- package/src/adapter/CommandGenerator/InboundTranslationResolvers_AppSync.res +95 -0
- package/src/adapter/CommandGenerator/InboundTranslationResolvers_AppSync.res.mjs +50 -0
- package/src/adapter/CommandTopic/CommandTopicChannel.res +6 -0
- package/src/adapter/CommandTopic/CommandTopicChannel.res.mjs +21 -0
- package/src/adapter/CommandTopic/CommandTopicChannel_Helpers.res +196 -0
- package/src/adapter/CommandTopic/CommandTopicChannel_Helpers.res.mjs +145 -0
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res +91 -0
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res.mjs +74 -0
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res +69 -0
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res.mjs +62 -0
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res +60 -0
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res.mjs +56 -0
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res +73 -0
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs +65 -0
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res +88 -0
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res.mjs +76 -0
- package/src/adapter/CommandTopic/CommandTopicRemoteChannel.res +1 -0
- package/src/adapter/CommandTopic/CommandTopicRemoteChannel.res.mjs +9 -0
- package/src/adapter/CommandTopic/CommandTopicRemoteChannel_SQS.res +7 -0
- package/src/adapter/CommandTopic/CommandTopicRemoteChannel_SQS.res.mjs +17 -0
- package/src/adapter/Counter/CounterHandler_DynamoDbStream.res +103 -0
- package/src/adapter/Counter/CounterHandler_DynamoDbStream.res.mjs +68 -0
- package/src/adapter/Counter/CounterHandler_DynamoDbStream_Runtime.res +107 -0
- package/src/adapter/Counter/CounterHandler_DynamoDbStream_Runtime.res.mjs +145 -0
- package/src/adapter/DcbEventLog/DcbEventLogStorage.res +1 -0
- package/src/adapter/DcbEventLog/DcbEventLogStorage.res.mjs +9 -0
- package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb.res +84 -0
- package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb.res.mjs +52 -0
- package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res +1436 -0
- package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res.mjs +1143 -0
- package/src/adapter/EventCollector/EventCollectorChannel.res +6 -0
- package/src/adapter/EventCollector/EventCollectorChannel.res.mjs +21 -0
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res +63 -0
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs +56 -0
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res +20 -0
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res.mjs +32 -0
- package/src/adapter/EventCollector/EventCollectorChannel_Helpers.res +266 -0
- package/src/adapter/EventCollector/EventCollectorChannel_Helpers.res.mjs +166 -0
- package/src/adapter/EventCollector/EventCollectorChannel_SQS.res +81 -0
- package/src/adapter/EventCollector/EventCollectorChannel_SQS.res.mjs +54 -0
- package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res +59 -0
- package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res.mjs +45 -0
- package/src/adapter/EventCollector/EventCollectorChannel_SQS_Runtime.res +91 -0
- package/src/adapter/EventCollector/EventCollectorChannel_SQS_Runtime.res.mjs +89 -0
- package/src/adapter/EventLog/EventLogStorage.res +3 -0
- package/src/adapter/EventLog/EventLogStorage.res.mjs +15 -0
- package/src/adapter/EventLog/EventLogStorage_DynamoDb.res +25 -0
- package/src/adapter/EventLog/EventLogStorage_DynamoDb.res.mjs +34 -0
- package/src/adapter/EventLog/EventLogStorage_DynamoDbStream.res +26 -0
- package/src/adapter/EventLog/EventLogStorage_DynamoDbStream.res.mjs +35 -0
- package/src/adapter/EventLog/EventLogStorage_DynamoDb_Runtime.res +121 -0
- package/src/adapter/EventLog/EventLogStorage_DynamoDb_Runtime.res.mjs +137 -0
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +265 -0
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs +185 -0
- package/src/adapter/EventTopic/EventTopicPublisher.res +6 -0
- package/src/adapter/EventTopic/EventTopicPublisher.res.mjs +21 -0
- package/src/adapter/EventTopic/EventTopicPublisher_DynamoDbStream.res +25 -0
- package/src/adapter/EventTopic/EventTopicPublisher_DynamoDbStream.res.mjs +28 -0
- package/src/adapter/EventTopic/EventTopicPublisher_SNS.res +40 -0
- package/src/adapter/EventTopic/EventTopicPublisher_SNS.res.mjs +39 -0
- package/src/adapter/EventTopic/EventTopicPublisher_SNS_FIFO.res +39 -0
- package/src/adapter/EventTopic/EventTopicPublisher_SNS_FIFO.res.mjs +38 -0
- package/src/adapter/EventTopic/EventTopicPublisher_SNS_Runtime.res +5 -0
- package/src/adapter/EventTopic/EventTopicPublisher_SNS_Runtime.res.mjs +18 -0
- package/src/adapter/Mcp/MCP_Lambda.res +502 -0
- package/src/adapter/Mcp/MCP_Lambda.res.mjs +289 -0
- package/src/adapter/QueryDb/NodeResolver_AppSync.res +71 -0
- package/src/adapter/QueryDb/NodeResolver_AppSync.res.mjs +44 -0
- package/src/adapter/QueryDb/QueryDbResolvers.res +2 -0
- package/src/adapter/QueryDb/QueryDbResolvers.res.mjs +12 -0
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res +434 -0
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res.mjs +243 -0
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync_NoOp.res +21 -0
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync_NoOp.res.mjs +14 -0
- package/src/adapter/QueryDb/QueryDbResolvers_NoOp.res +21 -0
- package/src/adapter/QueryDb/QueryDbResolvers_NoOp.res.mjs +14 -0
- package/src/adapter/QueryDb/QueryDbStorage.res +5 -0
- package/src/adapter/QueryDb/QueryDbStorage.res.mjs +18 -0
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDb.res +115 -0
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDb.res.mjs +100 -0
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDbStream.res +52 -0
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDbStream.res.mjs +38 -0
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDb_Runtime.res +271 -0
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDb_Runtime.res.mjs +306 -0
- package/src/adapter/QueryDb/QueryInterceptor_Lambda.res +20 -0
- package/src/adapter/QueryDb/QueryInterceptor_Lambda.res.mjs +22 -0
- package/src/adapter/QueryEngine/QueryEngine.res +3 -0
- package/src/adapter/QueryEngine/QueryEngine.res.mjs +12 -0
- package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res +184 -0
- package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res.mjs +221 -0
- package/src/adapter/Runtime/AdminEventCollectorEntryPoint.mjs +879 -0
- package/src/adapter/Runtime/AggregateEntryPoint.mjs +313 -0
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +327 -0
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res.mjs +239 -0
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res +331 -0
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res.mjs +240 -0
- package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res +247 -0
- package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res.mjs +185 -0
- package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate_Async.res +244 -0
- package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate_Async.res.mjs +185 -0
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +347 -0
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +244 -0
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +285 -0
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs +212 -0
- package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +124 -0
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +187 -0
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +147 -0
- package/src/adapter/Runtime/CounterEntryPoint.mjs +91 -0
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint.mjs +265 -0
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res +122 -0
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res.mjs +89 -0
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +193 -0
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +137 -0
- package/src/adapter/Runtime/EventMapperEntryPoint.mjs +97 -0
- package/src/adapter/Runtime/ExtensionPointEntryPoint.mjs +101 -0
- package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res +83 -0
- package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res.mjs +59 -0
- package/src/adapter/Runtime/HandlerFactoryHelpers.mjs +142 -0
- package/src/adapter/Runtime/HeartbeatEntryPoint.mjs +38 -0
- package/src/adapter/Runtime/PluginExtensionPointEntryPoint.mjs +137 -0
- package/src/adapter/Runtime/ReadModelEntryPoint.mjs +143 -0
- package/src/adapter/Runtime/RuntimeEnvironment.res +1 -0
- package/src/adapter/Runtime/RuntimeEnvironment.res.mjs +9 -0
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +254 -0
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs +169 -0
- package/src/adapter/Runtime/SideEffectEntryPoint.mjs +81 -0
- package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res +178 -0
- package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res.mjs +129 -0
- package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res +166 -0
- package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res.mjs +87 -0
- package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +136 -0
- package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +344 -0
- package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +242 -0
- package/src/adapter/Runtime/TaskBucketEntryPoint.mjs +150 -0
- package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res +123 -0
- package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res.mjs +74 -0
- package/src/adapter/ScheduledPublisher/ScheduledPublisher.res +2 -0
- package/src/adapter/ScheduledPublisher/ScheduledPublisher.res.mjs +12 -0
- package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents.res +54 -0
- package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents.res.mjs +41 -0
- package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents_Runtime.res +97 -0
- package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents_Runtime.res.mjs +85 -0
- package/src/adapter/StateTopic/StateTopic_AppSync.res +418 -0
- package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +295 -0
- package/src/adapter/Task/TaskBucket.res +1 -0
- package/src/adapter/Task/TaskBucket.res.mjs +9 -0
- package/src/adapter/Task/TaskBucket_S3.res +157 -0
- package/src/adapter/Task/TaskBucket_S3.res.mjs +108 -0
- package/src/adapter/Task/TaskBucket_S3_Runtime.res +11 -0
- package/src/adapter/Task/TaskBucket_S3_Runtime.res.mjs +15 -0
- package/src/components/Aggregate_Builder.res +1 -0
- package/src/components/Aggregate_Builder.res.mjs +25 -0
- package/src/components/Aggregate_Builder_Micro.res +63 -0
- package/src/components/Aggregate_Builder_Micro.res.mjs +80 -0
- package/src/components/Aggregate_Builder_NoResolver.res +61 -0
- package/src/components/Aggregate_Builder_NoResolver.res.mjs +80 -0
- package/src/components/Aggregate_Builder_PerAggregate.res +57 -0
- package/src/components/Aggregate_Builder_PerAggregate.res.mjs +80 -0
- package/src/components/Aggregate_Builder_Single.res +59 -0
- package/src/components/Aggregate_Builder_Single.res.mjs +80 -0
- package/src/components/Aggregate_Builder_Single_Async.res +59 -0
- package/src/components/Aggregate_Builder_Single_Async.res.mjs +80 -0
- package/src/components/Api/AppSync_Adapter.res +500 -0
- package/src/components/Api/AppSync_Adapter.res.mjs +396 -0
- package/src/components/AutomationSlice_Builder.res +52 -0
- package/src/components/AutomationSlice_Builder.res.mjs +72 -0
- package/src/components/AutomationSlice_Builder_Bundled.res.mjs +70 -0
- package/src/components/Counter_Builder.res +10 -0
- package/src/components/Counter_Builder.res.mjs +16 -0
- package/src/components/DataCleaner.res +170 -0
- package/src/components/DataCleaner.res.mjs +2 -0
- package/src/components/DcbEventLog_Builder.res +8 -0
- package/src/components/DcbEventLog_Builder.res.mjs +14 -0
- package/src/components/ExtensionPoint_Builder.res +16 -0
- package/src/components/ExtensionPoint_Builder.res.mjs +37 -0
- package/src/components/ForeignReadModel_Builder.res +19 -0
- package/src/components/ForeignReadModel_Builder.res.mjs +48 -0
- package/src/components/InboundTranslationSlice_Builder.res +25 -0
- package/src/components/InboundTranslationSlice_Builder.res.mjs +31 -0
- package/src/components/OutboundTranslationSlice_Builder.res +60 -0
- package/src/components/OutboundTranslationSlice_Builder.res.mjs +76 -0
- package/src/components/OutboundTranslationSlice_Builder_Bundled.res.mjs +75 -0
- package/src/components/Plugin.res +29 -0
- package/src/components/Plugin.res.mjs +63 -0
- package/src/components/ReadModel_Builder.res +1 -0
- package/src/components/ReadModel_Builder.res.mjs +19 -0
- package/src/components/ReadModel_Builder_NoResolver.res +55 -0
- package/src/components/ReadModel_Builder_NoResolver.res.mjs +64 -0
- package/src/components/ReadModel_Builder_NoResolver_Stream.res +60 -0
- package/src/components/ReadModel_Builder_NoResolver_Stream.res.mjs +64 -0
- package/src/components/ReadModel_Builder_PerReadModel.res +53 -0
- package/src/components/ReadModel_Builder_PerReadModel.res.mjs +66 -0
- package/src/components/ReadModel_Builder_Single.res +52 -0
- package/src/components/ReadModel_Builder_Single.res.mjs +66 -0
- package/src/components/ReadModel_Builder_Single_Stream.res +60 -0
- package/src/components/ReadModel_Builder_Single_Stream.res.mjs +66 -0
- package/src/components/Scheduler.res +1 -0
- package/src/components/Scheduler.res.mjs +13 -0
- package/src/components/SideEffectHandler.res +1 -0
- package/src/components/SideEffectHandler.res.mjs +19 -0
- package/src/components/SideEffectHandler_PerSideEffectHandler.res +13 -0
- package/src/components/SideEffectHandler_PerSideEffectHandler.res.mjs +52 -0
- package/src/components/SideEffectHandler_Single.res +54 -0
- package/src/components/SideEffectHandler_Single.res.mjs +60 -0
- package/src/components/StateChangeSlice_Builder.res +17 -0
- package/src/components/StateChangeSlice_Builder.res.mjs +23 -0
- package/src/components/StateViewSlice_Builder.res +46 -0
- package/src/components/StateViewSlice_Builder.res.mjs +68 -0
- package/src/components/StateViewSlice_Builder_Bundled.res.mjs +69 -0
- package/src/components/StateViewSlice_Builder_Stream.res +50 -0
- package/src/components/StateViewSlice_Builder_Stream.res.mjs +68 -0
- package/src/components/Task_Builder.res +1 -0
- package/src/components/Task_Builder.res.mjs +28 -0
- package/src/components/Task_Builder_PerBucket.res +22 -0
- package/src/components/Task_Builder_PerBucket.res.mjs +63 -0
- package/src/components/Vpc.res +214 -0
- package/src/components/Vpc.res.mjs +117 -0
- package/src/components/Vpc.resi +22 -0
- package/src/errors/AppSync_Error.res +36 -0
- package/src/errors/AppSync_Error.res.mjs +38 -0
- package/src/errors/Cognito_Error.res +36 -0
- package/src/errors/Cognito_Error.res.mjs +38 -0
- package/src/errors/DynamoDb_Error.res +75 -0
- package/src/errors/DynamoDb_Error.res.mjs +86 -0
- package/src/errors/Kinesis_Error.res +36 -0
- package/src/errors/Kinesis_Error.res.mjs +38 -0
- package/src/errors/SNS_Error.res +34 -0
- package/src/errors/SNS_Error.res.mjs +38 -0
- package/src/errors/SQS_Error.res +40 -0
- package/src/errors/SQS_Error.res.mjs +41 -0
- package/src/plugin/cloner/ClonerRunner.res +1 -0
- package/src/plugin/cloner/ClonerRunner.res.mjs +9 -0
- package/src/plugin/cloner/ClonerRunner_Fargate.res +362 -0
- package/src/plugin/cloner/ClonerRunner_Fargate.res.mjs +246 -0
- package/src/plugin/cloner/ClonerRunner_Fargate_Runtime.res +42 -0
- package/src/plugin/cloner/ClonerRunner_Fargate_Runtime.res.mjs +45 -0
- package/src/plugin/heartbeat/HeartbeatRunner.res +1 -0
- package/src/plugin/heartbeat/HeartbeatRunner.res.mjs +9 -0
- package/src/plugin/heartbeat/HeartbeatRunner_CloudWatchEvents.res +96 -0
- package/src/plugin/heartbeat/HeartbeatRunner_CloudWatchEvents.res.mjs +69 -0
- package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res +115 -0
- package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res.mjs +88 -0
- package/src/plugin/runtime/PluginRuntimeOperations.res +5 -0
- package/src/plugin/runtime/PluginRuntimeOperations.res.mjs +14 -0
- package/src/plugin/runtime/PluginRuntime_Builder.res +769 -0
- package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +464 -0
- package/src/plugin/runtime/Util_PluginMessage_Runtime.res +13 -0
- package/src/plugin/runtime/Util_PluginMessage_Runtime.res.mjs +19 -0
- package/src/plugin/stack/Plugin_ExtensionPoint_Builder.res +28 -0
- package/src/plugin/stack/Plugin_ExtensionPoint_Builder.res.mjs +67 -0
- package/src/plugin/stack/Plugin_Stack.res +439 -0
- package/src/plugin/stack/Plugin_Stack.res.mjs +263 -0
- package/src/util/Util.res +29 -0
- package/src/util/Util.res.mjs +93 -0
- package/src/util/Util_AppSync.res +36 -0
- package/src/util/Util_AppSync.res.mjs +35 -0
- package/src/util/Util_AppSync_Caller.res +213 -0
- package/src/util/Util_AppSync_Caller.res.mjs +170 -0
- package/src/util/Util_Bundle.res +255 -0
- package/src/util/Util_Bundle.res.mjs +179 -0
- package/src/util/Util_Cloudwatch.res +14 -0
- package/src/util/Util_Cloudwatch.res.mjs +19 -0
- package/src/util/Util_CognitoGroupUser.res +39 -0
- package/src/util/Util_CognitoGroupUser.res.mjs +32 -0
- package/src/util/Util_CognitoGroupUser_Runtime.res +65 -0
- package/src/util/Util_CognitoGroupUser_Runtime.res.mjs +50 -0
- package/src/util/Util_Cognito_Runtime.res +52 -0
- package/src/util/Util_Cognito_Runtime.res.mjs +35 -0
- package/src/util/Util_DeadLetterQueue.res +154 -0
- package/src/util/Util_DeadLetterQueue.res.mjs +158 -0
- package/src/util/Util_DynamoDb.res +210 -0
- package/src/util/Util_DynamoDb.res.mjs +218 -0
- package/src/util/Util_DynamoDbStream.res +138 -0
- package/src/util/Util_DynamoDbStream.res.mjs +134 -0
- package/src/util/Util_DynamoDbStream_Runtime.res +94 -0
- package/src/util/Util_DynamoDbStream_Runtime.res.mjs +131 -0
- package/src/util/Util_DynamoDb_Runtime.res +216 -0
- package/src/util/Util_DynamoDb_Runtime.res.mjs +257 -0
- package/src/util/Util_DynamoDb_TableManager.res +23 -0
- package/src/util/Util_DynamoDb_TableManager.res.mjs +35 -0
- package/src/util/Util_DynamoDb_TableManager.resi +3 -0
- package/src/util/Util_Env.res +5 -0
- package/src/util/Util_Env.res.mjs +2 -0
- package/src/util/Util_EventSourceMapping.res +49 -0
- package/src/util/Util_EventSourceMapping.res.mjs +32 -0
- package/src/util/Util_HostUiDomain.res +28 -0
- package/src/util/Util_HostUiDomain.res.mjs +26 -0
- package/src/util/Util_IAM_Role.res +14 -0
- package/src/util/Util_IAM_Role.res.mjs +28 -0
- package/src/util/Util_Kinesis_Runtime.res +20 -0
- package/src/util/Util_Kinesis_Runtime.res.mjs +24 -0
- package/src/util/Util_Lambda.res +39 -0
- package/src/util/Util_Lambda.res.mjs +45 -0
- package/src/util/Util_LocalConfig.res +119 -0
- package/src/util/Util_LocalConfig.res.mjs +112 -0
- package/src/util/Util_ResourceNaming.res +15 -0
- package/src/util/Util_ResourceNaming.res.mjs +26 -0
- package/src/util/Util_S3.res +17 -0
- package/src/util/Util_S3.res.mjs +30 -0
- package/src/util/Util_S3_Runtime.res +11 -0
- package/src/util/Util_S3_Runtime.res.mjs +15 -0
- package/src/util/Util_SNS.res +33 -0
- package/src/util/Util_SNS.res.mjs +50 -0
- package/src/util/Util_SNS_FIFO.res +21 -0
- package/src/util/Util_SNS_FIFO.res.mjs +27 -0
- package/src/util/Util_SNS_Runtime.res +33 -0
- package/src/util/Util_SNS_Runtime.res.mjs +27 -0
- package/src/util/Util_SQS.res +70 -0
- package/src/util/Util_SQS.res.mjs +83 -0
- package/src/util/Util_SQS_FIFO.res +16 -0
- package/src/util/Util_SQS_FIFO.res.mjs +28 -0
- package/src/util/Util_SQS_Runtime.res +169 -0
- package/src/util/Util_SQS_Runtime.res.mjs +150 -0
- package/src/util/Util_SesPolicy_Runtime.res +54 -0
- package/src/util/Util_SesPolicy_Runtime.res.mjs +55 -0
- package/src/util/Util_StaticBundle.res +124 -0
- package/src/util/Util_StaticBundle.res.mjs +132 -0
- package/src/util/Util_Vpc.res +22 -0
- package/src/util/Util_Vpc.res.mjs +27 -0
- package/tests/AppSync_AdapterTest.res +396 -0
- package/tests/AppSync_AdapterTest.res.mjs +326 -0
- package/tests/AppSync_Resolver_RetryingTest.res +59 -0
- package/tests/AppSync_Resolver_RetryingTest.res.mjs +66 -0
- package/tests/Auth_CognitoTest.res +203 -0
- package/tests/Auth_CognitoTest.res.mjs +248 -0
- package/tests/DcbEventLogStorage_DynamoDb_RuntimeTest.res +602 -0
- package/tests/DcbEventLogStorage_DynamoDb_RuntimeTest.res.mjs +742 -0
- package/tests/EventLogStorage_DynamoDb_RuntimeTest.res +57 -0
- package/tests/EventLogStorage_DynamoDb_RuntimeTest.res.mjs +68 -0
- package/tests/MCP_LambdaTest.res +101 -0
- package/tests/MCP_LambdaTest.res.mjs +56 -0
- package/tests/Util_HostUiDomainTest.res +90 -0
- package/tests/Util_HostUiDomainTest.res.mjs +50 -0
- package/tests/Util_SQS_RuntimeTest.res +46 -0
- package/tests/Util_SQS_RuntimeTest.res.mjs +45 -0
- package/tests/Util_StaticBundleTest.res +136 -0
- package/tests/Util_StaticBundleTest.res.mjs +149 -0
- package/tests/integration/DcbCommandTopicEntryPoint_IntegrationTest.res +101 -0
- package/tests/integration/DcbCommandTopicEntryPoint_IntegrationTest.res.mjs +91 -0
- package/tests/integration/DcbEventLogStorage_DynamoDb_IntegrationTest.res +315 -0
- package/tests/integration/DcbEventLogStorage_DynamoDb_IntegrationTest.res.mjs +435 -0
- package/tests/integration/DcbIntegrationHarness.res +102 -0
- package/tests/integration/DcbIntegrationHarness.res.mjs +163 -0
- package/tests/integration/EpTestSlice.res +28 -0
- package/tests/integration/EpTestSlice.res.mjs +43 -0
- package/tests/integration/EpTestSliceBehavior.res +18 -0
- 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
|
+
}
|