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