@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,412 @@
|
|
|
1
|
+
module type T = {
|
|
2
|
+
module Spec: Reventless.StateChangeSlice.Spec
|
|
3
|
+
module Behavior: Reventless.StateChangeSlice.Behavior with module Spec := Spec
|
|
4
|
+
let handleCommands: (
|
|
5
|
+
~tagKeysByEventType: Dict.t<array<string>>=?,
|
|
6
|
+
~crossPartitionTagKeys: array<string>=?,
|
|
7
|
+
DcbEventLog.operations,
|
|
8
|
+
Stream.t<
|
|
9
|
+
CommandTopic.topicItem<Message.command'<Reventless.Id.String.t, Spec.command>>,
|
|
10
|
+
string,
|
|
11
|
+
unit,
|
|
12
|
+
>,
|
|
13
|
+
) => Effect.t<array<result<string, string>>, string, unit>
|
|
14
|
+
/** Flushes this slice's in-process decision-model projection cache. The cache
|
|
15
|
+
is keyed on durable storage positions, so it must be cleared whenever the
|
|
16
|
+
backing store is reset out-of-band (test isolation; operational flush). */
|
|
17
|
+
let resetCache: unit => unit
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module Make = (
|
|
21
|
+
Spec: Reventless.StateChangeSlice.Spec,
|
|
22
|
+
Behavior: Reventless.StateChangeSlice.Behavior with module Spec := Spec,
|
|
23
|
+
): (T with module Spec = Spec and module Behavior := Behavior) => {
|
|
24
|
+
module Spec = Spec
|
|
25
|
+
module Behavior = Behavior
|
|
26
|
+
|
|
27
|
+
let comp = `StateChangeSlice(${Spec.name})`
|
|
28
|
+
|
|
29
|
+
let decoder = Reventless.DcbDecode.makeDecoder(Spec.consumedEventSchema)
|
|
30
|
+
let queryEventTypes = decoder.eventTypes
|
|
31
|
+
|
|
32
|
+
let encodeEvent = (
|
|
33
|
+
~parentMeta: Message.meta,
|
|
34
|
+
~tagKeysByEventType: Dict.t<array<string>>,
|
|
35
|
+
event: Spec.event,
|
|
36
|
+
): ReventlessInfra.DcbEventLog.rawEvent => {
|
|
37
|
+
let json = event->JSON.stringifyAny->Option.getOrThrow->JSON.parseOrThrow
|
|
38
|
+
let (eventType, data) = json->Message.splitMessage
|
|
39
|
+
// Use `extractTagsExpanded` (not `extractTags`) so per-element tags on
|
|
40
|
+
// `array<string>` fields are emitted — e.g. OrderPlaced's
|
|
41
|
+
// `productIds: array<string>` produces one tag per productId rather than
|
|
42
|
+
// dropping the field entirely. The stored event then carries each productId
|
|
43
|
+
// as a GSI tag attribute, so composite/GSI readers and the per-productId
|
|
44
|
+
// partition (for events partitioned by productId) resolve correctly.
|
|
45
|
+
//
|
|
46
|
+
// Note: a productId here is a *secondary* tag on OrderPlaced (the partition
|
|
47
|
+
// tag is orderId). The DynamoDB adapter fences a tag only when it is the
|
|
48
|
+
// event's partition tag, so emitting productId tags does NOT bump
|
|
49
|
+
// `fence#productId:<x>` — that fence is owned by productId-partitioned events
|
|
50
|
+
// (e.g. CatalogProductSynced). This keeps read-scope and fence-scope aligned;
|
|
51
|
+
// see `docs/analysis/dcb-consistency-check-issues.md`.
|
|
52
|
+
// Keep only the keys this event type is *indexed* by (`tagKeysByEventType`,
|
|
53
|
+
// which Dcb_Builder threads from the inferred scope — or the full annotated tag
|
|
54
|
+
// set on an ambiguous boundary, where this filter is a no-op). A foreign
|
|
55
|
+
// reference key that is payload here (e.g. `categoryId` on `ProductAdded`) is
|
|
56
|
+
// dropped, so the event is never written to that key's GSI and a cross-partition
|
|
57
|
+
// read of it can't sweep up this event. When no entry exists for the type, keep
|
|
58
|
+
// all tags (back-compat for callers that don't thread the map).
|
|
59
|
+
let baseTags = Reventless.DcbTag.extractTagsExpanded(Spec.eventSchema, event)
|
|
60
|
+
let indexedTags = switch tagKeysByEventType->Dict.get(eventType) {
|
|
61
|
+
| Some(keys) => baseTags->Array.filter(t => keys->Array.includes(t.key))
|
|
62
|
+
| None => baseTags
|
|
63
|
+
}
|
|
64
|
+
let tags =
|
|
65
|
+
indexedTags->Array.concat([{Reventless.DcbTag.key: "originatorSlice", value: Spec.name}])
|
|
66
|
+
// Inherit service from the triggering command — the DcbEventLog publish path
|
|
67
|
+
// overrides service to `<name>DcbEventLog` for routing so EventCollector
|
|
68
|
+
// subscriptions still match.
|
|
69
|
+
let meta = Message.deriveMeta(~parent=parentMeta)
|
|
70
|
+
{eventType, data: JSON.Object(data), tags, meta}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Computed once at functor init — used to extract entityId for publishJsonsAndWait outcomes.
|
|
74
|
+
let derivedPartitionTag = Reventless.DcbTag.derivePartitionTag([
|
|
75
|
+
(Spec.name, Behavior.moduleUrl, Spec.eventSchema->S.castToUnknown),
|
|
76
|
+
])
|
|
77
|
+
|
|
78
|
+
// Extracts the entity id of a read event from its own tags, for logging
|
|
79
|
+
// which events the decision model was built from. Prefers this slice's
|
|
80
|
+
// partition key (derived from the produced event), but consumed events from
|
|
81
|
+
// other sources are tagged by their own key (e.g. CatalogProductSynced →
|
|
82
|
+
// productId, not this slice's orderId), so fall back to the read event's own
|
|
83
|
+
// tag value(s) instead of logging no id.
|
|
84
|
+
let readEventId = (tags: array<Reventless.DcbTag.tag>): option<string> => {
|
|
85
|
+
let ownTagValues = () => {
|
|
86
|
+
// Skip the `originatorSlice` metadata tag that `encodeEvent` appends to
|
|
87
|
+
// every stored event — it's the producing slice's name, not an entity id.
|
|
88
|
+
let vals =
|
|
89
|
+
tags
|
|
90
|
+
->Array.filter((t: Reventless.DcbTag.tag) => t.key != "originatorSlice")
|
|
91
|
+
->Array.reduce([], (acc, t: Reventless.DcbTag.tag) =>
|
|
92
|
+
acc->Array.includes(t.value) ? acc : acc->Array.concat([t.value])
|
|
93
|
+
)
|
|
94
|
+
vals->Array.length == 0 ? None : Some(vals->Array.join(","))
|
|
95
|
+
}
|
|
96
|
+
switch derivedPartitionTag {
|
|
97
|
+
| Simple(pt) =>
|
|
98
|
+
switch tags->Array.findMap((t: Reventless.DcbTag.tag) =>
|
|
99
|
+
t.key == pt.key ? Some(t.value) : None
|
|
100
|
+
) {
|
|
101
|
+
| Some(v) => Some(v)
|
|
102
|
+
| None => ownTagValues()
|
|
103
|
+
}
|
|
104
|
+
| Composite(spec) => Some(Reventless.DcbTag.getCompositePartitionKeyValue(tags, spec))
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let maxRetries = 3
|
|
109
|
+
|
|
110
|
+
// In-process decision-model projection cache (per warm Lambda instance, per
|
|
111
|
+
// slice). Keyed on the serialised DCB query, it holds the
|
|
112
|
+
// `(decisionState, readHead)` the previous command for that query derived.
|
|
113
|
+
//
|
|
114
|
+
// On a hit, `handleSingleCommand` seeds the fold from the cached state and
|
|
115
|
+
// reads only events *after* `readHead` (the delta) instead of folding full
|
|
116
|
+
// history — O(delta) instead of O(history) per warm same-entity command.
|
|
117
|
+
//
|
|
118
|
+
// What is cached is deliberately the state the slice *decided on* and the head
|
|
119
|
+
// it *read up to* — NOT the events this command produced, and NOT the
|
|
120
|
+
// position `append` returned. The produced events are read back by the next
|
|
121
|
+
// command's delta read (their position is always > readHead on both the
|
|
122
|
+
// DynamoDB and in-memory backends), so the cache never needs to fold produced
|
|
123
|
+
// events (sidestepping the consumedEvent/event type split) nor trust the
|
|
124
|
+
// append position (which is the batch *base* on DynamoDB but the batch *max*
|
|
125
|
+
// in-memory). Correctness still rests entirely on the conditional append's
|
|
126
|
+
// fence: a stale cached state only changes the *decision*, and a stale
|
|
127
|
+
// decision conflicts at append time → retry → re-read refreshes the state.
|
|
128
|
+
//
|
|
129
|
+
// Capacity is fixed at 100 entries; a per-slice knob is a future refinement
|
|
130
|
+
// (see the plan's Step 4).
|
|
131
|
+
let projectionCacheCapacity = 100
|
|
132
|
+
let projectionCache: Lru.t<
|
|
133
|
+
string,
|
|
134
|
+
(Behavior.state, option<Reventless.DcbTag.sequencePosition>),
|
|
135
|
+
> = Lru.make(~capacity=projectionCacheCapacity)
|
|
136
|
+
|
|
137
|
+
let resetCache = () => projectionCache->Lru.clear
|
|
138
|
+
|
|
139
|
+
// Processes one command against the DCB event log with optimistic concurrency:
|
|
140
|
+
// 1. Reads relevant events (filtered by command tags) to build the decision
|
|
141
|
+
// model — seeded from the projection cache when warm, so only events after
|
|
142
|
+
// the cached head are read (delta), else the full history (cold)
|
|
143
|
+
// 2. Decodes raw events using consumedEventSchema
|
|
144
|
+
// 3. Calls Behavior.decide to produce new events
|
|
145
|
+
// 4. Encodes produced events to raw and appends with a condition
|
|
146
|
+
// 5. On conflict, retries from step 1 (up to maxRetries)
|
|
147
|
+
let handleSingleCommand = (
|
|
148
|
+
~tagKeysByEventType,
|
|
149
|
+
~crossPartitionTagKeys,
|
|
150
|
+
dcbEventLog: DcbEventLog.operations,
|
|
151
|
+
command': Message.command'<Reventless.Id.String.t, Spec.command>,
|
|
152
|
+
) => {
|
|
153
|
+
let cmdJson =
|
|
154
|
+
command'->Message.commandJsonOfCommand'(
|
|
155
|
+
~idToString=Reventless.Id.String.toString,
|
|
156
|
+
~commandSchema=Spec.commandSchema,
|
|
157
|
+
)
|
|
158
|
+
// Render `Name({fields})` without the standalone command id: for DCB slice
|
|
159
|
+
// commands the id is the entity/partition id, which is already shown among
|
|
160
|
+
// the fields, so a leading `(id)` would just duplicate it.
|
|
161
|
+
EffectLogger.logInfo(
|
|
162
|
+
~comp,
|
|
163
|
+
~detail=cmdJson.commandJson,
|
|
164
|
+
`handling command: ${cmdJson->LogFormat.cmdDetailNoId}`,
|
|
165
|
+
)->Effect.runSync
|
|
166
|
+
|
|
167
|
+
let query = Reventless.DcbTag.buildQueryFromCommand(
|
|
168
|
+
~eventTypes=queryEventTypes,
|
|
169
|
+
~schema=Spec.commandSchema,
|
|
170
|
+
~value=command'.command,
|
|
171
|
+
~tagKeysByEventType,
|
|
172
|
+
~crossPartitionTagKeys,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
// Log the DCB query parameters — the OR clauses (event types + tags) the
|
|
176
|
+
// read below filters on. Constant across retries, so logged once per command.
|
|
177
|
+
let queryDetail =
|
|
178
|
+
query
|
|
179
|
+
->Array.map((qi: Reventless.DcbTag.queryItem) => {
|
|
180
|
+
let types = switch qi.eventTypes {
|
|
181
|
+
| Some(ts) => ts->Array.map(LogFormat.bold)->Array.join("|")
|
|
182
|
+
| None => "*"
|
|
183
|
+
}
|
|
184
|
+
let tags = switch qi.tags {
|
|
185
|
+
| Some(ts) =>
|
|
186
|
+
ts->Array.map((t: Reventless.DcbTag.tag) => `${t.key}=${t.value}`)->Array.join(",")
|
|
187
|
+
| None => ""
|
|
188
|
+
}
|
|
189
|
+
tags == "" ? types : `${types}{${tags}}`
|
|
190
|
+
})
|
|
191
|
+
->Array.join(" OR ")
|
|
192
|
+
EffectLogger.logInfo(~comp, `query: ${queryDetail}`)->Effect.runSync
|
|
193
|
+
|
|
194
|
+
// Extract the entity ID from the command for use in Accepted outcomes.
|
|
195
|
+
let entityId = switch derivedPartitionTag {
|
|
196
|
+
| Simple(pt) => Reventless.DcbTag.getPartitionTagValue(query, pt)
|
|
197
|
+
| Composite(spec) =>
|
|
198
|
+
let tags = Reventless.DcbTag.extractTags(Spec.commandSchema, command'.command)
|
|
199
|
+
Some(Reventless.DcbTag.getCompositePartitionKeyValue(tags, spec))
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
let cacheKey = query->JSON.stringifyAny->Option.getOr("")
|
|
203
|
+
|
|
204
|
+
// Working seed across retries: the `(state, head)` the read folds from.
|
|
205
|
+
// Starts from the projection cache (warm) or empty (cold); the conflict
|
|
206
|
+
// branch re-seeds it with the just-read `(state, head)` so each retry reads
|
|
207
|
+
// only the delta the conflicting writer added rather than full history.
|
|
208
|
+
let seed = ref(Lru.get(projectionCache, cacheKey))
|
|
209
|
+
|
|
210
|
+
let rec attempt = (~retries) => {
|
|
211
|
+
let (baseState, afterPos) = switch seed.contents {
|
|
212
|
+
| Some((state, head)) => (state, head)
|
|
213
|
+
| None => (Behavior.initialState, None)
|
|
214
|
+
}
|
|
215
|
+
let cacheHit = seed.contents->Option.isSome
|
|
216
|
+
|
|
217
|
+
// Consistency: governed by the slice's build-time `readConsistency` mode
|
|
218
|
+
// (default `EscalateOnRetry`). Correctness is identical in every mode — the
|
|
219
|
+
// conditional append's fence is always evaluated strongly, so a stale read
|
|
220
|
+
// can only cost a rejected append (then a retry), never a wrong write. The
|
|
221
|
+
// mode is a per-slice RCU/latency lever against replica-lag conflicts.
|
|
222
|
+
// EscalateOnRetry — eventual on the first attempt (cheaper RCU), then
|
|
223
|
+
// strong on every retry so a replica-lag conflict self-heals.
|
|
224
|
+
// AlwaysStrong — strong every attempt (known-hot slices).
|
|
225
|
+
// AlwaysEventual — eventual every attempt (cost-sensitive, low-contention).
|
|
226
|
+
let strongConsistency = switch Spec.readConsistency {
|
|
227
|
+
| Reventless.ReadConsistency.EscalateOnRetry => retries < maxRetries
|
|
228
|
+
| AlwaysStrong => true
|
|
229
|
+
| AlwaysEventual => false
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
dcbEventLog.readStream(~query, ~after=?afterPos, ~strongConsistency)
|
|
233
|
+
->Stream.map(raw => {
|
|
234
|
+
let decoded = decoder.decode(
|
|
235
|
+
~eventType=raw.eventType,
|
|
236
|
+
~data=raw.data->JSON.Decode.object->Option.getOr(Dict.make()),
|
|
237
|
+
)
|
|
238
|
+
decoded->Option.map(event => (event, raw.position, raw.eventType, readEventId(raw.tags)))
|
|
239
|
+
})
|
|
240
|
+
->Stream.flatMap(opt =>
|
|
241
|
+
switch opt {
|
|
242
|
+
| Some(v) => Stream.fromIterable([v])
|
|
243
|
+
| None => Stream.empty
|
|
244
|
+
}
|
|
245
|
+
)
|
|
246
|
+
// The head accumulator starts at `afterPos` (the seeded head), not `None`,
|
|
247
|
+
// so an empty delta read keeps the cached head — the append must condition
|
|
248
|
+
// on the head that actually exists, not assert "no events exist yet".
|
|
249
|
+
->Stream.runFold((baseState, afterPos, []), (
|
|
250
|
+
(dm, _pos, reads),
|
|
251
|
+
(event, position, eventType, eventId),
|
|
252
|
+
) => (
|
|
253
|
+
Behavior.evolve(dm, event),
|
|
254
|
+
Some(position),
|
|
255
|
+
reads->Array.concat([
|
|
256
|
+
switch eventId {
|
|
257
|
+
| Some(id) => `${eventType->LogFormat.bold}(${id})`
|
|
258
|
+
| None => eventType->LogFormat.bold
|
|
259
|
+
},
|
|
260
|
+
]),
|
|
261
|
+
))
|
|
262
|
+
->Effect.tap(((_, _, reads)) =>
|
|
263
|
+
EffectLogger.logInfo(
|
|
264
|
+
~comp,
|
|
265
|
+
`read${cacheHit ? " (cached, delta)" : ""}: ${reads->Array.length->Int.toString} event(s)${reads->Array.length == 0
|
|
266
|
+
? ""
|
|
267
|
+
: ` [${reads->Array.join(", ")}]`}`,
|
|
268
|
+
)
|
|
269
|
+
)
|
|
270
|
+
->Effect.flatMap(((state, headPosition, _)) =>
|
|
271
|
+
EffectLogger.logDebug(
|
|
272
|
+
~comp,
|
|
273
|
+
`deciding on state: ${state->JSON.stringifyAny->Option.getOr("<unserializable>")}`,
|
|
274
|
+
)->Effect.flatMap(_ =>
|
|
275
|
+
switch Behavior.decide(state, command'.command) {
|
|
276
|
+
| Ok(newEvents) if newEvents->Array.length == 0 =>
|
|
277
|
+
// No append, but the read snapshot is valid — cache it for the next command.
|
|
278
|
+
Lru.put(projectionCache, cacheKey, (state, headPosition))
|
|
279
|
+
CommandTopic_Helpers.reportAccepted(
|
|
280
|
+
cmdJson.meta.msgId,
|
|
281
|
+
switch entityId {
|
|
282
|
+
| Some(eid) => {entityId: eid, eventCount: 0}
|
|
283
|
+
| None => {eventCount: 0}
|
|
284
|
+
},
|
|
285
|
+
)
|
|
286
|
+
EffectLogger.logInfo(~comp, "no events produced")->Effect.map(_ => Ok("ok"))
|
|
287
|
+
| Ok(newEvents) =>
|
|
288
|
+
let rawEvents =
|
|
289
|
+
newEvents->Array.map(e =>
|
|
290
|
+
encodeEvent(~parentMeta=command'.meta, ~tagKeysByEventType, e)
|
|
291
|
+
)
|
|
292
|
+
let eventCount = rawEvents->Array.length->Int.toString
|
|
293
|
+
let eventDetails =
|
|
294
|
+
rawEvents
|
|
295
|
+
->Array.map(
|
|
296
|
+
e => {
|
|
297
|
+
let fields = switch e.data {
|
|
298
|
+
| Object(dict) =>
|
|
299
|
+
let f =
|
|
300
|
+
dict
|
|
301
|
+
->Dict.toArray
|
|
302
|
+
->Array.map(((k, v)) => `${k}:${v->JSON.stringify}`)
|
|
303
|
+
->Array.join(",")
|
|
304
|
+
f == "" ? "" : `({${f}})`
|
|
305
|
+
| _ => ""
|
|
306
|
+
}
|
|
307
|
+
`${LogFormat.bold(e.eventType)}${fields}`
|
|
308
|
+
},
|
|
309
|
+
)
|
|
310
|
+
->Array.join(", ")
|
|
311
|
+
let eventJsons = rawEvents->Array.map(e => e.data)->JSON.Encode.array
|
|
312
|
+
let condition: Reventless.DcbTag.appendCondition = {
|
|
313
|
+
query,
|
|
314
|
+
after: ?headPosition,
|
|
315
|
+
}
|
|
316
|
+
EffectLogger.logInfo(
|
|
317
|
+
~comp,
|
|
318
|
+
~detail=eventJsons,
|
|
319
|
+
`produced ${eventCount} event(s): [${eventDetails}]`,
|
|
320
|
+
)
|
|
321
|
+
->Effect.flatMap(_ => Effect.promise(() => dcbEventLog.append(rawEvents, ~condition)))
|
|
322
|
+
->Effect.flatMap(
|
|
323
|
+
appendResult =>
|
|
324
|
+
switch appendResult {
|
|
325
|
+
| Ok(_position) =>
|
|
326
|
+
// Cache the decided-on state at the read head (NOT including the
|
|
327
|
+
// events we just appended, and NOT the returned position): the
|
|
328
|
+
// next command's delta read picks our events up from `headPosition`.
|
|
329
|
+
Lru.put(projectionCache, cacheKey, (state, headPosition))
|
|
330
|
+
CommandTopic_Helpers.reportAccepted(
|
|
331
|
+
cmdJson.meta.msgId,
|
|
332
|
+
switch entityId {
|
|
333
|
+
| Some(eid) => {entityId: eid, eventCount: rawEvents->Array.length}
|
|
334
|
+
| None => {eventCount: rawEvents->Array.length}
|
|
335
|
+
},
|
|
336
|
+
)
|
|
337
|
+
EffectLogger.logInfo(~comp, `append: ${eventCount} event(s)`)->Effect.map(
|
|
338
|
+
_ => Ok("ok"),
|
|
339
|
+
)
|
|
340
|
+
| Error(err) =>
|
|
341
|
+
if retries > 0 {
|
|
342
|
+
// Re-seed from the just-read snapshot so the retry reads only
|
|
343
|
+
// the delta the conflicting writer added, not full history.
|
|
344
|
+
seed := Some((state, headPosition))
|
|
345
|
+
// Per-slice CloudWatch counter: rising AppendRetry signals a
|
|
346
|
+
// slice feeling contention (the metric to watch the eventual
|
|
347
|
+
// default with — see dcb-high-contention-handling.md).
|
|
348
|
+
Metrics.emitCount(~metric="AppendRetry", ~slice=Spec.name)
|
|
349
|
+
EffectLogger.logWarn(
|
|
350
|
+
~comp,
|
|
351
|
+
`append failed (retrying ${(maxRetries - retries + 1)
|
|
352
|
+
->Int.toString}/${maxRetries->Int.toString}): ${err}`,
|
|
353
|
+
)->Effect.flatMap(_ => attempt(~retries=retries - 1))
|
|
354
|
+
} else {
|
|
355
|
+
// Retries exhausted — drop any cached snapshot so the next
|
|
356
|
+
// command for this entity takes the cold full-read path.
|
|
357
|
+
Lru.invalidate(projectionCache, cacheKey)
|
|
358
|
+
let errorCode = err->String.startsWith("Conflict") ? "Conflict" : "AppendFailed"
|
|
359
|
+
// A surfaced Conflict means the 3-retry loop could not absorb
|
|
360
|
+
// the contention — the operator signal for "this slice is hot"
|
|
361
|
+
// (consider sharding / async — Issue 10, §4 of the analysis).
|
|
362
|
+
if errorCode == "Conflict" {
|
|
363
|
+
Metrics.emitCount(~metric="AppendConflict", ~slice=Spec.name)
|
|
364
|
+
}
|
|
365
|
+
CommandTopic_Helpers.reportRejected(
|
|
366
|
+
cmdJson.meta.msgId,
|
|
367
|
+
{errorCode, errorDetail: err},
|
|
368
|
+
)
|
|
369
|
+
EffectLogger.logError(
|
|
370
|
+
~comp,
|
|
371
|
+
`append failed, retries exhausted: ${err}`,
|
|
372
|
+
)->Effect.map(_ => Error(err))
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
)
|
|
376
|
+
| Error(error) =>
|
|
377
|
+
// Business-rule rejection — no append, but the read snapshot is valid; cache it.
|
|
378
|
+
Lru.put(projectionCache, cacheKey, (state, headPosition))
|
|
379
|
+
let errorJson = error->S.reverseConvertToJsonOrThrow(Spec.errorSchema)
|
|
380
|
+
let errorCode = errorJson->Message.variantNameOfJson
|
|
381
|
+
let (_, payloadDict) = errorJson->Message.splitMessage
|
|
382
|
+
let errorDetail =
|
|
383
|
+
payloadDict->Dict.toArray->Array.length == 0
|
|
384
|
+
? ""
|
|
385
|
+
: payloadDict->JSON.Encode.object->JSON.stringify
|
|
386
|
+
CommandTopic_Helpers.reportRejected(cmdJson.meta.msgId, {errorCode, errorDetail})
|
|
387
|
+
EffectLogger.logError(
|
|
388
|
+
~comp,
|
|
389
|
+
`decide rejected: ${errorCode} ${errorDetail}`,
|
|
390
|
+
)->Effect.map(_ => Ok("rejected"))
|
|
391
|
+
}
|
|
392
|
+
)
|
|
393
|
+
)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
attempt(~retries=maxRetries)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// CommandTopic handler — processes each command sequentially through handleSingleCommand,
|
|
400
|
+
// returning Ok(reference) or Error(reference) per command.
|
|
401
|
+
let handleCommands = (~tagKeysByEventType=Dict.make(), ~crossPartitionTagKeys=[], dcbEventLog, stream) =>
|
|
402
|
+
stream
|
|
403
|
+
->Stream.mapEffect(({ReventlessInfra.CommandTopic.reference: reference, command}) =>
|
|
404
|
+
handleSingleCommand(~tagKeysByEventType, ~crossPartitionTagKeys, dcbEventLog, command)->Effect.map(result =>
|
|
405
|
+
switch result {
|
|
406
|
+
| Ok(_) => Ok(reference)
|
|
407
|
+
| Error(_) => Error(reference)
|
|
408
|
+
}
|
|
409
|
+
)
|
|
410
|
+
)
|
|
411
|
+
->Stream.runCollect
|
|
412
|
+
}
|