@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,1730 @@
|
|
|
1
|
+
let log = Logger.fromEnv()
|
|
2
|
+
|
|
3
|
+
// Wrapper to safely store Pulumi Output values as option fields.
|
|
4
|
+
// Pulumi Outputs are JS Proxies; wrapping them directly in Some() triggers
|
|
5
|
+
// the BS_PRIVATE_NESTED_SOME_NONE sentinel bug. Storing as {val: x} avoids
|
|
6
|
+
// this because the plain record has no BS_PRIVATE_NESTED_SOME_NONE property.
|
|
7
|
+
type hookedValue<'a> = {val: 'a}
|
|
8
|
+
|
|
9
|
+
// Local-only intermediate type used by Plugin_Builder during construction.
|
|
10
|
+
// All fields use Pulumi.Output.t-wrapped component output types because they
|
|
11
|
+
// originate from freshly-built local components (not deserialized stack exports).
|
|
12
|
+
// Cross-stack consumers use ReventlessInterop.Plugin.resolvedOutputs instead.
|
|
13
|
+
type builderOutputs = {
|
|
14
|
+
id: string,
|
|
15
|
+
version: string,
|
|
16
|
+
heartbeatInterval: int,
|
|
17
|
+
eventCollector: EventCollector.outputs,
|
|
18
|
+
extensionPoints: dict<ExtensionPoint.outputs>,
|
|
19
|
+
extensions: dict<Extension.outputs>,
|
|
20
|
+
aggregates: dict<Aggregate.outputs>,
|
|
21
|
+
stateChangeSlices: dict<StateChangeSlice.outputs>,
|
|
22
|
+
stateViewSlices: dict<StateViewSlice.outputs>,
|
|
23
|
+
automationSlices: dict<AutomationSlice.outputs>,
|
|
24
|
+
outboundTranslationSlices: dict<OutboundTranslationSlice.outputs>,
|
|
25
|
+
inboundTranslationSlices: dict<InboundTranslationSlice.outputs>,
|
|
26
|
+
readModels: dict<ReadModel.outputs>,
|
|
27
|
+
tasks: dict<Task.outputs>,
|
|
28
|
+
resolvers: array<ReventlessInfra.Adapter.resource>,
|
|
29
|
+
heartbeat: Heartbeat.outputs,
|
|
30
|
+
dcbEventLog: option<DcbEventLog.outputs>,
|
|
31
|
+
uiFragments: option<Reventless.Plugin.uiFragmentManifest>,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let getRemoteStorageResources = (pluginName, queryDbName) =>
|
|
35
|
+
switch Util_StackRefs.get(pluginName)->Option.map(stackRef => {
|
|
36
|
+
// Read the "readModels" top-level export directly (no longer nested under "plugin").
|
|
37
|
+
let readModelsOutput: Pulumi.Output.t<option<JSON.t>> =
|
|
38
|
+
stackRef->Pulumi.StackReference.getOutput("readModels")
|
|
39
|
+
|
|
40
|
+
readModelsOutput->Pulumi.Output.apply(readModelsOpt =>
|
|
41
|
+
switch readModelsOpt {
|
|
42
|
+
| Some(rawReadModels) =>
|
|
43
|
+
switch rawReadModels->JSON.Decode.object {
|
|
44
|
+
| Some(dict) =>
|
|
45
|
+
switch dict->Dict.get(queryDbName) {
|
|
46
|
+
| Some(rawReadModel) =>
|
|
47
|
+
try {
|
|
48
|
+
let readModel =
|
|
49
|
+
rawReadModel->S.parseOrThrow(ReventlessInterop.ReadModel.resolvedOutputsSchema)
|
|
50
|
+
readModel.queryDb.resources->Adapter.fromInteropResources
|
|
51
|
+
} catch {
|
|
52
|
+
| exn =>
|
|
53
|
+
let msg =
|
|
54
|
+
exn
|
|
55
|
+
->JsExn.fromException
|
|
56
|
+
->Option.flatMap(JsExn.message)
|
|
57
|
+
->Option.getOr("parse error")
|
|
58
|
+
log.error(
|
|
59
|
+
~comp="Plugin_Helpers",
|
|
60
|
+
`getRemoteStorageResources: failed to parse readModel ${queryDbName} from ${pluginName}: ${msg}`,
|
|
61
|
+
)
|
|
62
|
+
[]
|
|
63
|
+
}
|
|
64
|
+
| None => []
|
|
65
|
+
}
|
|
66
|
+
| None => []
|
|
67
|
+
}
|
|
68
|
+
| None => []
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
}) {
|
|
72
|
+
| Some(resources) => resources
|
|
73
|
+
| None =>
|
|
74
|
+
let known = Util_StackRefs.stackRefs->Dict.keysToArray->Array.join(",")
|
|
75
|
+
log.error(
|
|
76
|
+
~comp="Plugin_Helpers",
|
|
77
|
+
`getRemoteStorageResources: plugin ${pluginName} not found (check platform.stack config) known=[${known}]`,
|
|
78
|
+
)
|
|
79
|
+
[]->Pulumi.Output.make
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let getStorageResources = (allQueryDbs, pluginName, queryDbName) =>
|
|
83
|
+
switch pluginName {
|
|
84
|
+
| None => Util_QueryDb.getLocalStorageResources(allQueryDbs, queryDbName)->Pulumi.Output.make
|
|
85
|
+
| Some(pluginName) => getRemoteStorageResources(pluginName, queryDbName)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// Plugin EventCollector context registry.
|
|
90
|
+
//
|
|
91
|
+
// Adapter runtime entry points (e.g. AdminEventCollectorEntryPoint.mjs) read
|
|
92
|
+
// HANDLER_CONFIG to reconstruct Plugin_Callback at cold start. The JSON payload
|
|
93
|
+
// is assembled at deploy time inside `forPluginEventCollector` (AWS), but the
|
|
94
|
+
// plugin-specific data (pluginDefinition, the auto-included Connect extension
|
|
95
|
+
// entry, the admin EP cmd-topic URL the extension publishes to) is only known
|
|
96
|
+
// inside `Plugin_Helpers.connect` / `Platform_Admin.connect` where the relevant
|
|
97
|
+
// Outputs have resolved.
|
|
98
|
+
//
|
|
99
|
+
// The registry bridges that gap: core's connect helpers register the per-plugin
|
|
100
|
+
// context here keyed by EventCollector component name; the AWS adapter reads it
|
|
101
|
+
// when building the Lambda's HANDLER_CONFIG. Adapters that don't need it (in-
|
|
102
|
+
// memory) simply ignore the ref.
|
|
103
|
+
//
|
|
104
|
+
// moduleUrl strings are hardcoded framework-internal paths (Admin Plugin EP +
|
|
105
|
+
// PluginConnectExtension only — both ship from reventless-core/-infra). When
|
|
106
|
+
// user-declared extensions land, propagate Blueprint.moduleUrl through the
|
|
107
|
+
// registry too — likely by extending `Plugin_Helpers.createExtensions` to
|
|
108
|
+
// capture each blueprint's moduleUrl into its returned tuple.
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
type extensionPointEntry = {
|
|
112
|
+
specModule: string,
|
|
113
|
+
mappingsModule: string,
|
|
114
|
+
eventTopicArn: Pulumi.Output.t<string>,
|
|
115
|
+
aggregateNames: array<string>,
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
type connectExtensionEntry = {
|
|
119
|
+
specModule: string,
|
|
120
|
+
mappingsModule: string,
|
|
121
|
+
extensionPointName: string,
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// User-declared extension entry — one per merged extension group (multiple
|
|
125
|
+
// `*_Extension.res` files targeting the same EP combine into one component).
|
|
126
|
+
type extensionEntry = {
|
|
127
|
+
name: string,
|
|
128
|
+
specModule: string,
|
|
129
|
+
mappingsModule: string,
|
|
130
|
+
// npm specifier of the Delegate spec (aggregate / slice). The bundled Plugin
|
|
131
|
+
// EventCollector entry point dynamic-imports this at cold start to
|
|
132
|
+
// reconstruct `Mapping.Delegate` before handing the full mapping to
|
|
133
|
+
// ExtensionMapping.Make.
|
|
134
|
+
delegateModule: string,
|
|
135
|
+
extensionPointName: string,
|
|
136
|
+
// Subset of top-level publishToAggregates this extension publishes to —
|
|
137
|
+
// derived from Extension.outputs.aggregateNames.
|
|
138
|
+
aggregateNames: array<string>,
|
|
139
|
+
// RMs this extension's mapping may enqueue events into directly. Derived
|
|
140
|
+
// from readModelNamesForSourceName by reverse lookup on the extension's
|
|
141
|
+
// EP name (the source carried on incoming events).
|
|
142
|
+
readModelNames: array<string>,
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
type eventCollectorContext = {
|
|
146
|
+
// Pre-serialised JSON of the pluginDefinition (sury-encoded then JSON.stringify).
|
|
147
|
+
// Output because it's assembled from resolved component outputs at deploy time.
|
|
148
|
+
pluginDefinitionJson: Pulumi.Output.t<string>,
|
|
149
|
+
// Outgoing EPs whose events this Lambda's mapping handles. Admin Lambda: 1
|
|
150
|
+
// entry (its own Plugin EP). Plugin Lambda: 0 (plugins don't process their own
|
|
151
|
+
// EP outgoing events here).
|
|
152
|
+
extensionPoints: array<extensionPointEntry>,
|
|
153
|
+
// The auto-included PluginConnectExtension entry. Some for plugins; None for
|
|
154
|
+
// the admin Lambda (the admin IS the Plugin EP, doesn't consume it).
|
|
155
|
+
connectExtension: option<connectExtensionEntry>,
|
|
156
|
+
// User-declared extensions on this plugin. Empty for the admin Lambda.
|
|
157
|
+
extensions: array<extensionEntry>,
|
|
158
|
+
// SQS URL of the admin's Plugin EP command topic — where extensions publish
|
|
159
|
+
// ConnectPlugin commands via `publishToPluginExtensionPoint`. Empty string
|
|
160
|
+
// for admin (no extensions consume it).
|
|
161
|
+
pluginExtensionPointCmdTopicUrl: Pulumi.Output.t<string>,
|
|
162
|
+
// aggregateName → cmd-topic SQS URL. Includes every aggregate / DCB slice
|
|
163
|
+
// any user extension publishes to plus the admin's Plugin aggregate when
|
|
164
|
+
// ConnectExtension is wired.
|
|
165
|
+
publishToAggregates: dict<Pulumi.Output.t<string>>,
|
|
166
|
+
// rmName → RM EventCollector SQS URL. Populated only for RMs that user
|
|
167
|
+
// extensions feed directly via Extension_Operations.publishToReadModels.
|
|
168
|
+
readModelQueueUrls: dict<Pulumi.Output.t<string>>,
|
|
169
|
+
// Inversion of RM.sourceNames — sourceServiceName → [rmName]. Same dict
|
|
170
|
+
// surface used by Builder_Helpers; serialised verbatim into HANDLER_CONFIG.
|
|
171
|
+
readModelNamesForSourceName: dict<array<string>>,
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
let eventCollectorContextRef: ref<dict<eventCollectorContext>> = ref(Dict.make())
|
|
175
|
+
|
|
176
|
+
let registerEventCollectorContext = (~componentName: string, ~context: eventCollectorContext) =>
|
|
177
|
+
eventCollectorContextRef.contents->Dict.set(componentName, context)
|
|
178
|
+
|
|
179
|
+
// Framework-internal module URLs for the auto-Connect path — package specifiers
|
|
180
|
+
// the bundled EventCollector resolves (NOT file URLs). The two mapping specifiers
|
|
181
|
+
// are sourced from each module's `@@reventless.spec`-injected `moduleUrl`
|
|
182
|
+
// (derived from the file path, so they survive folder moves) rather than
|
|
183
|
+
// hardcoded. The spec specifier lives in reventless-infra (whose own `moduleUrl`
|
|
184
|
+
// is an `import.meta.url` file URL, the wrong form here), so it stays an explicit
|
|
185
|
+
// package specifier.
|
|
186
|
+
// User-declared extensions will eventually need to propagate their own URLs
|
|
187
|
+
// via createExtensions; this scope only covers the built-in Plugin EP + Connect.
|
|
188
|
+
let adminPluginExtensionPointSpecModule =
|
|
189
|
+
"@reventlessdev/reventless-infra/src/types/PluginExtensionPointSpec.res.mjs"
|
|
190
|
+
let adminPluginExtensionPointMappingsModule = PluginExtensionPoint_Plugin.moduleUrl
|
|
191
|
+
// Runtime-safe mapping module (not the `_Builder` variant, which the Lambda layer
|
|
192
|
+
// strips because it pulls Pulumi via `include Extension_Builder.Make(...)`).
|
|
193
|
+
let pluginConnectExtensionMappingsModule = PluginConnectExtension_Mapping.moduleUrl
|
|
194
|
+
|
|
195
|
+
type jsonEventsHandler = Plugin_Callback.jsonEventsHandler
|
|
196
|
+
type jsonEventsHandlers = {
|
|
197
|
+
outgoing?: jsonEventsHandler,
|
|
198
|
+
incoming?: jsonEventsHandler,
|
|
199
|
+
}
|
|
200
|
+
let getIncomingJsonEventsHandler = jsonEventsHandlers => jsonEventsHandlers.incoming
|
|
201
|
+
let getOutgoingJsonEventsHandler = jsonEventsHandlers => jsonEventsHandlers.outgoing
|
|
202
|
+
|
|
203
|
+
let serviceNameToJsonEventsHandlers: (
|
|
204
|
+
array<'o>,
|
|
205
|
+
'o => array<string>,
|
|
206
|
+
array<jsonEventsHandlers>,
|
|
207
|
+
jsonEventsHandlers => option<jsonEventsHandler>,
|
|
208
|
+
) => dict<array<jsonEventsHandler>> = (outputs, getServiceNames, handlers, getEventHandler) => {
|
|
209
|
+
let dict = Dict.make()
|
|
210
|
+
Array.zip(outputs, handlers)->Array.forEach(((outputs, jsonEventsHandlers)) => {
|
|
211
|
+
jsonEventsHandlers
|
|
212
|
+
->getEventHandler
|
|
213
|
+
->Option.forEach(jsonEventsHandler =>
|
|
214
|
+
outputs
|
|
215
|
+
->getServiceNames
|
|
216
|
+
->Array.forEach(
|
|
217
|
+
serviceName =>
|
|
218
|
+
switch dict->Dict.get(serviceName) {
|
|
219
|
+
| Some(jsonEventsHandlers) =>
|
|
220
|
+
Dict.set(dict, serviceName, jsonEventsHandlers->Array.concat([jsonEventsHandler]))
|
|
221
|
+
| None => Dict.set(dict, serviceName, [jsonEventsHandler])
|
|
222
|
+
},
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
})
|
|
226
|
+
dict
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
include Builder_Helpers
|
|
230
|
+
|
|
231
|
+
module ApiNoApi = ApiNoApiHelpers
|
|
232
|
+
|
|
233
|
+
let isNoApi = ApiNoApi.isNoApi
|
|
234
|
+
let getExcludedVariants = ApiNoApi.getExcludedVariants
|
|
235
|
+
let filterNoApiVariants = ApiNoApi.filterNoApiVariants
|
|
236
|
+
|
|
237
|
+
// Side-output of `createExtensions`: per merged-group registry data the
|
|
238
|
+
// adapter entry point reconstructs Extension_Operations from. Lives next to
|
|
239
|
+
// the existing `extensionsOutputs` / `extensionsHandlers` arrays — same
|
|
240
|
+
// indexing.
|
|
241
|
+
type extensionRegistryInfo = {
|
|
242
|
+
specModule: string,
|
|
243
|
+
mappingsModule: string,
|
|
244
|
+
delegateModule: string,
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
let createExtensions = (
|
|
248
|
+
extensions: array<module(ReventlessInfra.Extension.Blueprint)>,
|
|
249
|
+
~pluginName,
|
|
250
|
+
~publishToPluginExtensionPoint,
|
|
251
|
+
~publishToAggregates,
|
|
252
|
+
~publishToReadModels,
|
|
253
|
+
~queryEngine,
|
|
254
|
+
~opts,
|
|
255
|
+
) => {
|
|
256
|
+
// Group blueprints by extension point name for auto-merge.
|
|
257
|
+
let groups: dict<array<module(ReventlessInfra.Extension.Blueprint)>> = Dict.make()
|
|
258
|
+
extensions->Array.forEach(bp => {
|
|
259
|
+
let module(BP: ReventlessInfra.Extension.Blueprint) = bp
|
|
260
|
+
let epName = BP.Spec.name
|
|
261
|
+
switch groups->Dict.get(epName) {
|
|
262
|
+
| Some(existing) => existing->Array.push(bp)
|
|
263
|
+
| None => groups->Dict.set(epName, [bp])
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
// For each EP group: merge mappings, build Extension component.
|
|
268
|
+
let triples = groups
|
|
269
|
+
->Dict.toArray
|
|
270
|
+
->Array.map(((_epName, blueprints)) => {
|
|
271
|
+
// Use the first blueprint's Spec as the canonical type.
|
|
272
|
+
let module(First: ReventlessInfra.Extension.Blueprint) = blueprints->Array.getUnsafe(0)
|
|
273
|
+
|
|
274
|
+
// Merge all mappings arrays. Blueprints for the same EP have the same Spec
|
|
275
|
+
// at runtime — use Obj.magic to unify the existential Mapping types.
|
|
276
|
+
let allMappings: array<module(First.Mapping)> =
|
|
277
|
+
blueprints->Array.flatMap(bp => {
|
|
278
|
+
let module(BP: ReventlessInfra.Extension.Blueprint) = bp
|
|
279
|
+
(BP.mappings: array<module(BP.Mapping)>)->Obj.magic
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
// Build the Extension component with pluginName as the extension name.
|
|
283
|
+
module Spec = First.Spec
|
|
284
|
+
module Mappings: Extension.Mappings with module Spec := Spec = {
|
|
285
|
+
module type Mapping = ReventlessInfra.ExtensionMapping.T
|
|
286
|
+
with module ExtensionPoint := Spec
|
|
287
|
+
let name = pluginName
|
|
288
|
+
let moduleUrl = First.moduleUrl
|
|
289
|
+
let mappings: array<module(Mapping)> = allMappings->Obj.magic
|
|
290
|
+
}
|
|
291
|
+
module ExtensionMaker = Extension_Builder.Make(Spec, Mappings)
|
|
292
|
+
|
|
293
|
+
let extension = ExtensionMaker.make(
|
|
294
|
+
~publishToPluginExtensionPoint,
|
|
295
|
+
~publishToAggregates,
|
|
296
|
+
~readModelNamesForSourceName,
|
|
297
|
+
~publishToReadModels,
|
|
298
|
+
~queryEngine,
|
|
299
|
+
~opts=Some(opts),
|
|
300
|
+
)
|
|
301
|
+
let ops: Pulumi.Output.t<Extension.operations> =
|
|
302
|
+
ExtensionMaker.operations(extension)->Obj.magic
|
|
303
|
+
let registryInfo: extensionRegistryInfo = {
|
|
304
|
+
// EP spec module URL (e.g. OrderingSpec/.../Orders_ExtensionPoint.res.mjs)
|
|
305
|
+
specModule: Spec.moduleUrl,
|
|
306
|
+
// User extension file URL — the .res.mjs declaring `module Mapping`.
|
|
307
|
+
// The bundled Plugin EventCollector entry point dynamic-imports this to
|
|
308
|
+
// pick up mapIncomingEvent / mapOutgoingEvent. Multi-blueprint merges
|
|
309
|
+
// still surface one path here so the runtime has one known module to
|
|
310
|
+
// import; the entry point regroups by extensionPointName.
|
|
311
|
+
mappingsModule: First.moduleUrl,
|
|
312
|
+
// Delegate spec URL (aggregate / slice the extension delegates to).
|
|
313
|
+
// The entry point dynamic-imports this to reconstruct
|
|
314
|
+
// `Mapping.Delegate` before handing the full mapping to ExtensionMapping.Make.
|
|
315
|
+
delegateModule: First.delegateModuleUrl,
|
|
316
|
+
}
|
|
317
|
+
(
|
|
318
|
+
ExtensionMaker.outputs(extension),
|
|
319
|
+
ops->Pulumi.Output.apply(({outgoingJsonEventsHandler, incomingJsonEventsHandler}) => {
|
|
320
|
+
incoming: incomingJsonEventsHandler,
|
|
321
|
+
outgoing: outgoingJsonEventsHandler,
|
|
322
|
+
}),
|
|
323
|
+
registryInfo,
|
|
324
|
+
)
|
|
325
|
+
})
|
|
326
|
+
let outputs = triples->Array.map(((o, _, _)) => o)
|
|
327
|
+
let handlers = triples->Array.map(((_, h, _)) => h)
|
|
328
|
+
let registryInfos = triples->Array.map(((_, _, r)) => r)
|
|
329
|
+
(outputs, handlers, registryInfos)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
let extractExtensionPointDefinitions = (extensionPointsOutputs: array<ExtensionPoint.outputs>) =>
|
|
333
|
+
extensionPointsOutputs
|
|
334
|
+
->Array.map(extensionPointOutputs =>
|
|
335
|
+
(
|
|
336
|
+
extensionPointOutputs.commandTopic->Pulumi.Output.flatMap(({resources}) =>
|
|
337
|
+
switch resources->Array.get(0) {
|
|
338
|
+
| Some(r) => r.id
|
|
339
|
+
| None => Pulumi.Output.make("")
|
|
340
|
+
}
|
|
341
|
+
),
|
|
342
|
+
extensionPointOutputs.eventTopic->Pulumi.Output.flatMap(({resources}) =>
|
|
343
|
+
switch resources->Array.get(0) {
|
|
344
|
+
| Some(r) => r.id
|
|
345
|
+
| None => Pulumi.Output.make("")
|
|
346
|
+
}
|
|
347
|
+
),
|
|
348
|
+
)
|
|
349
|
+
->Pulumi.Output.all2
|
|
350
|
+
->Pulumi.Output.apply(((commandTopicChannelId, eventTopicPublisherId)) => {
|
|
351
|
+
Reventless.Plugin.name: extensionPointOutputs.name,
|
|
352
|
+
commandTopic: commandTopicChannelId,
|
|
353
|
+
eventTopic: eventTopicPublisherId,
|
|
354
|
+
})
|
|
355
|
+
)
|
|
356
|
+
->Pulumi.Output.all
|
|
357
|
+
|
|
358
|
+
// `dcbSources` lists cross-plugin DCB EventLog names this extension consumes.
|
|
359
|
+
// Empty for now: the current Extension model is mono-source (single EP Spec).
|
|
360
|
+
// Future work will populate this by walking extension blueprint mappings for
|
|
361
|
+
// `Source { name = "<peer>DcbEventLog" }` declarations.
|
|
362
|
+
let extractExtensionDefinitions = (extensionsOutputs: array<Extension.outputs>) =>
|
|
363
|
+
extensionsOutputs->Array.map(extensionOutputs => {
|
|
364
|
+
Reventless.Plugin.name: extensionOutputs.name,
|
|
365
|
+
extensionPointName: extensionOutputs.extensionPointName,
|
|
366
|
+
dcbSources: [],
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
let createConnectPluginExtension = (
|
|
370
|
+
~pluginDefinition,
|
|
371
|
+
~publishToPluginExtensionPoint,
|
|
372
|
+
~publishToAggregates,
|
|
373
|
+
~readModelNamesForSourceName,
|
|
374
|
+
~publishToReadModels,
|
|
375
|
+
~queryEngine,
|
|
376
|
+
~opts,
|
|
377
|
+
) =>
|
|
378
|
+
pluginDefinition
|
|
379
|
+
->Pulumi.Output.apply(pluginDefinition => {
|
|
380
|
+
module ConnectPluginExtension = PluginConnectExtension_Builder.Make({
|
|
381
|
+
let pluginDefinition = pluginDefinition
|
|
382
|
+
})
|
|
383
|
+
let connectPluginExtension = ConnectPluginExtension.make(
|
|
384
|
+
~publishToPluginExtensionPoint,
|
|
385
|
+
~publishToAggregates,
|
|
386
|
+
~readModelNamesForSourceName,
|
|
387
|
+
~publishToReadModels,
|
|
388
|
+
~queryEngine,
|
|
389
|
+
~opts=Some(opts),
|
|
390
|
+
)
|
|
391
|
+
let connectPluginExtensionOutputs = connectPluginExtension->Component.outputs
|
|
392
|
+
let connectPluginExtensionIncomingEventHandler =
|
|
393
|
+
connectPluginExtension
|
|
394
|
+
->Component.operations
|
|
395
|
+
->Pulumi.Output.apply(({incomingJsonEventsHandler}) => incomingJsonEventsHandler)
|
|
396
|
+
|
|
397
|
+
(connectPluginExtensionOutputs, connectPluginExtensionIncomingEventHandler)
|
|
398
|
+
})
|
|
399
|
+
->Pulumi.Output.unzip
|
|
400
|
+
|
|
401
|
+
let tasksOutputs = ref([])
|
|
402
|
+
|
|
403
|
+
let createTasks = (
|
|
404
|
+
tasks,
|
|
405
|
+
~aggregatesOutputs,
|
|
406
|
+
~scheduler,
|
|
407
|
+
~schedulerRoleUrn,
|
|
408
|
+
~publishToAggregates,
|
|
409
|
+
~queryEngine,
|
|
410
|
+
~resourceNaming,
|
|
411
|
+
~opts,
|
|
412
|
+
) => {
|
|
413
|
+
tasksOutputs :=
|
|
414
|
+
tasks->Array.map((module(SpecificTask: ReventlessInfra.Task.T)) =>
|
|
415
|
+
SpecificTask.outputs(SpecificTask.make(
|
|
416
|
+
~queryBucketName=(~taskName, ~bucketName="Bucket") =>
|
|
417
|
+
ResourceQueryRuntime.bucketNameOfTaskExn(tasksOutputs.contents, ~taskName, ~bucketName),
|
|
418
|
+
~scheduler,
|
|
419
|
+
~schedulerRoleUrn,
|
|
420
|
+
~publishToAggregates,
|
|
421
|
+
~queryEngine,
|
|
422
|
+
~resourceNaming,
|
|
423
|
+
~allAggregates=aggregatesOutputs,
|
|
424
|
+
~opts=Some(opts),
|
|
425
|
+
))
|
|
426
|
+
)
|
|
427
|
+
tasksOutputs.contents
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
module MakeEventCollectorHelper = (
|
|
431
|
+
RuntimeEnvironment: Runtime.Environment,
|
|
432
|
+
EventCollectorChannel: EventCollector_Adapter.Channel
|
|
433
|
+
with type runtimeParts = RuntimeEnvironment.parts,
|
|
434
|
+
PluginRuntimeBuilder: PluginRuntime_Builder.T
|
|
435
|
+
with module EventCollectorChannel = EventCollectorChannel,
|
|
436
|
+
) => {
|
|
437
|
+
module PluginEventCollector = EventCollector_Builder.Make(
|
|
438
|
+
RuntimeEnvironment,
|
|
439
|
+
EventCollectorChannel,
|
|
440
|
+
)
|
|
441
|
+
let make = (~name, ~eventTopics, ~opts) => {
|
|
442
|
+
let eventCollector = PluginEventCollector.make(~name, ~eventTopics, ~opts)
|
|
443
|
+
let eventCollectorOutputs = eventCollector->Component.outputs
|
|
444
|
+
// pluginDefinition.eventCollector must identify the EC's inbound queue
|
|
445
|
+
// ARN — admin's `manageSubscriptions` uses it as the subscription target
|
|
446
|
+
// (SNS topic → SQS endpoint). The AWS SQS EventCollectorChannel appends
|
|
447
|
+
// the queue resource AFTER the upstream event-topic resources in its
|
|
448
|
+
// `channel.resources` list, so the queue is always the last element.
|
|
449
|
+
// Earlier this picked `resources[0]`, which silently selected the first
|
|
450
|
+
// subscribed event topic (e.g. a DCB EventLog DynamoDB stream ARN) and
|
|
451
|
+
// produced "Invalid parameter: SQS endpoint ARN" errors at subscribe
|
|
452
|
+
// time. In-memory adapters don't use this field (no SNS), so the choice
|
|
453
|
+
// there is moot.
|
|
454
|
+
let resources = eventCollectorOutputs.resources
|
|
455
|
+
let eventCollectorUrn = switch resources->Array.get(resources->Array.length - 1) {
|
|
456
|
+
| Some(r) => r.urn
|
|
457
|
+
| None => Pulumi.Output.make("")
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
(eventCollector, eventCollectorOutputs, eventCollectorUrn)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Unified connect — handles both with-admin and without-admin cases.
|
|
464
|
+
// When ~pluginExtensionPointUnwrapped is provided, admin extension point
|
|
465
|
+
// resources and ConnectPluginExtension handlers are wired. Otherwise falls
|
|
466
|
+
// back to a simplified path (no admin extension point resources, empty
|
|
467
|
+
// incoming connect extension handlers).
|
|
468
|
+
let connect = (
|
|
469
|
+
~eventCollector: EventCollector.component,
|
|
470
|
+
~eventTopics: EventTopic.allOutputs,
|
|
471
|
+
~extensionPointsOutputs: array<ExtensionPoint.outputs>,
|
|
472
|
+
~extensionsOutputs: array<Extension.outputs>,
|
|
473
|
+
~pluginExtensionPointUnwrapped: option<ReventlessInterop.ExtensionPoint.resolvedOutputs>=?,
|
|
474
|
+
~pluginDefinition,
|
|
475
|
+
~connectPluginExtensionIncomingEventHandler: option<Pulumi.Output.t<Pulumi.Output.t<Plugin_Callback.jsonEventsHandler>>>=?,
|
|
476
|
+
~extensionsHandlers,
|
|
477
|
+
~extensionPointsHandlers,
|
|
478
|
+
~connectPluginExtensionOutputs: option<Pulumi.Output.t<Extension.outputs>>=?,
|
|
479
|
+
// Per-extension registry metadata (parallel to extensionsOutputs / extensionsHandlers).
|
|
480
|
+
// Empty when there are no user extensions or when the caller doesn't need
|
|
481
|
+
// bundled-handler reconstruction (e.g. Platform_Admin path).
|
|
482
|
+
~extensionRegistryInfos: array<extensionRegistryInfo>=[],
|
|
483
|
+
// Per-extension-point registry metadata (parallel to extensionPointsOutputs /
|
|
484
|
+
// extensionPointsHandlers). Carries spec + mapping module URLs the
|
|
485
|
+
// EventCollector entry point dynamic-imports to publish outgoing events.
|
|
486
|
+
// Empty when there are no user extension points (e.g. Platform_Admin path).
|
|
487
|
+
~extensionPointRegistryInfos: array<Builder_Helpers.extensionPointRegistryInfo>=[],
|
|
488
|
+
// aggregateName → cmd-topic SQS URL. Includes every aggregate / DCB slice
|
|
489
|
+
// that a user extension may publish to. The full plugin dict is fine —
|
|
490
|
+
// the runtime filters to ext.aggregateNames when invoking each extension.
|
|
491
|
+
~aggregateQueueUrls: dict<Pulumi.Output.t<string>>=Dict.make(),
|
|
492
|
+
// rmName → RM EventCollector SQS URL. Populated only for RMs that
|
|
493
|
+
// user extensions may enqueue events into directly. Plugin RMs fed
|
|
494
|
+
// through the normal EventTopic → EventColl subscription path don't
|
|
495
|
+
// need entries here.
|
|
496
|
+
~readModelQueueUrls: dict<Pulumi.Output.t<string>>=Dict.make(),
|
|
497
|
+
// The global readModelNamesForSourceName inversion (sourceName → [rmName]).
|
|
498
|
+
// Plain dict — no Pulumi outputs because it's pure name metadata.
|
|
499
|
+
~readModelNamesForSourceName: dict<array<string>>=Dict.make(),
|
|
500
|
+
) => {
|
|
501
|
+
let resources =
|
|
502
|
+
extensionPointsOutputs
|
|
503
|
+
->Array.map(extensionPoint => extensionPoint.eventTopic)
|
|
504
|
+
->Pulumi.Output.all
|
|
505
|
+
->Pulumi.Output.apply(eventTopics => {
|
|
506
|
+
let base =
|
|
507
|
+
eventTopics
|
|
508
|
+
->Array.map(eventTopic => eventTopic.resources)
|
|
509
|
+
->Array.flat
|
|
510
|
+
switch pluginExtensionPointUnwrapped {
|
|
511
|
+
| Some(unwrapped) =>
|
|
512
|
+
base->Array.concat(unwrapped.commandTopic.resources->Adapter.fromInteropResources)
|
|
513
|
+
| None => base
|
|
514
|
+
}
|
|
515
|
+
})
|
|
516
|
+
|
|
517
|
+
// Resolve ConnectPluginExtension data (or provide defaults for no-Core path)
|
|
518
|
+
let connectExtData =
|
|
519
|
+
switch (connectPluginExtensionIncomingEventHandler, connectPluginExtensionOutputs) {
|
|
520
|
+
| (Some(handler), Some(outputs)) =>
|
|
521
|
+
(handler->Pulumi.Output.unwrap, outputs)
|
|
522
|
+
->Pulumi.Output.all2
|
|
523
|
+
->Pulumi.Output.apply(((h, o)) => Some((h, o)))
|
|
524
|
+
| _ => Pulumi.Output.make(None)
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
(
|
|
528
|
+
pluginDefinition,
|
|
529
|
+
connectExtData,
|
|
530
|
+
extensionsHandlers->Pulumi.Output.all,
|
|
531
|
+
extensionPointsHandlers->Pulumi.Output.all,
|
|
532
|
+
resources,
|
|
533
|
+
)
|
|
534
|
+
->Pulumi.Output.all5
|
|
535
|
+
->Pulumi.Output.apply(((
|
|
536
|
+
pluginDefinition,
|
|
537
|
+
connectExtData,
|
|
538
|
+
extensionsHandlers,
|
|
539
|
+
extensionPointsHandlers,
|
|
540
|
+
resources,
|
|
541
|
+
)) => {
|
|
542
|
+
let incomingConnectExtensionEventHandlers = switch connectExtData {
|
|
543
|
+
| Some((handler, outputs)) =>
|
|
544
|
+
serviceNameToJsonEventsHandlers(
|
|
545
|
+
[outputs],
|
|
546
|
+
outputs => [outputs.extensionPointName],
|
|
547
|
+
[{incoming: handler}],
|
|
548
|
+
getIncomingJsonEventsHandler,
|
|
549
|
+
)
|
|
550
|
+
| None => Dict.make()
|
|
551
|
+
}
|
|
552
|
+
module Callback = Plugin_Callback.Make({
|
|
553
|
+
let pluginDefinition = pluginDefinition
|
|
554
|
+
let outgoingExtensionPointEventHandlers = serviceNameToJsonEventsHandlers(
|
|
555
|
+
extensionPointsOutputs,
|
|
556
|
+
outputs => outputs.aggregateNames,
|
|
557
|
+
extensionPointsHandlers->Array.map(extensionPointsHandler => {
|
|
558
|
+
outgoing: extensionPointsHandler,
|
|
559
|
+
}),
|
|
560
|
+
getOutgoingJsonEventsHandler,
|
|
561
|
+
)
|
|
562
|
+
let incomingConnectExtensionEventHandlers = incomingConnectExtensionEventHandlers
|
|
563
|
+
let outgoingExtensionEventHandlers = serviceNameToJsonEventsHandlers(
|
|
564
|
+
extensionsOutputs,
|
|
565
|
+
outputs => outputs.aggregateNames,
|
|
566
|
+
extensionsHandlers,
|
|
567
|
+
getOutgoingJsonEventsHandler,
|
|
568
|
+
)
|
|
569
|
+
let incomingExtensionEventHandlers = serviceNameToJsonEventsHandlers(
|
|
570
|
+
extensionsOutputs,
|
|
571
|
+
outputs => [outputs.extensionPointName],
|
|
572
|
+
extensionsHandlers,
|
|
573
|
+
getIncomingJsonEventsHandler,
|
|
574
|
+
)
|
|
575
|
+
})
|
|
576
|
+
let handler = PluginEventCollector.makeHandler(
|
|
577
|
+
~eventCollector,
|
|
578
|
+
~jsonEventsHandler=Callback.handleJsonEvents,
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
// Register the runtime context the adapter entry point reconstructs from
|
|
582
|
+
// HANDLER_CONFIG. AWS reads this in forPluginEventCollector; in-memory
|
|
583
|
+
// adapters ignore it and stay on the Output-bound Callback above.
|
|
584
|
+
let ecResource = eventCollector->Component.toPulumiResource
|
|
585
|
+
let ecName =
|
|
586
|
+
ecResource.name->ComponentType.nameOpt(EventCollector.componentType)
|
|
587
|
+
let pluginDefinitionJson =
|
|
588
|
+
pluginDefinition
|
|
589
|
+
->S.reverseConvertToJsonOrThrow(Reventless.Plugin.pluginDefinitionSchema)
|
|
590
|
+
->JSON.stringify
|
|
591
|
+
->Pulumi.Output.make
|
|
592
|
+
let pluginExtensionPointCmdTopicUrl = switch pluginExtensionPointUnwrapped {
|
|
593
|
+
| Some(unwrapped) =>
|
|
594
|
+
switch unwrapped.commandTopic.resources->Array.get(0) {
|
|
595
|
+
| Some(r) => Pulumi.Output.make(r.id)
|
|
596
|
+
| None => Pulumi.Output.make("")
|
|
597
|
+
}
|
|
598
|
+
| None => Pulumi.Output.make("")
|
|
599
|
+
}
|
|
600
|
+
let connectExtension = switch connectExtData {
|
|
601
|
+
| Some(_) =>
|
|
602
|
+
Some({
|
|
603
|
+
specModule: adminPluginExtensionPointSpecModule,
|
|
604
|
+
mappingsModule: pluginConnectExtensionMappingsModule,
|
|
605
|
+
extensionPointName: ReventlessInfra.PluginExtensionPointSpec.name,
|
|
606
|
+
})
|
|
607
|
+
| None => None
|
|
608
|
+
}
|
|
609
|
+
// Build the user-extension registry entries. Each entry pairs
|
|
610
|
+
// Extension.outputs (name/EP name/aggregate names) with the matching
|
|
611
|
+
// moduleUrls captured by createExtensions. Skipped if the parallel
|
|
612
|
+
// arrays don't match length — defensive against future refactors.
|
|
613
|
+
let extensions: array<extensionEntry> = if (
|
|
614
|
+
extensionsOutputs->Array.length == extensionRegistryInfos->Array.length
|
|
615
|
+
) {
|
|
616
|
+
extensionsOutputs->Array.mapWithIndex((output, i) => {
|
|
617
|
+
let info = extensionRegistryInfos->Array.getUnsafe(i)
|
|
618
|
+
let rmNames =
|
|
619
|
+
readModelNamesForSourceName
|
|
620
|
+
->Dict.get(output.extensionPointName)
|
|
621
|
+
->Option.getOr([])
|
|
622
|
+
{
|
|
623
|
+
name: output.name,
|
|
624
|
+
specModule: info.specModule,
|
|
625
|
+
mappingsModule: info.mappingsModule,
|
|
626
|
+
delegateModule: info.delegateModule,
|
|
627
|
+
extensionPointName: output.extensionPointName,
|
|
628
|
+
aggregateNames: output.aggregateNames,
|
|
629
|
+
readModelNames: rmNames,
|
|
630
|
+
}
|
|
631
|
+
})
|
|
632
|
+
} else {
|
|
633
|
+
[]
|
|
634
|
+
}
|
|
635
|
+
// Connect always publishes to the admin's Plugin aggregate. When
|
|
636
|
+
// user extensions are wired, merge their aggregate URLs in as well —
|
|
637
|
+
// the runtime filters per extension.
|
|
638
|
+
let mergedAggregateUrls = Dict.make()
|
|
639
|
+
aggregateQueueUrls
|
|
640
|
+
->Dict.toArray
|
|
641
|
+
->Array.forEach(((k, v)) => mergedAggregateUrls->Dict.set(k, v))
|
|
642
|
+
switch connectExtData {
|
|
643
|
+
| Some(_) =>
|
|
644
|
+
// Plugin aggregate cmd-topic URL = the admin's Plugin EP cmd topic
|
|
645
|
+
// (Connect routes ConnectPlugin through the EP). Look it up if not
|
|
646
|
+
// already supplied by the caller.
|
|
647
|
+
switch mergedAggregateUrls->Dict.get(ReventlessCore.PluginSpec.name) {
|
|
648
|
+
| Some(_) => ()
|
|
649
|
+
| None => mergedAggregateUrls->Dict.set(ReventlessCore.PluginSpec.name, pluginExtensionPointCmdTopicUrl)
|
|
650
|
+
}
|
|
651
|
+
| None => ()
|
|
652
|
+
}
|
|
653
|
+
// Build the extension-point registry entries (mirrors the user-extension
|
|
654
|
+
// pattern above). Pairs Pulumi outputs (name / eventTopic / aggregateNames)
|
|
655
|
+
// with the moduleUrls captured by createExtensionPoints. Skipped if the
|
|
656
|
+
// parallel arrays don't match length — defensive against future refactors.
|
|
657
|
+
let extensionPointEntries: array<extensionPointEntry> = if (
|
|
658
|
+
extensionPointsOutputs->Array.length == extensionPointRegistryInfos->Array.length
|
|
659
|
+
) {
|
|
660
|
+
extensionPointsOutputs->Array.mapWithIndex((output, i) => {
|
|
661
|
+
let info = extensionPointRegistryInfos->Array.getUnsafe(i)
|
|
662
|
+
let eventTopicArn =
|
|
663
|
+
output.eventTopic->Pulumi.Output.flatMap(({resources}) =>
|
|
664
|
+
switch resources->Array.get(0) {
|
|
665
|
+
| Some(r) => r.id
|
|
666
|
+
| None => Pulumi.Output.make("")
|
|
667
|
+
}
|
|
668
|
+
)
|
|
669
|
+
{
|
|
670
|
+
specModule: info.specModule,
|
|
671
|
+
mappingsModule: info.mappingsModule,
|
|
672
|
+
eventTopicArn,
|
|
673
|
+
aggregateNames: output.aggregateNames,
|
|
674
|
+
}
|
|
675
|
+
})
|
|
676
|
+
} else {
|
|
677
|
+
[]
|
|
678
|
+
}
|
|
679
|
+
registerEventCollectorContext(
|
|
680
|
+
~componentName=ecName,
|
|
681
|
+
~context={
|
|
682
|
+
pluginDefinitionJson,
|
|
683
|
+
extensionPoints: extensionPointEntries,
|
|
684
|
+
connectExtension,
|
|
685
|
+
extensions,
|
|
686
|
+
pluginExtensionPointCmdTopicUrl,
|
|
687
|
+
publishToAggregates: mergedAggregateUrls,
|
|
688
|
+
readModelQueueUrls,
|
|
689
|
+
readModelNamesForSourceName,
|
|
690
|
+
},
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
eventCollector->PluginRuntimeBuilder.forPluginEventCollector(
|
|
694
|
+
~handler,
|
|
695
|
+
~eventTopics,
|
|
696
|
+
~resources,
|
|
697
|
+
)
|
|
698
|
+
|
|
699
|
+
let _ = switch (eventCollector->Component.outputs).resources->Array.get(0) {
|
|
700
|
+
| Some(r) => r.urn->Pulumi.Output.apply(urn => pluginDefinition.eventCollector = urn)
|
|
701
|
+
| None => Pulumi.Output.make()
|
|
702
|
+
}
|
|
703
|
+
})
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
// ---------------------------------------------------------------------------
|
|
709
|
+
// Shared schema type and plugin-built hook — re-exported from Plugin_Callbacks
|
|
710
|
+
// (runtime-safe, no Pulumi imports) for backward compatibility.
|
|
711
|
+
// ---------------------------------------------------------------------------
|
|
712
|
+
include Plugin_BuiltHook
|
|
713
|
+
|
|
714
|
+
// ---------------------------------------------------------------------------
|
|
715
|
+
// Plugin-deployed hook — fires inside Output.apply after all component
|
|
716
|
+
// resources have resolved, providing fully resolved resource data.
|
|
717
|
+
// ---------------------------------------------------------------------------
|
|
718
|
+
type pluginDeployedSubComponent = {
|
|
719
|
+
role: string,
|
|
720
|
+
resources: array<ReventlessInterop.Resource.t>,
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
type pluginDeployedComponent = {
|
|
724
|
+
name: string,
|
|
725
|
+
kind: string,
|
|
726
|
+
schema: pluginDeployedSchema,
|
|
727
|
+
resources: array<ReventlessInterop.Resource.t>,
|
|
728
|
+
subComponents: array<pluginDeployedSubComponent>,
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
type extensionWiring = {
|
|
732
|
+
extensionName: string,
|
|
733
|
+
extensionPointName: string,
|
|
734
|
+
providerPlugin: string,
|
|
735
|
+
providerVersion: string,
|
|
736
|
+
subscriberPlugin: string,
|
|
737
|
+
subscriberVersion: string,
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
type pluginDeployedInfo = {
|
|
741
|
+
name: string,
|
|
742
|
+
version: string,
|
|
743
|
+
environment: string,
|
|
744
|
+
stackName: string,
|
|
745
|
+
deployedAt: string,
|
|
746
|
+
actor: string,
|
|
747
|
+
deploymentId: string,
|
|
748
|
+
kind?: pluginKind,
|
|
749
|
+
displayName?: string,
|
|
750
|
+
vendor?: string,
|
|
751
|
+
architectureType?: architectureType,
|
|
752
|
+
components: array<pluginDeployedComponent>,
|
|
753
|
+
extensionWirings: array<extensionWiring>,
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
let onPluginDeployedHook: ref<option<pluginDeployedInfo => promise<unit>>> = ref(None)
|
|
757
|
+
|
|
758
|
+
let registerOnPluginDeployed = (hook: pluginDeployedInfo => promise<unit>) => {
|
|
759
|
+
onPluginDeployedHook.contents = Some(hook)
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
let clearOnPluginDeployed = () => {
|
|
763
|
+
onPluginDeployedHook.contents = None
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// ---------------------------------------------------------------------------
|
|
767
|
+
// Platform-deployed hook — fires after deployPlatform / makePlatform
|
|
768
|
+
// completes, providing platform-level metadata with resolved values.
|
|
769
|
+
// ---------------------------------------------------------------------------
|
|
770
|
+
type platformDeployedInfo = {
|
|
771
|
+
name: string,
|
|
772
|
+
environment: string,
|
|
773
|
+
region: string,
|
|
774
|
+
// Domain API — handles application mutations from plugin stacks.
|
|
775
|
+
domainApiEndpoint: string,
|
|
776
|
+
domainApiRoleArn: string,
|
|
777
|
+
// Platform API — handles Platform_Sync* and admin mutations.
|
|
778
|
+
// Equals domainApiEndpoint/domainApiRoleArn in unified (non-split) mode.
|
|
779
|
+
platformApiEndpoint: string,
|
|
780
|
+
platformApiRoleArn: string,
|
|
781
|
+
adminResources: array<ReventlessInterop.Resource.t>,
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
let onPlatformDeployedHook: ref<option<platformDeployedInfo => unit>> = ref(None)
|
|
785
|
+
|
|
786
|
+
let lastPlatformDeployedInfo: ref<option<platformDeployedInfo>> = ref(None)
|
|
787
|
+
|
|
788
|
+
let registerOnPlatformDeployed = (hook: platformDeployedInfo => unit) => {
|
|
789
|
+
onPlatformDeployedHook.contents = Some(hook)
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
let firePlatformDeployedHook = (info: platformDeployedInfo) => {
|
|
793
|
+
lastPlatformDeployedInfo.contents = Some(info)
|
|
794
|
+
switch onPlatformDeployedHook.contents {
|
|
795
|
+
| Some(hook) => hook(info)
|
|
796
|
+
| None => ()
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
let replayPlatformDeployedHook = () => {
|
|
801
|
+
switch (onPlatformDeployedHook.contents, lastPlatformDeployedInfo.contents) {
|
|
802
|
+
| (Some(hook), Some(info)) => hook(info)
|
|
803
|
+
| _ => ()
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
let clearOnPlatformDeployed = () => {
|
|
808
|
+
onPlatformDeployedHook.contents = None
|
|
809
|
+
lastPlatformDeployedInfo.contents = None
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// Resolver error hook has moved to Plugin_ResolverError to keep it on the
|
|
813
|
+
// runtime-safe import chain (CommandGenerator_Callback runs in Lambda; this
|
|
814
|
+
// module pulls in Pulumi for deploy-time helpers).
|
|
815
|
+
|
|
816
|
+
// ---------------------------------------------------------------------------
|
|
817
|
+
// Shared parameter types for hooks that carry structured data.
|
|
818
|
+
// ---------------------------------------------------------------------------
|
|
819
|
+
type mutationKind = Aggregate | Dcb
|
|
820
|
+
|
|
821
|
+
type inboundAppSyncResolverParams = {
|
|
822
|
+
runtime: Runtime.environment<unknown>,
|
|
823
|
+
fieldNames: array<string>,
|
|
824
|
+
externalInputSchemas: array<S.t<unknown>>,
|
|
825
|
+
opts: Pulumi.ComponentResource.options,
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
type dcbAppSyncResolverParams = {
|
|
829
|
+
runtime: Runtime.environment<unknown>,
|
|
830
|
+
fieldNames: array<string>,
|
|
831
|
+
tags: array<string>,
|
|
832
|
+
opts: Pulumi.ComponentResource.options,
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// ---------------------------------------------------------------------------
|
|
836
|
+
// Query field names registry — populated by Plugin_Builder during construct()
|
|
837
|
+
// to map QueryDb component names → plugin-prefixed GraphQL query field names.
|
|
838
|
+
// Read by QueryDbResolvers_GraphQL.make() to align resolver SDL with fragment SDL.
|
|
839
|
+
// ---------------------------------------------------------------------------
|
|
840
|
+
let queryFieldNamesRegistry: dict<Api_Naming.queryNames> = Dict.make()
|
|
841
|
+
|
|
842
|
+
// ---------------------------------------------------------------------------
|
|
843
|
+
// State schema registry — populated alongside queryFieldNamesRegistry so that
|
|
844
|
+
// the in-memory QueryDb resolver can derive the same `serverCapability`
|
|
845
|
+
// (filter / orderBy fields) the FragmentGenerator computed at SDL emission
|
|
846
|
+
// time. Keyed by Spec.name like queryFieldNamesRegistry.
|
|
847
|
+
// ---------------------------------------------------------------------------
|
|
848
|
+
let stateSchemaRegistry: dict<S.t<unknown>> = Dict.make()
|
|
849
|
+
|
|
850
|
+
// ---------------------------------------------------------------------------
|
|
851
|
+
// Aggregate mutation field names registry — populated by Plugin_Builder during
|
|
852
|
+
// construct() to map aggregate Spec.name → plugin-prefixed mutation field names.
|
|
853
|
+
// Read by CommandGenerator_Builder.connect() to provide plugin-prefixed mutation field names.
|
|
854
|
+
// ---------------------------------------------------------------------------
|
|
855
|
+
let aggregateMutationFieldsRegistry: dict<array<string>> = Dict.make()
|
|
856
|
+
|
|
857
|
+
// ---------------------------------------------------------------------------
|
|
858
|
+
// MCP schema registration params type — shared by platformHooks and callers.
|
|
859
|
+
// ---------------------------------------------------------------------------
|
|
860
|
+
type mcpRegistrationParams = {
|
|
861
|
+
pluginName: string,
|
|
862
|
+
mutationEntries: array<ReventlessInfra.Api.mutationSchemaEntry>,
|
|
863
|
+
queryEntries: array<ReventlessInfra.Api.querySchemaEntry>,
|
|
864
|
+
eventLogEntries: array<ReventlessInfra.Api.eventLogSchemaEntry>,
|
|
865
|
+
/** Subscription SDL field strings from the plugin fragment (Sources A, B, C).
|
|
866
|
+
Used by the in-memory platform to register yoga WebSocket subscription resolvers. */
|
|
867
|
+
subscriptionFields: array<string>,
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// Subscription infrastructure hook params — fired after allQueryDbs and allEventTopics
|
|
871
|
+
// are assembled inside builderOutputs. Lets the AWS platform wire StateTopic and
|
|
872
|
+
// EventLogSubscription Lambdas without touching reventless-core.
|
|
873
|
+
type subscriptionInfraParams = {
|
|
874
|
+
allQueryDbs: ReventlessInfra.QueryDb.allOutputs,
|
|
875
|
+
allEventTopics: dict<ReventlessInfra.EventTopic.outputs>,
|
|
876
|
+
eventLogEntries: array<ReventlessInfra.Api.eventLogSchemaEntry>,
|
|
877
|
+
opts: Pulumi.ComponentResource.options,
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// ---------------------------------------------------------------------------
|
|
881
|
+
// platformHooks — platform callbacks and coordination state passed to builder
|
|
882
|
+
// functors as a record.
|
|
883
|
+
//
|
|
884
|
+
// Optional fields (field?): callbacks set at record creation; absent = None.
|
|
885
|
+
// Required ref field: adminExtensionPoints — a mutable cell set by makePlatform
|
|
886
|
+
// after Admin.construct() returns, read by Plugin_Builder to wire the local
|
|
887
|
+
// admin connection path (in-memory). Initialized to an empty dict output;
|
|
888
|
+
// AWS platforms leave it empty and fall back to Interstack instead.
|
|
889
|
+
//
|
|
890
|
+
// `noHooks` is the "no callbacks" default, with adminExtensionPoints starting
|
|
891
|
+
// as an empty dict output.
|
|
892
|
+
// ---------------------------------------------------------------------------
|
|
893
|
+
type platformHooks = {
|
|
894
|
+
// ── In-memory GraphQL mutation registration ────────────────────────────
|
|
895
|
+
// Phase 1: register SDL + resolver stub synchronously.
|
|
896
|
+
// `commandAuthorization` is the `command => permission` function from the
|
|
897
|
+
// aggregate / slice Spec; the resolver evaluates it per-request against
|
|
898
|
+
// `ctx.identity` to enforce per-constructor authorization rules.
|
|
899
|
+
mutationResolverHook?: (
|
|
900
|
+
~kind: mutationKind,
|
|
901
|
+
~fields: array<string>,
|
|
902
|
+
~commandSchema: S.t<unknown>,
|
|
903
|
+
~commandAuthorization: unknown => Reventless.Authorization.permission,
|
|
904
|
+
) => unit,
|
|
905
|
+
// Phase 2: bind generateCommand inside Output.apply.
|
|
906
|
+
mutationBindHook?: (~field: string, ~generateCommand: CommandGenerator.commandGenerator) => unit,
|
|
907
|
+
// InboundTranslationSlice — phase 1: register SDL + stub.
|
|
908
|
+
inboundMutationResolverHook?: (~fieldName: string, ~externalInputSchema: S.t<unknown>) => unit,
|
|
909
|
+
// InboundTranslationSlice — phase 2: bind receive.
|
|
910
|
+
inboundMutationBindReceiveHook?: (~fieldName: string, ~receive: JSON.t => promise<result<array<string>, string>>) => unit,
|
|
911
|
+
// GraphQL type definitions.
|
|
912
|
+
schemaTypeRegistrationHook?: array<string> => unit,
|
|
913
|
+
// Subscription infrastructure (AWS): wire StateTopic + EventLogSubscription Lambdas.
|
|
914
|
+
// Fired inside builderOutputs after allQueryDbs and allEventTopics are assembled.
|
|
915
|
+
subscriptionInfraHook?: subscriptionInfraParams => unit,
|
|
916
|
+
// MCP tools and resources.
|
|
917
|
+
mcpSchemaRegistrationHook?: mcpRegistrationParams => unit,
|
|
918
|
+
// ── AppSync resolver creation (AWS) ───────────────────────────────────
|
|
919
|
+
preResolversSchemaHook?: (~name: string, ~version: string, Reventless.Plugin.apiSchemaFragment) => Pulumi.Output.t<unit>,
|
|
920
|
+
// Admin equivalent of preResolversSchemaHook. Set by AWS platforms to push
|
|
921
|
+
// the admin SDL to the Platform (split mode) or Domain (unified mode) API.
|
|
922
|
+
// Platform_Admin.construct invokes this with a barrier that resolves once the
|
|
923
|
+
// admin AppSync DataSources are ready, and gates admin createResolvers on the
|
|
924
|
+
// returned Output so resolver CreateResolver calls don't race the API-level
|
|
925
|
+
// schema-creation lock. Mirrors Plugin_Builder's schemaPushed gating at
|
|
926
|
+
// Plugin_Builder.res:452-466. The hook owns SDL construction (no fragment
|
|
927
|
+
// arg — the admin SDL is platform-defined, not per-plugin).
|
|
928
|
+
preAdminResolversSchemaHook?: (~adminBarrier: Pulumi.Output.t<unit>) => Pulumi.Output.t<unit>,
|
|
929
|
+
inboundAppSyncResolverHook?: inboundAppSyncResolverParams => unit,
|
|
930
|
+
dcbAppSyncResolverHook?: dcbAppSyncResolverParams => unit,
|
|
931
|
+
// ── Deployment lifecycle (AWS) ─────────────────────────────────────────
|
|
932
|
+
onDcbEventLogCreated?: unknown => unit,
|
|
933
|
+
onDcbCommandTopicCreated?: unknown => unit,
|
|
934
|
+
onDcbSlicesCreated?: unknown => unit,
|
|
935
|
+
onHeartbeatEpChannelAvailable?: (unknown, ~pluginId: string) => unit,
|
|
936
|
+
// ── Admin → Plugin coordination ────────────────────────────────────────
|
|
937
|
+
// Set by makePlatform after Admin.construct(); read by Plugin_Builder to
|
|
938
|
+
// wire the local admin connection path (in-memory). Starts as an empty
|
|
939
|
+
// dict; AWS platforms leave it empty and use Interstack instead.
|
|
940
|
+
adminExtensionPoints: ref<Pulumi.Output.t<dict<ExtensionPoint.outputs>>>,
|
|
941
|
+
// ── Platform context (populated by makePlatform/deployPlugin) ──────────
|
|
942
|
+
// Read by Plugin_Builder.make so app plugins don't need to pass these.
|
|
943
|
+
// api/apiRole stored as hookedValue<unknown> to avoid Pulumi Output Proxy
|
|
944
|
+
// corrupting ReScript option boxing (BS_PRIVATE_NESTED_SOME_NONE sentinel).
|
|
945
|
+
// Cast back via Obj.magic in Plugin_Builder where concrete types are known.
|
|
946
|
+
scheduler: ref<option<Pulumi.Output.t<Scheduler.operations>>>,
|
|
947
|
+
// URN identifying the principal/role the scheduler invokes targets as.
|
|
948
|
+
// Populated by deployment-aware platforms; empty Output on platforms that
|
|
949
|
+
// don't expose this (e.g. in-memory). Threaded through Plugin_Builder into
|
|
950
|
+
// each Task's runtime config so bundled handlers can talk to the
|
|
951
|
+
// underlying scheduler service.
|
|
952
|
+
schedulerRoleUrn: ref<Pulumi.Output.t<string>>,
|
|
953
|
+
api: ref<option<hookedValue<unknown>>>,
|
|
954
|
+
apiRole: ref<option<hookedValue<unknown>>>,
|
|
955
|
+
// Current deploy target, set by deployPlugin before plugin.make() is called.
|
|
956
|
+
// "Domain" (default) → resolvers/schema go to the DomainApi.
|
|
957
|
+
// "Platform" → resolvers/schema go to the PlatformApi.
|
|
958
|
+
// Plugin_Builder captures this synchronously when constructing pluginDefinition
|
|
959
|
+
// to avoid the async-reset race (deployPlugin resets to "Domain" after make()).
|
|
960
|
+
deployTarget: ref<string>,
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// Default hooks — no callbacks, empty platform context.
|
|
964
|
+
let noHooks: platformHooks = {
|
|
965
|
+
adminExtensionPoints: ref(Pulumi.Output.make(Dict.make())),
|
|
966
|
+
scheduler: ref(None),
|
|
967
|
+
schedulerRoleUrn: ref(Pulumi.Output.make("")),
|
|
968
|
+
api: ref(None),
|
|
969
|
+
apiRole: ref(None),
|
|
970
|
+
deployTarget: ref("Domain"),
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
// Functor parameter wrapper for platformHooks.
|
|
974
|
+
module type HooksConfig = {
|
|
975
|
+
let hooks: platformHooks
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// Pre-built HooksConfig with no hooks — use for builders that need no callbacks
|
|
979
|
+
// (e.g. AWS aggregate builders that route via AggregateRuntimeBuilder instead).
|
|
980
|
+
module NoopHooksConfig: HooksConfig = {
|
|
981
|
+
let hooks = noHooks
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// Derive admin-prefixed mutation field names from each aggregate's command schema,
|
|
985
|
+
// drop @noApi variants, register them into aggregateMutationFieldsRegistry (consumed
|
|
986
|
+
// by CommandGenerator_Builder.connect to wire AppSync resolvers in the auto-flow)
|
|
987
|
+
// and fire `mutationResolverHook` so adapter-side SDL stubs get registered
|
|
988
|
+
// synchronously. The matching `mutationSchemaEntry` records — needed to keep the
|
|
989
|
+
// SDL aligned — live in `PluginBaseFragment.pluginAggregateMutationEntries` so the
|
|
990
|
+
// AWS path (which pushes `AdminApi.baseFragment` directly, not Admin.construct's
|
|
991
|
+
// adminFragment) picks them up too. This helper is side-effect only.
|
|
992
|
+
let registerAdminAggregateMutations = (
|
|
993
|
+
type a,
|
|
994
|
+
aggregates: array<module(ReventlessInfra.Aggregate.T with type api = a)>,
|
|
995
|
+
~hooks: platformHooks,
|
|
996
|
+
): unit =>
|
|
997
|
+
aggregates->Array.forEach((module(M: ReventlessInfra.Aggregate.T with type api = a)) => {
|
|
998
|
+
let commandSchema = M.Spec.commandSchema->S.castToUnknown
|
|
999
|
+
if ApiNoApiHelpers.isNoApi(commandSchema) {
|
|
1000
|
+
()
|
|
1001
|
+
} else {
|
|
1002
|
+
let constructorNames = Reventless.DcbTag.extractAllVariantNames(M.Spec.commandSchema)
|
|
1003
|
+
let filteredConstructorNames =
|
|
1004
|
+
ApiNoApiHelpers.filterNoApiVariants(constructorNames, commandSchema)
|
|
1005
|
+
let fieldNames =
|
|
1006
|
+
filteredConstructorNames->Array.map(cname =>
|
|
1007
|
+
Api_Naming.adminField(~name=M.Spec.name ++ "_" ++ cname)
|
|
1008
|
+
)
|
|
1009
|
+
aggregateMutationFieldsRegistry->Dict.set(M.Spec.name, fieldNames)
|
|
1010
|
+
if fieldNames->Array.length > 0 {
|
|
1011
|
+
hooks.mutationResolverHook->Option.forEach(registerResolver =>
|
|
1012
|
+
registerResolver(
|
|
1013
|
+
~kind=Aggregate,
|
|
1014
|
+
~fields=fieldNames,
|
|
1015
|
+
~commandSchema,
|
|
1016
|
+
~commandAuthorization=M.Spec.commandAuthorization->Obj.magic,
|
|
1017
|
+
)
|
|
1018
|
+
)
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
})
|
|
1022
|
+
|
|
1023
|
+
// ---------------------------------------------------------------------------
|
|
1024
|
+
// Interop metadata — computed from builderOutputs at deploy time and stored so
|
|
1025
|
+
// that the plugin's entry-point module can export it as `_interopMeta`.
|
|
1026
|
+
// ---------------------------------------------------------------------------
|
|
1027
|
+
|
|
1028
|
+
// Module-level ref; follows the same mutable-state pattern as `tasksOutputs`
|
|
1029
|
+
// above. Set by Plugin_Builder during construct(); read by user entry-point
|
|
1030
|
+
// code via `getInteropMeta()`.
|
|
1031
|
+
// IMPORTANT: Do NOT use option<Pulumi.Output.t<_>> here. Pulumi.Output.t is a
|
|
1032
|
+
// JavaScript Proxy. ReScript's Caml_option.some() checks for BS_PRIVATE_NESTED_SOME_NONE
|
|
1033
|
+
// on the value — the Proxy intercepts that property access and returns a truthy value,
|
|
1034
|
+
// causing some() to produce the sentinel object {BS_PRIVATE_NESTED_SOME_NONE: 0}
|
|
1035
|
+
// instead of wrapping the actual Output. Use a raw JS null ref to avoid option wrapping.
|
|
1036
|
+
let interopMetaOutput: ref<Pulumi.Output.t<JSON.t>> = ref(%raw(`null`))
|
|
1037
|
+
|
|
1038
|
+
// Derive a field-name union across all tasks (field names present in at least
|
|
1039
|
+
// one task's serialized resolvedOutputs). Optional fields only appear in the
|
|
1040
|
+
// union when they are Some in the source Task.outputs record.
|
|
1041
|
+
let taskFieldUnion = (tasks: dict<Task.outputs>): array<string> => {
|
|
1042
|
+
module SSet = Belt.Set.String
|
|
1043
|
+
tasks
|
|
1044
|
+
->Dict.valuesToArray
|
|
1045
|
+
->Array.reduce(SSet.empty, (acc, taskOutput) => {
|
|
1046
|
+
// Convert Task.outputs → Task.resolvedOutputs using placeholders for nested
|
|
1047
|
+
// Pulumi.Output.t values — we only need presence, not actual string content.
|
|
1048
|
+
// ReScript optional fields must be set to the inner type (not option<_>) in
|
|
1049
|
+
// record literals, so we use a switch to conditionally include them.
|
|
1050
|
+
let resolved: ReventlessInterop.Task.resolvedOutputs =
|
|
1051
|
+
switch (taskOutput.bucketNames, taskOutput.sideEffectSources) {
|
|
1052
|
+
| (None, None) => {name: taskOutput.name}
|
|
1053
|
+
| (Some(_), None) => {name: taskOutput.name, bucketNames: Dict.make()}
|
|
1054
|
+
| (None, Some(src)) => {name: taskOutput.name, sideEffectSources: src}
|
|
1055
|
+
| (Some(_), Some(src)) => {
|
|
1056
|
+
name: taskOutput.name,
|
|
1057
|
+
bucketNames: Dict.make(),
|
|
1058
|
+
sideEffectSources: src,
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
ReventlessInterop.ExportMeta.fieldNamesOf(resolved, ReventlessInterop.Task.resolvedOutputsSchema)
|
|
1062
|
+
->Array.reduce(acc, SSet.add)
|
|
1063
|
+
})
|
|
1064
|
+
->SSet.toArray
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// Build an ExportMeta.t from builderOutputs. Called inside a Pulumi.Output.apply
|
|
1068
|
+
// so all top-level Output.t wrappers in builderOutputs are already resolved.
|
|
1069
|
+
// Nested Output.t values (e.g. inside Task.bucketNames dict values) are still
|
|
1070
|
+
// pending — we use placeholder detection (Some vs None) rather than unwrapping.
|
|
1071
|
+
let toInteropMeta = (outputs: builderOutputs): ReventlessInterop.ExportMeta.t => {
|
|
1072
|
+
// EventMapper field manifest uses minimum required fields (counter presence
|
|
1073
|
+
// cannot be checked without resolving inner Output.t — improved in later phase).
|
|
1074
|
+
let eventMapperMinimal: ReventlessInterop.EventMapper.resolvedOutputs = {
|
|
1075
|
+
name: "",
|
|
1076
|
+
eventCollector: {name: "", resources: []},
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
{
|
|
1080
|
+
version: ReventlessInterop.ExportMeta.version,
|
|
1081
|
+
fields: Dict.fromArray([
|
|
1082
|
+
("tasks", taskFieldUnion(outputs.tasks)),
|
|
1083
|
+
(
|
|
1084
|
+
"eventMappers",
|
|
1085
|
+
ReventlessInterop.ExportMeta.fieldNamesOf(
|
|
1086
|
+
eventMapperMinimal,
|
|
1087
|
+
ReventlessInterop.EventMapper.resolvedOutputsSchema,
|
|
1088
|
+
),
|
|
1089
|
+
),
|
|
1090
|
+
]),
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
// Returns the computed interop meta Output. Call this from the plugin's entry-
|
|
1095
|
+
// point module and export the result as `let _interopMeta = getInteropMeta()`.
|
|
1096
|
+
let getInteropMeta = (): Pulumi.Output.t<JSON.t> => {
|
|
1097
|
+
let v = interopMetaOutput.contents
|
|
1098
|
+
// Use raw null check to avoid option wrapping of the Proxy value.
|
|
1099
|
+
if %raw(`v === null`) {
|
|
1100
|
+
JsError.throwWithMessage("getInteropMeta() called before Plugin_Builder.construct()")
|
|
1101
|
+
} else {
|
|
1102
|
+
v
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// ---------------------------------------------------------------------------
|
|
1107
|
+
// Stack metadata export — environment, region, timestamp, actor, git SHA.
|
|
1108
|
+
// Called from both exportPluginOutputs and exportPlatformOutputs.
|
|
1109
|
+
// ---------------------------------------------------------------------------
|
|
1110
|
+
@val external processEnv: dict<string> = "process.env"
|
|
1111
|
+
|
|
1112
|
+
let exportDeploymentMetadata = () => {
|
|
1113
|
+
let metadata =
|
|
1114
|
+
[
|
|
1115
|
+
("environment", Pulumi.Pulumi.getStackName()),
|
|
1116
|
+
("region", Pulumi.Config.make(Some("aws"))->Pulumi.Config.get("region")->Option.getOr("unknown")),
|
|
1117
|
+
("timestamp", Date.make()->Date.toISOString),
|
|
1118
|
+
("gitSha", processEnv->Dict.get("GITHUB_SHA")->Option.getOr("unknown")),
|
|
1119
|
+
("actor", processEnv->Dict.get("GITHUB_ACTOR")->Option.getOr("unknown")),
|
|
1120
|
+
]
|
|
1121
|
+
->Dict.fromArray
|
|
1122
|
+
Pulumi.Pulumi.export(
|
|
1123
|
+
"deploymentMetadata",
|
|
1124
|
+
metadata->Dict.mapValues(JSON.Encode.string)->JSON.Encode.object->Pulumi.Output.make,
|
|
1125
|
+
)
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
// ---------------------------------------------------------------------------
|
|
1129
|
+
// Serialize individual component output dicts as top-level Pulumi stack exports.
|
|
1130
|
+
// Each component type gets its own export key (e.g. "aggregates", "readModels").
|
|
1131
|
+
// ---------------------------------------------------------------------------
|
|
1132
|
+
|
|
1133
|
+
// Serialize a plain dict of component outputs to JSON. Unlike serializeDictExport,
|
|
1134
|
+
// this does NOT wrap the dict in Pulumi.Output.make (which deeply resolves nested
|
|
1135
|
+
// Outputs and breaks toResolvedOutputs).
|
|
1136
|
+
let serializePlainDictExport = (
|
|
1137
|
+
dict: dict<'outputs>,
|
|
1138
|
+
toResolved: 'outputs => Pulumi.Output.t<'resolved>,
|
|
1139
|
+
schema: S.t<'resolved>,
|
|
1140
|
+
): Pulumi.Output.t<JSON.t> =>
|
|
1141
|
+
dict
|
|
1142
|
+
->Dict.toArray
|
|
1143
|
+
->Array.map(((name, outputs)) =>
|
|
1144
|
+
outputs
|
|
1145
|
+
->toResolved
|
|
1146
|
+
->Pulumi.Output.apply(resolved => (
|
|
1147
|
+
name,
|
|
1148
|
+
resolved->S.reverseConvertToJsonOrThrow(schema),
|
|
1149
|
+
))
|
|
1150
|
+
)
|
|
1151
|
+
->Pulumi.Output.all
|
|
1152
|
+
->Pulumi.Output.apply(pairs => pairs->Dict.fromArray->JSON.Encode.object)
|
|
1153
|
+
|
|
1154
|
+
let serializeDictExport = (
|
|
1155
|
+
dictOutput: Pulumi.Output.t<dict<'outputs>>,
|
|
1156
|
+
toResolved: 'outputs => Pulumi.Output.t<'resolved>,
|
|
1157
|
+
schema: S.t<'resolved>,
|
|
1158
|
+
): Pulumi.Output.t<JSON.t> =>
|
|
1159
|
+
dictOutput->Pulumi.Output.flatMap(dict =>
|
|
1160
|
+
dict
|
|
1161
|
+
->Dict.toArray
|
|
1162
|
+
->Array.map(((name, outputs)) =>
|
|
1163
|
+
outputs
|
|
1164
|
+
->toResolved
|
|
1165
|
+
->Pulumi.Output.apply(resolved => (
|
|
1166
|
+
name,
|
|
1167
|
+
resolved->S.reverseConvertToJsonOrThrow(schema),
|
|
1168
|
+
))
|
|
1169
|
+
)
|
|
1170
|
+
->Pulumi.Output.all
|
|
1171
|
+
->Pulumi.Output.apply(pairs => pairs->Dict.fromArray->JSON.Encode.object)
|
|
1172
|
+
)
|
|
1173
|
+
|
|
1174
|
+
// Serialize tasks dict to JSON array for the "tasks" stack export.
|
|
1175
|
+
let serializeTasksOutputs = (pluginOutputs: Plugin.outputs): Pulumi.Output.t<JSON.t> =>
|
|
1176
|
+
pluginOutputs.tasks->Pulumi.Output.flatMap(tasks =>
|
|
1177
|
+
tasks
|
|
1178
|
+
->Dict.valuesToArray
|
|
1179
|
+
->Array.map(task =>
|
|
1180
|
+
task
|
|
1181
|
+
->Task.toResolvedOutputs
|
|
1182
|
+
->Pulumi.Output.apply(resolved =>
|
|
1183
|
+
resolved->S.reverseConvertToJsonOrThrow(ReventlessInterop.Task.resolvedOutputsSchema)
|
|
1184
|
+
)
|
|
1185
|
+
)
|
|
1186
|
+
->Pulumi.Output.all
|
|
1187
|
+
->Pulumi.Output.apply(arr => arr->JSON.Encode.array)
|
|
1188
|
+
)
|
|
1189
|
+
|
|
1190
|
+
// Serialize event mappers from aggregates to JSON array for the "eventMappers" stack export.
|
|
1191
|
+
let serializeEventMappersOutputs = (pluginOutputs: Plugin.outputs): Pulumi.Output.t<JSON.t> =>
|
|
1192
|
+
pluginOutputs.aggregates->Pulumi.Output.flatMap(aggregates =>
|
|
1193
|
+
aggregates
|
|
1194
|
+
->Dict.valuesToArray
|
|
1195
|
+
->Array.filterMap((agg: Aggregate.outputs) =>
|
|
1196
|
+
agg.eventMapper->Option.map(eventMapperOutput =>
|
|
1197
|
+
eventMapperOutput->Pulumi.Output.flatMap(em =>
|
|
1198
|
+
em
|
|
1199
|
+
->EventMapper.toResolvedOutputs
|
|
1200
|
+
->Pulumi.Output.apply(resolved =>
|
|
1201
|
+
resolved->S.reverseConvertToJsonOrThrow(
|
|
1202
|
+
ReventlessInterop.EventMapper.resolvedOutputsSchema,
|
|
1203
|
+
)
|
|
1204
|
+
)
|
|
1205
|
+
)
|
|
1206
|
+
)
|
|
1207
|
+
)
|
|
1208
|
+
->Pulumi.Output.all
|
|
1209
|
+
->Pulumi.Output.apply(arr => arr->JSON.Encode.array)
|
|
1210
|
+
)
|
|
1211
|
+
|
|
1212
|
+
// Export platform admin component outputs as individual top-level Pulumi stack exports.
|
|
1213
|
+
// Follows the same serialization pattern as exportPluginOutputs.
|
|
1214
|
+
// Only exports non-empty component dicts to avoid cluttering stack output with empty values.
|
|
1215
|
+
let exportPlatformOutputs = (
|
|
1216
|
+
~extensionPointsOutputs: Pulumi.Output.t<array<ExtensionPoint.outputs>>,
|
|
1217
|
+
~aggregatesOutputs: dict<Aggregate.outputs>,
|
|
1218
|
+
~readModelsOutputs: dict<ReadModel.outputs>,
|
|
1219
|
+
~dcbEventLogOutputs: option<DcbEventLog.outputs>,
|
|
1220
|
+
~stateChangeSlicesOutputs: dict<StateChangeSlice.outputs>,
|
|
1221
|
+
~stateViewSlicesOutputs: dict<StateViewSlice.outputs>,
|
|
1222
|
+
~automationSlicesOutputs: dict<AutomationSlice.outputs>,
|
|
1223
|
+
~outboundTranslationSlicesOutputs: dict<OutboundTranslationSlice.outputs>,
|
|
1224
|
+
~inboundTranslationSlicesOutputs: dict<InboundTranslationSlice.outputs>,
|
|
1225
|
+
) => {
|
|
1226
|
+
// Stack metadata
|
|
1227
|
+
exportDeploymentMetadata()
|
|
1228
|
+
|
|
1229
|
+
// Extension points — always exported (the admin's primary component output)
|
|
1230
|
+
Pulumi.Pulumi.export(
|
|
1231
|
+
"extensionPoints",
|
|
1232
|
+
extensionPointsOutputs->Pulumi.Output.flatMap(eps =>
|
|
1233
|
+
eps
|
|
1234
|
+
->Array.map(ep =>
|
|
1235
|
+
ep
|
|
1236
|
+
->ExtensionPoint.toResolvedOutputs
|
|
1237
|
+
->Pulumi.Output.apply(resolved => (
|
|
1238
|
+
ep.name,
|
|
1239
|
+
resolved->S.reverseConvertToJsonOrThrow(
|
|
1240
|
+
ReventlessInterop.ExtensionPoint.resolvedOutputsSchema,
|
|
1241
|
+
),
|
|
1242
|
+
))
|
|
1243
|
+
)
|
|
1244
|
+
->Pulumi.Output.all
|
|
1245
|
+
->Pulumi.Output.apply(pairs => pairs->Dict.fromArray->JSON.Encode.object)
|
|
1246
|
+
),
|
|
1247
|
+
)
|
|
1248
|
+
|
|
1249
|
+
// Aggregates — only export if non-empty
|
|
1250
|
+
if aggregatesOutputs->Dict.keysToArray->Array.length > 0 {
|
|
1251
|
+
Pulumi.Pulumi.export(
|
|
1252
|
+
"aggregates",
|
|
1253
|
+
serializePlainDictExport(
|
|
1254
|
+
aggregatesOutputs,
|
|
1255
|
+
Aggregate.toResolvedOutputs,
|
|
1256
|
+
ReventlessInterop.Aggregate.resolvedOutputsSchema,
|
|
1257
|
+
),
|
|
1258
|
+
)
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
// ReadModels — only export if non-empty
|
|
1262
|
+
if readModelsOutputs->Dict.keysToArray->Array.length > 0 {
|
|
1263
|
+
Pulumi.Pulumi.export(
|
|
1264
|
+
"readModels",
|
|
1265
|
+
serializePlainDictExport(
|
|
1266
|
+
readModelsOutputs,
|
|
1267
|
+
ReadModel.toResolvedOutputs,
|
|
1268
|
+
ReventlessInterop.ReadModel.resolvedOutputsSchema,
|
|
1269
|
+
),
|
|
1270
|
+
)
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
// DCB event log — only export if present
|
|
1274
|
+
switch dcbEventLogOutputs {
|
|
1275
|
+
| Some(dcbOutputs) =>
|
|
1276
|
+
Pulumi.Pulumi.export(
|
|
1277
|
+
"dcbEventLog",
|
|
1278
|
+
dcbOutputs
|
|
1279
|
+
->DcbEventLog.toResolvedOutputs
|
|
1280
|
+
->Pulumi.Output.apply(resolved =>
|
|
1281
|
+
resolved->S.reverseConvertToJsonOrThrow(
|
|
1282
|
+
ReventlessInterop.DcbEventLog.resolvedOutputsSchema,
|
|
1283
|
+
)
|
|
1284
|
+
),
|
|
1285
|
+
)
|
|
1286
|
+
| None => ()
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
// DCB slices — only export non-empty dicts
|
|
1290
|
+
if stateChangeSlicesOutputs->Dict.keysToArray->Array.length > 0 {
|
|
1291
|
+
Pulumi.Pulumi.export(
|
|
1292
|
+
"stateChangeSlices",
|
|
1293
|
+
serializePlainDictExport(
|
|
1294
|
+
stateChangeSlicesOutputs,
|
|
1295
|
+
StateChangeSlice.toResolvedOutputs,
|
|
1296
|
+
ReventlessInterop.StateChangeSlice.resolvedOutputsSchema,
|
|
1297
|
+
),
|
|
1298
|
+
)
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
if stateViewSlicesOutputs->Dict.keysToArray->Array.length > 0 {
|
|
1302
|
+
Pulumi.Pulumi.export(
|
|
1303
|
+
"stateViewSlices",
|
|
1304
|
+
serializePlainDictExport(
|
|
1305
|
+
stateViewSlicesOutputs,
|
|
1306
|
+
StateViewSlice.toResolvedOutputs,
|
|
1307
|
+
ReventlessInterop.StateViewSlice.resolvedOutputsSchema,
|
|
1308
|
+
),
|
|
1309
|
+
)
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
if automationSlicesOutputs->Dict.keysToArray->Array.length > 0 {
|
|
1313
|
+
Pulumi.Pulumi.export(
|
|
1314
|
+
"automationSlices",
|
|
1315
|
+
serializePlainDictExport(
|
|
1316
|
+
automationSlicesOutputs,
|
|
1317
|
+
AutomationSlice.toResolvedOutputs,
|
|
1318
|
+
ReventlessInterop.AutomationSlice.resolvedOutputsSchema,
|
|
1319
|
+
),
|
|
1320
|
+
)
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
if outboundTranslationSlicesOutputs->Dict.keysToArray->Array.length > 0 {
|
|
1324
|
+
Pulumi.Pulumi.export(
|
|
1325
|
+
"outboundTranslationSlices",
|
|
1326
|
+
serializePlainDictExport(
|
|
1327
|
+
outboundTranslationSlicesOutputs,
|
|
1328
|
+
OutboundTranslationSlice.toResolvedOutputs,
|
|
1329
|
+
ReventlessInterop.OutboundTranslationSlice.resolvedOutputsSchema,
|
|
1330
|
+
),
|
|
1331
|
+
)
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
if inboundTranslationSlicesOutputs->Dict.keysToArray->Array.length > 0 {
|
|
1335
|
+
Pulumi.Pulumi.export(
|
|
1336
|
+
"inboundTranslationSlices",
|
|
1337
|
+
serializePlainDictExport(
|
|
1338
|
+
inboundTranslationSlicesOutputs,
|
|
1339
|
+
InboundTranslationSlice.toResolvedOutputs,
|
|
1340
|
+
ReventlessInterop.InboundTranslationSlice.resolvedOutputsSchema,
|
|
1341
|
+
),
|
|
1342
|
+
)
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
// Export all plugin outputs as individual top-level Pulumi stack exports.
|
|
1347
|
+
// Each component type is its own export key for flat, readable `pulumi stack output`.
|
|
1348
|
+
let exportPluginOutputs = (pluginOutputs: Plugin.outputs) => {
|
|
1349
|
+
// Stack metadata
|
|
1350
|
+
exportDeploymentMetadata()
|
|
1351
|
+
|
|
1352
|
+
// Scalar fields
|
|
1353
|
+
Pulumi.Pulumi.export("id", pluginOutputs.id->Pulumi.Output.apply(v => v->JSON.Encode.string))
|
|
1354
|
+
Pulumi.Pulumi.export(
|
|
1355
|
+
"version",
|
|
1356
|
+
pluginOutputs.version->Pulumi.Output.apply(v => v->JSON.Encode.string),
|
|
1357
|
+
)
|
|
1358
|
+
|
|
1359
|
+
// Component dicts
|
|
1360
|
+
Pulumi.Pulumi.export(
|
|
1361
|
+
"aggregates",
|
|
1362
|
+
serializeDictExport(
|
|
1363
|
+
pluginOutputs.aggregates,
|
|
1364
|
+
Aggregate.toResolvedOutputs,
|
|
1365
|
+
ReventlessInterop.Aggregate.resolvedOutputsSchema,
|
|
1366
|
+
),
|
|
1367
|
+
)
|
|
1368
|
+
Pulumi.Pulumi.export(
|
|
1369
|
+
"readModels",
|
|
1370
|
+
serializeDictExport(
|
|
1371
|
+
pluginOutputs.readModels,
|
|
1372
|
+
ReadModel.toResolvedOutputs,
|
|
1373
|
+
ReventlessInterop.ReadModel.resolvedOutputsSchema,
|
|
1374
|
+
),
|
|
1375
|
+
)
|
|
1376
|
+
Pulumi.Pulumi.export(
|
|
1377
|
+
"extensionPoints",
|
|
1378
|
+
serializeDictExport(
|
|
1379
|
+
pluginOutputs.extensionPoints,
|
|
1380
|
+
ExtensionPoint.toResolvedOutputs,
|
|
1381
|
+
ReventlessInterop.ExtensionPoint.resolvedOutputsSchema,
|
|
1382
|
+
),
|
|
1383
|
+
)
|
|
1384
|
+
Pulumi.Pulumi.export(
|
|
1385
|
+
"stateChangeSlices",
|
|
1386
|
+
serializeDictExport(
|
|
1387
|
+
pluginOutputs.stateChangeSlices,
|
|
1388
|
+
StateChangeSlice.toResolvedOutputs,
|
|
1389
|
+
ReventlessInterop.StateChangeSlice.resolvedOutputsSchema,
|
|
1390
|
+
),
|
|
1391
|
+
)
|
|
1392
|
+
Pulumi.Pulumi.export(
|
|
1393
|
+
"stateViewSlices",
|
|
1394
|
+
serializeDictExport(
|
|
1395
|
+
pluginOutputs.stateViewSlices,
|
|
1396
|
+
StateViewSlice.toResolvedOutputs,
|
|
1397
|
+
ReventlessInterop.StateViewSlice.resolvedOutputsSchema,
|
|
1398
|
+
),
|
|
1399
|
+
)
|
|
1400
|
+
Pulumi.Pulumi.export(
|
|
1401
|
+
"automationSlices",
|
|
1402
|
+
serializeDictExport(
|
|
1403
|
+
pluginOutputs.automationSlices,
|
|
1404
|
+
AutomationSlice.toResolvedOutputs,
|
|
1405
|
+
ReventlessInterop.AutomationSlice.resolvedOutputsSchema,
|
|
1406
|
+
),
|
|
1407
|
+
)
|
|
1408
|
+
Pulumi.Pulumi.export(
|
|
1409
|
+
"outboundTranslationSlices",
|
|
1410
|
+
serializeDictExport(
|
|
1411
|
+
pluginOutputs.outboundTranslationSlices,
|
|
1412
|
+
OutboundTranslationSlice.toResolvedOutputs,
|
|
1413
|
+
ReventlessInterop.OutboundTranslationSlice.resolvedOutputsSchema,
|
|
1414
|
+
),
|
|
1415
|
+
)
|
|
1416
|
+
Pulumi.Pulumi.export(
|
|
1417
|
+
"inboundTranslationSlices",
|
|
1418
|
+
serializeDictExport(
|
|
1419
|
+
pluginOutputs.inboundTranslationSlices,
|
|
1420
|
+
InboundTranslationSlice.toResolvedOutputs,
|
|
1421
|
+
ReventlessInterop.InboundTranslationSlice.resolvedOutputsSchema,
|
|
1422
|
+
),
|
|
1423
|
+
)
|
|
1424
|
+
|
|
1425
|
+
// DCB event log — single optional value
|
|
1426
|
+
Pulumi.Pulumi.export(
|
|
1427
|
+
"dcbEventLog",
|
|
1428
|
+
pluginOutputs.dcbEventLog->Pulumi.Output.flatMap(opt =>
|
|
1429
|
+
switch opt {
|
|
1430
|
+
| Some(dcbOutputs) =>
|
|
1431
|
+
dcbOutputs
|
|
1432
|
+
->DcbEventLog.toResolvedOutputs
|
|
1433
|
+
->Pulumi.Output.apply(resolved =>
|
|
1434
|
+
resolved->S.reverseConvertToJsonOrThrow(
|
|
1435
|
+
ReventlessInterop.DcbEventLog.resolvedOutputsSchema,
|
|
1436
|
+
)
|
|
1437
|
+
)
|
|
1438
|
+
| None => Pulumi.Output.make(Obj.magic(JSON.Encode.null))
|
|
1439
|
+
}
|
|
1440
|
+
),
|
|
1441
|
+
)
|
|
1442
|
+
|
|
1443
|
+
// Array exports
|
|
1444
|
+
Pulumi.Pulumi.export("tasks", serializeTasksOutputs(pluginOutputs))
|
|
1445
|
+
Pulumi.Pulumi.export("eventMappers", serializeEventMappersOutputs(pluginOutputs))
|
|
1446
|
+
|
|
1447
|
+
// Fire onPluginDeployed hook with fully resolved resource data.
|
|
1448
|
+
switch onPluginDeployedHook.contents {
|
|
1449
|
+
| Some(hook) =>
|
|
1450
|
+
// Read deployment provenance from env vars synchronously (before Output.apply).
|
|
1451
|
+
let actor =
|
|
1452
|
+
processEnv
|
|
1453
|
+
->Dict.get("GITHUB_ACTOR")
|
|
1454
|
+
->Option.orElse(processEnv->Dict.get("CI_COMMIT_AUTHOR"))
|
|
1455
|
+
->Option.orElse(processEnv->Dict.get("USER"))
|
|
1456
|
+
->Option.getOr("local")
|
|
1457
|
+
let deploymentId =
|
|
1458
|
+
processEnv
|
|
1459
|
+
->Dict.get("GITHUB_SHA")
|
|
1460
|
+
->Option.orElse(processEnv->Dict.get("CI_COMMIT_SHA"))
|
|
1461
|
+
->Option.getOr(Date.make()->Date.toISOString)
|
|
1462
|
+
let schemaFor = name =>
|
|
1463
|
+
componentSchemaRegistry->Dict.get(name)->Option.getOr({})
|
|
1464
|
+
let resolveAggregates = pluginOutputs.aggregates->Pulumi.Output.flatMap(aggs =>
|
|
1465
|
+
aggs
|
|
1466
|
+
->Dict.toArray
|
|
1467
|
+
->Array.map(((name, outputs)) =>
|
|
1468
|
+
outputs
|
|
1469
|
+
->Aggregate.toResolvedOutputs
|
|
1470
|
+
->Pulumi.Output.apply((resolved: ReventlessInterop.Aggregate.resolvedOutputs) => {
|
|
1471
|
+
let component: pluginDeployedComponent = {
|
|
1472
|
+
name,
|
|
1473
|
+
kind: "Aggregate",
|
|
1474
|
+
schema: schemaFor(name),
|
|
1475
|
+
resources: [],
|
|
1476
|
+
subComponents: [
|
|
1477
|
+
{role: "commandGenerator", resources: resolved.commandGenerator.resources},
|
|
1478
|
+
{role: "commandTopic", resources: resolved.commandTopic.resources},
|
|
1479
|
+
{role: "eventLog", resources: resolved.eventLog.resources},
|
|
1480
|
+
{role: "eventTopic", resources: resolved.eventLog.eventTopic.resources},
|
|
1481
|
+
],
|
|
1482
|
+
}
|
|
1483
|
+
component
|
|
1484
|
+
})
|
|
1485
|
+
)
|
|
1486
|
+
->Pulumi.Output.all
|
|
1487
|
+
)
|
|
1488
|
+
|
|
1489
|
+
let resolveReadModels = pluginOutputs.readModels->Pulumi.Output.flatMap(rms =>
|
|
1490
|
+
rms
|
|
1491
|
+
->Dict.toArray
|
|
1492
|
+
->Array.map(((name, outputs)) =>
|
|
1493
|
+
outputs
|
|
1494
|
+
->ReadModel.toResolvedOutputs
|
|
1495
|
+
->Pulumi.Output.apply((resolved: ReventlessInterop.ReadModel.resolvedOutputs) => {
|
|
1496
|
+
let component: pluginDeployedComponent = {
|
|
1497
|
+
name,
|
|
1498
|
+
kind: "ReadModel",
|
|
1499
|
+
schema: schemaFor(name),
|
|
1500
|
+
resources: [],
|
|
1501
|
+
subComponents: [{role: "queryDb", resources: resolved.queryDb.resources}],
|
|
1502
|
+
}
|
|
1503
|
+
component
|
|
1504
|
+
})
|
|
1505
|
+
)
|
|
1506
|
+
->Pulumi.Output.all
|
|
1507
|
+
)
|
|
1508
|
+
|
|
1509
|
+
let resolveExtensionPoints = pluginOutputs.extensionPoints->Pulumi.Output.flatMap(eps =>
|
|
1510
|
+
eps
|
|
1511
|
+
->Dict.toArray
|
|
1512
|
+
->Array.map(((name, outputs)) =>
|
|
1513
|
+
outputs
|
|
1514
|
+
->ExtensionPoint.toResolvedOutputs
|
|
1515
|
+
->Pulumi.Output.apply((resolved: ReventlessInterop.ExtensionPoint.resolvedOutputs) => {
|
|
1516
|
+
let component: pluginDeployedComponent = {
|
|
1517
|
+
name,
|
|
1518
|
+
kind: "ExtensionPoint",
|
|
1519
|
+
schema: schemaFor(name),
|
|
1520
|
+
resources: [],
|
|
1521
|
+
subComponents: [
|
|
1522
|
+
{role: "commandTopic", resources: resolved.commandTopic.resources},
|
|
1523
|
+
{role: "eventTopic", resources: resolved.eventTopic.resources},
|
|
1524
|
+
],
|
|
1525
|
+
}
|
|
1526
|
+
component
|
|
1527
|
+
})
|
|
1528
|
+
)
|
|
1529
|
+
->Pulumi.Output.all
|
|
1530
|
+
)
|
|
1531
|
+
|
|
1532
|
+
let resolveStateChangeSlices = pluginOutputs.stateChangeSlices->Pulumi.Output.flatMap(slices =>
|
|
1533
|
+
slices
|
|
1534
|
+
->Dict.toArray
|
|
1535
|
+
->Array.map(((name, outputs)) =>
|
|
1536
|
+
outputs
|
|
1537
|
+
->StateChangeSlice.toResolvedOutputs
|
|
1538
|
+
->Pulumi.Output.apply((resolved: ReventlessInterop.StateChangeSlice.resolvedOutputs) => {
|
|
1539
|
+
let component: pluginDeployedComponent = {
|
|
1540
|
+
name,
|
|
1541
|
+
kind: "StateChangeSlice",
|
|
1542
|
+
schema: schemaFor(name),
|
|
1543
|
+
resources: resolved.resources,
|
|
1544
|
+
subComponents: [],
|
|
1545
|
+
}
|
|
1546
|
+
component
|
|
1547
|
+
})
|
|
1548
|
+
)
|
|
1549
|
+
->Pulumi.Output.all
|
|
1550
|
+
)
|
|
1551
|
+
|
|
1552
|
+
let resolveStateViewSlices = pluginOutputs.stateViewSlices->Pulumi.Output.flatMap(slices =>
|
|
1553
|
+
slices
|
|
1554
|
+
->Dict.toArray
|
|
1555
|
+
->Array.map(((name, outputs)) =>
|
|
1556
|
+
outputs
|
|
1557
|
+
->StateViewSlice.toResolvedOutputs
|
|
1558
|
+
->Pulumi.Output.apply((resolved: ReventlessInterop.StateViewSlice.resolvedOutputs) => {
|
|
1559
|
+
let component: pluginDeployedComponent = {
|
|
1560
|
+
name,
|
|
1561
|
+
kind: "StateViewSlice",
|
|
1562
|
+
schema: schemaFor(name),
|
|
1563
|
+
resources: resolved.resources,
|
|
1564
|
+
subComponents: [{role: "queryDb", resources: resolved.queryDb.resources}],
|
|
1565
|
+
}
|
|
1566
|
+
component
|
|
1567
|
+
})
|
|
1568
|
+
)
|
|
1569
|
+
->Pulumi.Output.all
|
|
1570
|
+
)
|
|
1571
|
+
|
|
1572
|
+
let resolveAutomationSlices = pluginOutputs.automationSlices->Pulumi.Output.flatMap(slices =>
|
|
1573
|
+
slices
|
|
1574
|
+
->Dict.toArray
|
|
1575
|
+
->Array.map(((name, outputs)) =>
|
|
1576
|
+
outputs
|
|
1577
|
+
->AutomationSlice.toResolvedOutputs
|
|
1578
|
+
->Pulumi.Output.apply((resolved: ReventlessInterop.AutomationSlice.resolvedOutputs) => {
|
|
1579
|
+
let component: pluginDeployedComponent = {
|
|
1580
|
+
name,
|
|
1581
|
+
kind: "AutomationSlice",
|
|
1582
|
+
schema: schemaFor(name),
|
|
1583
|
+
resources: resolved.resources,
|
|
1584
|
+
subComponents: [{role: "queryDb", resources: resolved.queryDb.resources}],
|
|
1585
|
+
}
|
|
1586
|
+
component
|
|
1587
|
+
})
|
|
1588
|
+
)
|
|
1589
|
+
->Pulumi.Output.all
|
|
1590
|
+
)
|
|
1591
|
+
|
|
1592
|
+
let resolveOutboundTranslationSlices =
|
|
1593
|
+
pluginOutputs.outboundTranslationSlices->Pulumi.Output.flatMap(slices =>
|
|
1594
|
+
slices
|
|
1595
|
+
->Dict.toArray
|
|
1596
|
+
->Array.map(((name, outputs)) =>
|
|
1597
|
+
outputs
|
|
1598
|
+
->OutboundTranslationSlice.toResolvedOutputs
|
|
1599
|
+
->Pulumi.Output.apply(
|
|
1600
|
+
(resolved: ReventlessInterop.OutboundTranslationSlice.resolvedOutputs) => {
|
|
1601
|
+
let component: pluginDeployedComponent = {
|
|
1602
|
+
name,
|
|
1603
|
+
kind: "OutboundTranslationSlice",
|
|
1604
|
+
schema: schemaFor(name),
|
|
1605
|
+
resources: resolved.resources,
|
|
1606
|
+
subComponents: [{role: "queryDb", resources: resolved.queryDb.resources}],
|
|
1607
|
+
}
|
|
1608
|
+
component
|
|
1609
|
+
},
|
|
1610
|
+
)
|
|
1611
|
+
)
|
|
1612
|
+
->Pulumi.Output.all
|
|
1613
|
+
)
|
|
1614
|
+
|
|
1615
|
+
let resolveInboundTranslationSlices =
|
|
1616
|
+
pluginOutputs.inboundTranslationSlices->Pulumi.Output.flatMap(slices =>
|
|
1617
|
+
slices
|
|
1618
|
+
->Dict.toArray
|
|
1619
|
+
->Array.map(((name, outputs)) =>
|
|
1620
|
+
outputs
|
|
1621
|
+
->InboundTranslationSlice.toResolvedOutputs
|
|
1622
|
+
->Pulumi.Output.apply(
|
|
1623
|
+
(resolved: ReventlessInterop.InboundTranslationSlice.resolvedOutputs) => {
|
|
1624
|
+
let component: pluginDeployedComponent = {
|
|
1625
|
+
name,
|
|
1626
|
+
kind: "InboundTranslationSlice",
|
|
1627
|
+
schema: schemaFor(name),
|
|
1628
|
+
resources: resolved.resources,
|
|
1629
|
+
subComponents: [{role: "queryDb", resources: resolved.queryDb.resources}],
|
|
1630
|
+
}
|
|
1631
|
+
component
|
|
1632
|
+
},
|
|
1633
|
+
)
|
|
1634
|
+
)
|
|
1635
|
+
->Pulumi.Output.all
|
|
1636
|
+
)
|
|
1637
|
+
|
|
1638
|
+
let resolveDcbEventLog = pluginOutputs.dcbEventLog->Pulumi.Output.flatMap(opt =>
|
|
1639
|
+
switch opt {
|
|
1640
|
+
| Some(outputs) =>
|
|
1641
|
+
outputs
|
|
1642
|
+
->DcbEventLog.toResolvedOutputs
|
|
1643
|
+
->Pulumi.Output.apply((resolved: ReventlessInterop.DcbEventLog.resolvedOutputs) => {
|
|
1644
|
+
let component: pluginDeployedComponent = {
|
|
1645
|
+
name: "DcbEventLog",
|
|
1646
|
+
kind: "DcbEventLog",
|
|
1647
|
+
schema: schemaFor("DcbEventLog"),
|
|
1648
|
+
resources: resolved.resources,
|
|
1649
|
+
subComponents: [{role: "eventTopic", resources: resolved.eventTopic.resources}],
|
|
1650
|
+
}
|
|
1651
|
+
[component]
|
|
1652
|
+
})
|
|
1653
|
+
| None => Pulumi.Output.make([])
|
|
1654
|
+
}
|
|
1655
|
+
)
|
|
1656
|
+
|
|
1657
|
+
// Resolve extension wiring metadata.
|
|
1658
|
+
let resolveExtensionWirings = pluginOutputs.extensions->Pulumi.Output.flatMap(exts =>
|
|
1659
|
+
pluginOutputs.id->Pulumi.Output.apply(id => {
|
|
1660
|
+
let pluginName = id->String.split("@")->Array.getUnsafe(0)
|
|
1661
|
+
let pluginVersion = id->String.split("@")->Array.getUnsafe(1)
|
|
1662
|
+
exts
|
|
1663
|
+
->Dict.valuesToArray
|
|
1664
|
+
->Array.map((ext: ReventlessInfra.Extension.outputs) => {
|
|
1665
|
+
// Derive provider plugin name from the extension point name
|
|
1666
|
+
// (e.g. "Catalog.Products" → "Catalog").
|
|
1667
|
+
let providerPlugin =
|
|
1668
|
+
ext.extensionPointName->String.split(".")->Array.getUnsafe(0)
|
|
1669
|
+
let wiring: extensionWiring = {
|
|
1670
|
+
extensionName: ext.name,
|
|
1671
|
+
extensionPointName: ext.extensionPointName,
|
|
1672
|
+
providerPlugin,
|
|
1673
|
+
providerVersion: "",
|
|
1674
|
+
subscriberPlugin: pluginName,
|
|
1675
|
+
subscriberVersion: pluginVersion,
|
|
1676
|
+
}
|
|
1677
|
+
wiring
|
|
1678
|
+
})
|
|
1679
|
+
})
|
|
1680
|
+
)
|
|
1681
|
+
|
|
1682
|
+
// Collect all resolved components, fire the hook, and export the resulting
|
|
1683
|
+
// Output<unit> at top level so Pulumi blocks on the hook's Promise.
|
|
1684
|
+
let hookOutput =
|
|
1685
|
+
(
|
|
1686
|
+
pluginOutputs.id,
|
|
1687
|
+
pluginOutputs.version,
|
|
1688
|
+
resolveAggregates,
|
|
1689
|
+
resolveReadModels,
|
|
1690
|
+
resolveExtensionPoints,
|
|
1691
|
+
resolveStateChangeSlices,
|
|
1692
|
+
)
|
|
1693
|
+
->Pulumi.Output.all6
|
|
1694
|
+
->Pulumi.Output.flatMap(((id, version, aggs, rms, eps, scs)) =>
|
|
1695
|
+
(
|
|
1696
|
+
resolveStateViewSlices,
|
|
1697
|
+
resolveAutomationSlices,
|
|
1698
|
+
resolveOutboundTranslationSlices,
|
|
1699
|
+
resolveInboundTranslationSlices,
|
|
1700
|
+
resolveDcbEventLog,
|
|
1701
|
+
resolveExtensionWirings,
|
|
1702
|
+
)
|
|
1703
|
+
->Pulumi.Output.all6
|
|
1704
|
+
->Pulumi.Output.apply(((svs, autos, ots, its, dcb, wirings)) => {
|
|
1705
|
+
let name = id->String.split("@")->Array.getUnsafe(0)
|
|
1706
|
+
let meta = pluginMetadataRegistry.contents
|
|
1707
|
+
let info: pluginDeployedInfo = {
|
|
1708
|
+
name,
|
|
1709
|
+
version,
|
|
1710
|
+
environment: Pulumi.Pulumi.getStackName(),
|
|
1711
|
+
stackName: Pulumi.Pulumi.getStackName(),
|
|
1712
|
+
deployedAt: Date.make()->Date.toISOString,
|
|
1713
|
+
actor,
|
|
1714
|
+
deploymentId,
|
|
1715
|
+
kind: ?meta->Option.flatMap(m => m.kind),
|
|
1716
|
+
displayName: ?meta->Option.flatMap(m => m.displayName),
|
|
1717
|
+
vendor: ?meta->Option.flatMap(m => m.vendor),
|
|
1718
|
+
architectureType: ?meta->Option.flatMap(m => m.architectureType),
|
|
1719
|
+
components: Array.flat([aggs, rms, eps, scs, svs, autos, ots, its, dcb]),
|
|
1720
|
+
extensionWirings: wirings,
|
|
1721
|
+
}
|
|
1722
|
+
hook(info)
|
|
1723
|
+
})
|
|
1724
|
+
)
|
|
1725
|
+
->Pulumi.Output.flatMap(p => p->Pulumi.Output.fromPromise)
|
|
1726
|
+
|
|
1727
|
+
Pulumi.Pulumi.export("_pluginDeployedSync", hookOutput)
|
|
1728
|
+
| None => ()
|
|
1729
|
+
}
|
|
1730
|
+
}
|