diff --git a/Brighter.slnx b/Brighter.slnx
index e9fdd0a42c..2a1fc58280 100644
--- a/Brighter.slnx
+++ b/Brighter.slnx
@@ -6,6 +6,9 @@
+
+
+
@@ -153,6 +156,7 @@
+
@@ -180,14 +184,18 @@
+
+
+
-
+
+
@@ -259,6 +267,9 @@
+
+
+
@@ -267,5 +278,4 @@
-
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 7c998201d7..9118b42ddc 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -58,6 +58,10 @@
+
+
+
+
diff --git a/docs/adr/0037-provide-roslyn-analyzers-for-brighter.md b/docs/adr/0037-provide-roslyn-analyzers-for-brighter.md
new file mode 100644
index 0000000000..b10f354927
--- /dev/null
+++ b/docs/adr/0037-provide-roslyn-analyzers-for-brighter.md
@@ -0,0 +1,59 @@
+# 37. Provide Roslyn Analyzers for Brighter
+
+Date: 2026-01-25
+
+## Status
+
+Proposed
+
+## Context
+
+Using Brighter correctly often involves adhering to specific patterns and conventions that are not strictly enforced by the C# compiler alone. For example, developers must ensure that:
+- Attributes like `[Wrap]` are used correctly to configure the pipeline.
+- `Publication` objects are initialized with a valid `RequestType` that implements `IRequest` for proper mapping.
+- Subscriptions are configured with valid parameters.
+
+Currently, violations of these rules often result in runtime errors (e.g., `RuntimeBinderException`, `ArgumentException`) or silent failures. identifying these issues requires running the application, which delays the feedback loop and increases the risk of bugs reaching production.
+
+We want to "shift left" on these checks, providing feedback to developers as they write code.
+
+## Decision
+
+We will introduce a new project, `Paramore.Brighter.Analyzer`, containing custom Roslyn analyzers.
+This project will be distributed as a NuGet package that developers can include in their projects.
+
+The analyzers will inspect code at compile-time and report diagnostics (warnings or errors) for known invalid usage patterns of Brighter components.
+
+The initial implementation will include the following analyzers:
+
+1. **PublicationRequestTypeAssignmentAnalyzer**:
+ - Ensures that when a `Publication` object is created, the `RequestType` property is explicitly assigned.
+ - Verifies that the assigned type implements `IRequest`.
+
+2. **SubscriptionConstructorAnalyzer**:
+ - Validates that `Subscription` objects are instantiated with correct arguments and configuration.
+
+3. **WrapAttributeAnalyzer**:
+ - Ensures that attributes used for wrapping (e.g., encryption, compression) are applied to the correct mapping method (`MapToMessage` or `MapToRequest`).
+ - Prevents incorrect pipeline configuration by verifying that attributes intended for requests are not applied to message mapping and vice versa.
+
+## Technical Implementation Strategy
+
+The logic for the analyzers will be implemented using the **Roslyn API** and the **Visitor Pattern**.
+
+1. **Roslyn Syntax & Operation Trees**: We leverage Roslyn to inspect the code structure. We specifically focus on the **Operation Tree (IOperation)**, which provides a semantic view of the code (e.g., understanding that a line is an object creation regardless of syntax).
+2. **Visitor Pattern**: To efficiently traverse the code, we implement a `Visitor` (e.g., `RequestTypeAssignmentVisitor`).
+ - The **Visitor** "walks" through specific nodes of the code tree (like `ObjectCreation`).
+ - We utilize **Double Dispatch** (via the `Accept` method) to ensure the correct `Visit` method is called for each node type. This avoids the need for explicit type casting and massive `if-else` or `switch` statements to determine node types, making the code cleaner and more maintainable.
+ - It maintains state as it visits nodes (e.g., "I am currently inside a Publication object creation").
+
+## Consequences
+
+### Positive
+- **Immediate Feedback**: Developers receive feedback on incorrect usage within the IDE and at build time.
+- **Reduced Runtime Errors**: Prevents a class of configuration and usage errors from occurring at runtime.
+- **Better Discovery**: Helps new users learn Brighter's constraints and requirements through compiler messages rather than documentation lookup or trial-and-error.
+
+### Negative
+- **Maintenance**: The analyzer project must be maintained and updated as Brighter's API and patterns evolve.
+
diff --git a/samples/Analyzer/AnalyzerSamples/AnalyzerSamples.csproj b/samples/Analyzer/AnalyzerSamples/AnalyzerSamples.csproj
new file mode 100644
index 0000000000..546304d333
--- /dev/null
+++ b/samples/Analyzer/AnalyzerSamples/AnalyzerSamples.csproj
@@ -0,0 +1,17 @@
+
+
+
+ net9.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/Analyzer/AnalyzerSamples/PublicationSample.cs b/samples/Analyzer/AnalyzerSamples/PublicationSample.cs
new file mode 100644
index 0000000000..fa6b322323
--- /dev/null
+++ b/samples/Analyzer/AnalyzerSamples/PublicationSample.cs
@@ -0,0 +1,48 @@
+using Paramore.Brighter;
+
+namespace AnalyzerSamples
+{
+ public static class PublicationCreator
+ {
+ public static Publication GetPublicationNoRequestType()
+ {
+ return new PublicationSample() {
+ Subject="test"
+ };
+ }
+ public static Publication GetPublicationWrongRequestType()
+ {
+ return new PublicationSample()
+ {
+ Subject = "test",
+ RequestType = typeof(EventSample2)
+ };
+ }
+ public static Publication GetPublicationWithRequestType()
+ {
+ return new PublicationSample()
+ {
+ Subject = "test",
+ RequestType = typeof(EventSample)
+ };
+ }
+ public static AnyClass GetAnyOtherClass()
+ {
+ return new AnyClass();
+ }
+ }
+ public class PublicationSample : Publication
+ {
+ }
+ public class EventSample(Id id) : Event(id)
+ {
+ }
+ public class EventSample2
+ {
+ }
+
+ public class AnyClass
+ {
+
+ }
+}
diff --git a/samples/Analyzer/AnalyzerSamples/SubscriptionSample.cs b/samples/Analyzer/AnalyzerSamples/SubscriptionSample.cs
new file mode 100644
index 0000000000..3fc5041dc2
--- /dev/null
+++ b/samples/Analyzer/AnalyzerSamples/SubscriptionSample.cs
@@ -0,0 +1,37 @@
+
+
+using Paramore.Brighter;
+
+namespace AnalyzerSamples
+{
+ internal class SubscriptionSample
+ {
+ }
+ public static class SubscriptionCreator
+ {
+ public static Subscription GetSubscription_MessagePumpSpecified()
+ {
+ return new SubscriptionTest("name", "name", "key", messagePumpType: MessagePumpType.Reactor);
+ }
+ public static Subscription GetSubscription_NoMessagePumpSpecified()
+ {
+ return new SubscriptionTest("name", "name", "key");
+ }
+ public static Subscription GetSubscriptionNested_NoMessagePumpSpecified()
+ {
+ return new SubscriptionTestNested("name", "name", "key");
+ }
+ public class SubscriptionTest : Subscription
+ {
+ public SubscriptionTest(SubscriptionName subscriptionName, ChannelName channelName, RoutingKey routingKey, Type? requestType = null, Func? getRequestType = null, int bufferSize = 1, int noOfPerformers = 1, TimeSpan? timeOut = null, int requeueCount = -1, TimeSpan? requeueDelay = null, int unacceptableMessageLimit = 0, MessagePumpType messagePumpType = MessagePumpType.Unknown, IAmAChannelFactory? channelFactory = null, OnMissingChannel makeChannels = OnMissingChannel.Create, TimeSpan? emptyChannelDelay = null, TimeSpan? channelFailureDelay = null) : base(subscriptionName, channelName, routingKey, requestType, getRequestType, bufferSize, noOfPerformers, timeOut, requeueCount, requeueDelay, unacceptableMessageLimit, messagePumpType, channelFactory, makeChannels, emptyChannelDelay, channelFailureDelay)
+ {
+ }
+ }
+ public class SubscriptionTestNested : SubscriptionTest
+ {
+ public SubscriptionTestNested(SubscriptionName subscriptionName, ChannelName channelName, RoutingKey routingKey, Type? requestType = null, Func? getRequestType = null, int bufferSize = 1, int noOfPerformers = 1, TimeSpan? timeOut = null, int requeueCount = -1, TimeSpan? requeueDelay = null, int unacceptableMessageLimit = 0, MessagePumpType messagePumpType = MessagePumpType.Unknown, IAmAChannelFactory? channelFactory = null, OnMissingChannel makeChannels = OnMissingChannel.Create, TimeSpan? emptyChannelDelay = null, TimeSpan? channelFailureDelay = null) : base(subscriptionName, channelName, routingKey, requestType, getRequestType, bufferSize, noOfPerformers, timeOut, requeueCount, requeueDelay, unacceptableMessageLimit, messagePumpType, channelFactory, makeChannels, emptyChannelDelay, channelFailureDelay)
+ {
+ }
+ }
+ }
+}
diff --git a/samples/Analyzer/AnalyzerSamples/WrapWithMapperSample.cs b/samples/Analyzer/AnalyzerSamples/WrapWithMapperSample.cs
new file mode 100644
index 0000000000..506709f6d2
--- /dev/null
+++ b/samples/Analyzer/AnalyzerSamples/WrapWithMapperSample.cs
@@ -0,0 +1,47 @@
+using Paramore.Brighter;
+using Paramore.Brighter.Transforms.Attributes;
+
+namespace AnalyzerSamples
+{
+ public class WrapWithMapperAsyncSample: IAmAMessageMapperAsync
+ {
+ public IRequestContext? Context { get; set ; }
+
+ [Compress(0)]
+ [ClaimCheck(0)]
+ public async Task MapToRequestAsync(Message message, CancellationToken cancellationToken = default)
+ {
+ await Task.Yield();
+ return new SampleEvent(message.Id);
+ }
+
+ [Decompress(0)]
+ public async Task MapToMessageAsync(SampleEvent request, Publication publication, CancellationToken cancellationToken = default)
+ {
+ await Task.Yield();
+ throw new NotImplementedException();
+ }
+ }
+ public class WrapWithMapperSample: IAmAMessageMapper
+ {
+ public IRequestContext? Context { get; set ; }
+
+ [Compress(0)]
+ [ClaimCheck(0)]
+ public SampleEvent MapToRequest(Message message)
+ {
+ return new SampleEvent(message.Id);
+ }
+
+ [Decompress(0)]
+ public Message MapToMessage(SampleEvent request, Publication publication)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+
+ public class SampleEvent(Id id) : Event(id)
+ {
+ }
+}
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 48bc70b36a..e127a59c4f 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -29,6 +29,7 @@
10.0.0
+ netstandard2.0;
netstandard2.0;net8.0;net9.0;net10.0
net462;net8.0;net9.0;net10.0
net8.0;net9.0;net10.0
diff --git a/src/Paramore.Brighter.Analyzer.Package/Paramore.Brighter.Analyzer.Package.csproj b/src/Paramore.Brighter.Analyzer.Package/Paramore.Brighter.Analyzer.Package.csproj
new file mode 100644
index 0000000000..34bbc026c8
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer.Package/Paramore.Brighter.Analyzer.Package.csproj
@@ -0,0 +1,36 @@
+
+
+
+ $(BrighterNetStandardTargetFrameworks)
+ false
+ true
+ true
+
+
+
+ AboubakrNasef
+ false
+ Paramore.Brighter.Analyzer
+ Analyzer For usage of Brighter Command
+ Paramore.Brighter,Paramore.Brighter.Analyzer, analyzers
+ true
+ true
+
+ $(TargetsForTfmSpecificContentInPackage);_AddAnalyzersToOutput
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Paramore.Brighter.Analyzer.Package/build/Paramore.Brighter.Analyzer.props b/src/Paramore.Brighter.Analyzer.Package/build/Paramore.Brighter.Analyzer.props
new file mode 100644
index 0000000000..ead6e08f09
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer.Package/build/Paramore.Brighter.Analyzer.props
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Paramore.Brighter.Analyzer.Package/configuration/default.editorconfig b/src/Paramore.Brighter.Analyzer.Package/configuration/default.editorconfig
new file mode 100644
index 0000000000..c4458e30f1
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer.Package/configuration/default.editorconfig
@@ -0,0 +1,5 @@
+dotnet_diagnostic.BRT001.severity = warning
+dotnet_diagnostic.BRT002.severity = warning
+dotnet_diagnostic.BRT003.severity = warning
+dotnet_diagnostic.BRT004.severity = warning
+dotnet_diagnostic.BRT005.severity = warning
\ No newline at end of file
diff --git a/src/Paramore.Brighter.Analyzer.Package/configuration/none.editorconfig b/src/Paramore.Brighter.Analyzer.Package/configuration/none.editorconfig
new file mode 100644
index 0000000000..aef953fc0e
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer.Package/configuration/none.editorconfig
@@ -0,0 +1,5 @@
+dotnet_diagnostic.BRT001.severity = none
+dotnet_diagnostic.BRT002.severity = none
+dotnet_diagnostic.BRT003.severity = none
+dotnet_diagnostic.BRT004.severity = none
+dotnet_diagnostic.BRT005.severity = none
\ No newline at end of file
diff --git a/src/Paramore.Brighter.Analyzer.Package/tools/install.ps1 b/src/Paramore.Brighter.Analyzer.Package/tools/install.ps1
new file mode 100644
index 0000000000..53136a238f
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer.Package/tools/install.ps1
@@ -0,0 +1,275 @@
+param($installPath, $toolsPath, $package, $project)
+
+if($project.Object.SupportsPackageDependencyResolution)
+{
+ if($project.Object.SupportsPackageDependencyResolution())
+ {
+ # Do not install analyzers via install.ps1, instead let the project system handle it.
+ return
+ }
+}
+
+$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve
+
+foreach($analyzersPath in $analyzersPaths)
+{
+ if (Test-Path $analyzersPath)
+ {
+ # Install the language agnostic analyzers.
+ foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll)
+ {
+ if($project.Object.AnalyzerReferences)
+ {
+ $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
+ }
+ }
+ }
+}
+
+# $project.Type gives the language name like (C# or VB.NET)
+$languageFolder = ""
+if($project.Type -eq "C#")
+{
+ $languageFolder = "cs"
+}
+if($project.Type -eq "VB.NET")
+{
+ $languageFolder = "vb"
+}
+if($languageFolder -eq "")
+{
+ return
+}
+
+foreach($analyzersPath in $analyzersPaths)
+{
+ # Install language specific analyzers.
+ $languageAnalyzersPath = join-path $analyzersPath $languageFolder
+ if (Test-Path $languageAnalyzersPath)
+ {
+ foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll)
+ {
+ if($project.Object.AnalyzerReferences)
+ {
+ $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
+ }
+ }
+ }
+}
+# SIG # Begin signature block
+# MIIoKgYJKoZIhvcNAQcCoIIoGzCCKBcCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA/i+qRUHsWzI0s
+# FVk99zLgt/HOEQ33uvkFsWtHTHZgf6CCDXYwggX0MIID3KADAgECAhMzAAADTrU8
+# esGEb+srAAAAAANOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
+# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
+# bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI5WhcNMjQwMzE0MTg0MzI5WjB0MQsw
+# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
+# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+# AQDdCKiNI6IBFWuvJUmf6WdOJqZmIwYs5G7AJD5UbcL6tsC+EBPDbr36pFGo1bsU
+# p53nRyFYnncoMg8FK0d8jLlw0lgexDDr7gicf2zOBFWqfv/nSLwzJFNP5W03DF/1
+# 1oZ12rSFqGlm+O46cRjTDFBpMRCZZGddZlRBjivby0eI1VgTD1TvAdfBYQe82fhm
+# WQkYR/lWmAK+vW/1+bO7jHaxXTNCxLIBW07F8PBjUcwFxxyfbe2mHB4h1L4U0Ofa
+# +HX/aREQ7SqYZz59sXM2ySOfvYyIjnqSO80NGBaz5DvzIG88J0+BNhOu2jl6Dfcq
+# jYQs1H/PMSQIK6E7lXDXSpXzAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
+# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUnMc7Zn/ukKBsBiWkwdNfsN5pdwAw
+# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
+# MBQGA1UEBRMNMjMwMDEyKzUwMDUxNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
+# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
+# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
+# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
+# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
+# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAD21v9pHoLdBSNlFAjmk
+# mx4XxOZAPsVxxXbDyQv1+kGDe9XpgBnT1lXnx7JDpFMKBwAyIwdInmvhK9pGBa31
+# TyeL3p7R2s0L8SABPPRJHAEk4NHpBXxHjm4TKjezAbSqqbgsy10Y7KApy+9UrKa2
+# kGmsuASsk95PVm5vem7OmTs42vm0BJUU+JPQLg8Y/sdj3TtSfLYYZAaJwTAIgi7d
+# hzn5hatLo7Dhz+4T+MrFd+6LUa2U3zr97QwzDthx+RP9/RZnur4inzSQsG5DCVIM
+# pA1l2NWEA3KAca0tI2l6hQNYsaKL1kefdfHCrPxEry8onJjyGGv9YKoLv6AOO7Oh
+# JEmbQlz/xksYG2N/JSOJ+QqYpGTEuYFYVWain7He6jgb41JbpOGKDdE/b+V2q/gX
+# UgFe2gdwTpCDsvh8SMRoq1/BNXcr7iTAU38Vgr83iVtPYmFhZOVM0ULp/kKTVoir
+# IpP2KCxT4OekOctt8grYnhJ16QMjmMv5o53hjNFXOxigkQWYzUO+6w50g0FAeFa8
+# 5ugCCB6lXEk21FFB1FdIHpjSQf+LP/W2OV/HfhC3uTPgKbRtXo83TZYEudooyZ/A
+# Vu08sibZ3MkGOJORLERNwKm2G7oqdOv4Qj8Z0JrGgMzj46NFKAxkLSpE5oHQYP1H
+# tPx1lPfD7iNSbJsP6LiUHXH1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
+# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
+# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
+# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
+# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
+# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
+# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
+# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
+# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
+# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
+# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
+# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
+# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
+# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
+# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
+# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
+# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
+# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
+# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
+# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
+# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
+# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
+# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
+# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
+# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
+# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
+# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
+# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
+# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
+# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
+# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
+# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
+# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
+# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
+# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
+# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
+# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
+# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
+# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
+# /Xmfwb1tbWrJUnMTDXpQzTGCGgowghoGAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
+# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
+# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
+# Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB
+# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
+# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIEY4Ow3COroWH11sAEOoStJj
+# 1u4sq9rcx0dAx0gyZLHCMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
+# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
+# BQAEggEAFlSLlk17KQp2AwLbr5e4T5zyE44MGZOdNejIip+Mg8co8qho16qdNSvg
+# AhvoJYxPJJr70DSCU9BIUoWY7hXoi9S4P08YlAid7BUT5OciIgHlrb8I900LaE+S
+# 83rSvfVU1CZCjiSwcgng5DD2VPRo0Lu4G9p2Ky14dyOPwtPvrpsib5s9kewZLdiy
+# /KxEDLKX8P+cHat1xH7RaZLDNxweRS6GSomjE2vjOlQHNSW879XR8bSoAt/m4uR1
+# WyrAxTGZb4miEYX+I5HsrWvbZLw9NSCJ/crbbap3LIobfQtK5binjY7v4MQp/5Oq
+# y4S/4FAfwhWDXfaQfq6YTeOjHRVQbKGCF5QwgheQBgorBgEEAYI3AwMBMYIXgDCC
+# F3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq
+# hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
+# AwQCAQUABCCV3irbWIoYz2Llfx9YQhUNtcP6GOrL7+YTUXQ4y5qzWAIGZNTJrsAW
+# GBMyMDIzMDgzMTAwMTI1OC45ODNaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV
+# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
+# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l
+# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTIwMC0w
+# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg
+# ghHqMIIHIDCCBQigAwIBAgITMwAAAc9SNr5xS81IygABAAABzzANBgkqhkiG9w0B
+# AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
+# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
+# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzA1MjUxOTEy
+# MTFaFw0yNDAyMDExOTEyMTFaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
+# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
+# cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z
+# MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTIwMC0wNUUwLUQ5NDcxJTAjBgNV
+# BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB
+# AQUAA4ICDwAwggIKAoICAQC4Pct+15TYyrUje553lzBQodgmd5Bz7WuH8SdHpAoW
+# z+01TrHExBSuaMKnxvVMsyYtas5h6aopUGAS5WKVLZAvUtH62TKmAE0JK+i1hafi
+# CSXLZPcRexxeRkOqeZefLBzXp0nudMOXUUab333Ss8LkoK4l3LYxm1Ebsr3b2OTo
+# 2ebsAoNJ4kSxmVuPM7C+RDhGtVKR/EmHsQ9GcwGmluu54bqiVFd0oAFBbw4txTU1
+# mruIGWP/i+sgiNqvdV/wah/QcrKiGlpWiOr9a5aGrJaPSQD2xgEDdPbrSflYxsRM
+# dZCJI8vzvOv6BluPcPPGGVLEaU7OszdYjK5f4Z5Su/lPK1eST5PC4RFsVcOiS4L0
+# sI4IFZywIdDJHoKgdqWRp6Q5vEDk8kvZz6HWFnYLOlHuqMEYvQLr6OgooYU9z0A5
+# cMLHEIHYV1xiaBzx2ERiRY9MUPWohh+TpZWEUZlUm/q9anXVRN0ujejm6OsUVFDs
+# sIMszRNCqEotJGwtHHm5xrCKuJkFr8GfwNelFl+XDoHXrQYL9zY7Np+frsTXQpKR
+# NnmI1ashcn5EC+wxUt/EZIskWzewEft0/+/0g3+8YtMkUdaQE5+8e7C8UMiXOHkM
+# K25jNNQqLCedlJwFIf9ir9SpMc72NR+1j6Uebiz/ZPV74do3jdVvq7DiPFlTb92U
+# KwIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFDaeKPtp0eTSVdG+gZc5BDkabTg4MB8G
+# A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG
+# Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy
+# MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w
+# XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy
+# dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG
+# A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD
+# AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQBQgm4pnA0xkd/9uKXJMzdMYyxUfUm/ZusU
+# Ba32MEZXQuMGp20pSuX2VW9/tpTMo5bkaJdBVoUyd2DbDsNb1kjr/36ntT0jvL3A
+# oWStAFhZBypmpPbx+BPK49ZlejlM4d5epX668tRRGfFip9Til9yKRfXBrXnM/q64
+# IinN7zXEQ3FFQhdJMzt8ibXClO7eFA+1HiwZPWysYWPb/ZOFobPEMvXie+GmEbTK
+# bhE5tze6RrA9aejjP+v1ouFoD5bMj5Qg+wfZXqe+hfYKpMd8QOnQyez+Nlj1ityn
+# OZWfwHVR7dVwV0yLSlPT+yHIO8g+3fWiAwpoO17bDcntSZ7YOBljXrIgad4W4gX+
+# 4tp1eBsc6XWIITPBNzxQDZZRxD4rXzOB6XRlEVJdYZQ8gbXOirg/dNvS2GxcR50Q
+# dOXDAumdEHaGNHb6y2InJadCPp2iT5QLC4MnzR+YZno1b8mWpCdOdRs9g21QbbrI
+# 06iLk9KD61nx7K5ReSucuS5Z9nbkIBaLUxDesFhr1wmd1ynf0HQ51Swryh7YI7TX
+# T0jr81mbvvI9xtoqjFvIhNBsICdCfTR91ylJTH8WtUlpDhEgSqWt3gzNLPTSvXAx
+# XTpIM583sZdd+/2YGADMeWmt8PuMce6GsIcLCOF2NiYZ10SXHZS5HRrLrChuzedD
+# RisWpIu5uTCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI
+# hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
+# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
+# MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy
+# MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC
+# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
+# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
+# bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
+# AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg
+# M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF
+# dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6
+# GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp
+# Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu
+# yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E
+# XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0
+# lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q
+# GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ
+# +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA
+# PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw
+# EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG
+# NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV
+# MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj
+# cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK
+# BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
+# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
+# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
+# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
+# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
+# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG
+# 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x
+# M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC
+# VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449
+# xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM
+# nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS
+# PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d
+# Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn
+# GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs
+# QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL
+# jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL
+# 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNN
+# MIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
+# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
+# b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn
+# MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjkyMDAtMDVFMC1EOTQ3MSUwIwYDVQQD
+# ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQDq
+# 8xzVXwLguauAQj1rrJ4/TyEMm6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD
+# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
+# IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6JpNsjAiGA8yMDIzMDgzMDIzMjIy
+# NloYDzIwMjMwODMxMjMyMjI2WjB0MDoGCisGAQQBhFkKBAExLDAqMAoCBQDomk2y
+# AgEAMAcCAQACAi7oMAcCAQACAhLnMAoCBQDom58yAgEAMDYGCisGAQQBhFkKBAIx
+# KDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZI
+# hvcNAQELBQADggEBADE16CHIs3WeXYKQG9djja+StAB7gsNJxV9p+CETL3847UL3
+# +DIeoj6p5g0FkS8PJK2xc+UbcNx6XJO+WUXEbU9GL4mrOCQjYOM+i3r8FEU+l3Gh
+# 6ZG/9ygsIYRXEfDKK4lsbrrUkFQs9nDISHT3f8JZEJJXSsGmwcHWlNC0LC8bv0Jp
+# e2Bw2+SNc6SlGD8Vv45r4WFPHhfSioCz4HSsF1He3/2Wku7OH85FKvugBlsca7+F
+# bpGsDSL4LO9bv60DxuD+8xBZuyTB8s64ifCGlOXCNpK5VaHND48PhoJbuD0COwlM
+# Rn5NlT6T4hhtkPOqNscMlzYHmTOKc5NhWK8PyrIxggQNMIIECQIBATCBkzB8MQsw
+# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
+# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy
+# b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAc9SNr5xS81IygABAAABzzAN
+# BglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8G
+# CSqGSIb3DQEJBDEiBCAjGiC3/PscCMBvPZjpZqbYcL2WRZ+Ecf74oiIPQKkjSzCB
+# +gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EILPpsLqeNS4NuYXE2VJlMuvQeWVA
+# 80ZDFhpOPjSzhPa/MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh
+# c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
+# b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw
+# MTACEzMAAAHPUja+cUvNSMoAAQAAAc8wIgQgWdbLA3Co0zf3nE5086HU0tdn0vK7
+# yXyU9aAFpszvWFIwDQYJKoZIhvcNAQELBQAEggIANrHN06oaP0N1lUSxxoJteJgI
+# fQjN82xV5VSE0cwNCBy05fg1VydPERF59+MIwIlJHhikSo3YOj5tt3AohC78U46P
+# 2IdrLHKlA3rjiLUGwQvGUKJUSsEH4uueA1mmh9jwUBJhY3NjTcMQaQqp/oxZTaya
+# l7NqbubBIZvsDD126SUr8jtVWtZZzw8pnWCFb4Rijii4fY1UiQzfQLFwqQuid6tE
+# I0AaY3IoTlp7U9K2wfAPWcP1G7n3qv+990GEiQlGJlCfIJSSJQodzL2QZF5HCn/K
+# SfgkRPn3y/Aax8683mWCT8zricYzO3MZ9j0T7tAcqOiWb7PFCsk5Va44lq4Gv0u+
+# +60FYCAA/Qn6eMuNkqIpBeIK0+NYUcMSwPdY/riKXdgkVLwChEJC5WWznD/Iqsil
+# Jj9XYailhXzYx7Pa2MLays62LPwCnUxRQBTD9/rL3XQMj69iA4lisb51dKAtrqAU
+# 53aRXFn+twYYFTAUQ/oNK14nZEQE5H53xAfhphMokJOu+CnQQKeCMeYlex6Q4zfw
+# TQxP/xXZ+QW2cSZTwh1d5iE0XMhKCZxhxIOF/rRA+75L5GUz60reRZPeH/7USYZL
+# VPc0+kxIdSbNFJAhAp0u59wSMQdBofor3+HfDfmxmoSjfCSH4TvOkNIulX1PPJPX
+# UB7H4n7XHWJPkUU0cUw=
+# SIG # End signature block
diff --git a/src/Paramore.Brighter.Analyzer.Package/tools/uninstall.ps1 b/src/Paramore.Brighter.Analyzer.Package/tools/uninstall.ps1
new file mode 100644
index 0000000000..1f6b6d4f0e
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer.Package/tools/uninstall.ps1
@@ -0,0 +1,282 @@
+param($installPath, $toolsPath, $package, $project)
+
+if($project.Object.SupportsPackageDependencyResolution)
+{
+ if($project.Object.SupportsPackageDependencyResolution())
+ {
+ # Do not uninstall analyzers via uninstall.ps1, instead let the project system handle it.
+ return
+ }
+}
+
+$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve
+
+foreach($analyzersPath in $analyzersPaths)
+{
+ # Uninstall the language agnostic analyzers.
+ if (Test-Path $analyzersPath)
+ {
+ foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll)
+ {
+ if($project.Object.AnalyzerReferences)
+ {
+ $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
+ }
+ }
+ }
+}
+
+# $project.Type gives the language name like (C# or VB.NET)
+$languageFolder = ""
+if($project.Type -eq "C#")
+{
+ $languageFolder = "cs"
+}
+if($project.Type -eq "VB.NET")
+{
+ $languageFolder = "vb"
+}
+if($languageFolder -eq "")
+{
+ return
+}
+
+foreach($analyzersPath in $analyzersPaths)
+{
+ # Uninstall language specific analyzers.
+ $languageAnalyzersPath = join-path $analyzersPath $languageFolder
+ if (Test-Path $languageAnalyzersPath)
+ {
+ foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll)
+ {
+ if($project.Object.AnalyzerReferences)
+ {
+ try
+ {
+ $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
+ }
+ catch
+ {
+
+ }
+ }
+ }
+ }
+}
+# SIG # Begin signature block
+# MIIoLQYJKoZIhvcNAQcCoIIoHjCCKBoCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDC68wb97fg0QGL
+# yXrxJhYfmibzcOh8caqC0uZprfczDaCCDXYwggX0MIID3KADAgECAhMzAAADTrU8
+# esGEb+srAAAAAANOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
+# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
+# bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI5WhcNMjQwMzE0MTg0MzI5WjB0MQsw
+# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
+# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+# AQDdCKiNI6IBFWuvJUmf6WdOJqZmIwYs5G7AJD5UbcL6tsC+EBPDbr36pFGo1bsU
+# p53nRyFYnncoMg8FK0d8jLlw0lgexDDr7gicf2zOBFWqfv/nSLwzJFNP5W03DF/1
+# 1oZ12rSFqGlm+O46cRjTDFBpMRCZZGddZlRBjivby0eI1VgTD1TvAdfBYQe82fhm
+# WQkYR/lWmAK+vW/1+bO7jHaxXTNCxLIBW07F8PBjUcwFxxyfbe2mHB4h1L4U0Ofa
+# +HX/aREQ7SqYZz59sXM2ySOfvYyIjnqSO80NGBaz5DvzIG88J0+BNhOu2jl6Dfcq
+# jYQs1H/PMSQIK6E7lXDXSpXzAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
+# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUnMc7Zn/ukKBsBiWkwdNfsN5pdwAw
+# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
+# MBQGA1UEBRMNMjMwMDEyKzUwMDUxNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
+# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
+# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
+# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
+# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
+# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAD21v9pHoLdBSNlFAjmk
+# mx4XxOZAPsVxxXbDyQv1+kGDe9XpgBnT1lXnx7JDpFMKBwAyIwdInmvhK9pGBa31
+# TyeL3p7R2s0L8SABPPRJHAEk4NHpBXxHjm4TKjezAbSqqbgsy10Y7KApy+9UrKa2
+# kGmsuASsk95PVm5vem7OmTs42vm0BJUU+JPQLg8Y/sdj3TtSfLYYZAaJwTAIgi7d
+# hzn5hatLo7Dhz+4T+MrFd+6LUa2U3zr97QwzDthx+RP9/RZnur4inzSQsG5DCVIM
+# pA1l2NWEA3KAca0tI2l6hQNYsaKL1kefdfHCrPxEry8onJjyGGv9YKoLv6AOO7Oh
+# JEmbQlz/xksYG2N/JSOJ+QqYpGTEuYFYVWain7He6jgb41JbpOGKDdE/b+V2q/gX
+# UgFe2gdwTpCDsvh8SMRoq1/BNXcr7iTAU38Vgr83iVtPYmFhZOVM0ULp/kKTVoir
+# IpP2KCxT4OekOctt8grYnhJ16QMjmMv5o53hjNFXOxigkQWYzUO+6w50g0FAeFa8
+# 5ugCCB6lXEk21FFB1FdIHpjSQf+LP/W2OV/HfhC3uTPgKbRtXo83TZYEudooyZ/A
+# Vu08sibZ3MkGOJORLERNwKm2G7oqdOv4Qj8Z0JrGgMzj46NFKAxkLSpE5oHQYP1H
+# tPx1lPfD7iNSbJsP6LiUHXH1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
+# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
+# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
+# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
+# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
+# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
+# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
+# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
+# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
+# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
+# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
+# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
+# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
+# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
+# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
+# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
+# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
+# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
+# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
+# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
+# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
+# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
+# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
+# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
+# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
+# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
+# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
+# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
+# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
+# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
+# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
+# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
+# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
+# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
+# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
+# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
+# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
+# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
+# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
+# /Xmfwb1tbWrJUnMTDXpQzTGCGg0wghoJAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
+# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
+# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
+# Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB
+# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
+# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIBdcqRcs5QL71hlQnl3M636V
+# 5iTZvb6co3MHeMuIr36qMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
+# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
+# BQAEggEARbhO/zqm6SWBf6DSJ3P1r82VjSyMGXaLtMfTOq/bqHOXPOqC25R1v5uO
+# zu4ri+UOS8dU6EfW6C9Xf/Z1Ue/oxrvxn5j8mPvsmcs5OyDO0hW0Wv6pYEy5Z5up
+# mfcqvfUfl3+ir29lgPuz0f1mLpz0XxqhjqElEi5RfZD1k1YVg65f0qHroP2txql5
+# TfC77DXJ8verzVm1wqXBHTAERQD94TJobahYTCmyaudMjLUVFakv2lTMv0YTnrQR
+# So006ZQg3i1jcVCJt/bRDGKh3xUo1IHgoh3NjMEkxT3iWt8rnX8Us6T6Zg8B2OxC
+# 0EnuIu/eYYUlYTLQxO9eks7w1kVcUqGCF5cwgheTBgorBgEEAYI3AwMBMYIXgzCC
+# F38GCSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq
+# hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
+# AwQCAQUABCBQ1HvJFFsT0ICl6oEZlSNN8DlUeQobBHt/oTUGN6iKBgIGZNTKS0Bc
+# GBMyMDIzMDgzMTAwMTI1OC4zODVaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV
+# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
+# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l
+# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046N0YwMC0w
+# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg
+# ghHtMIIHIDCCBQigAwIBAgITMwAAAdWpAs/Fp8npWgABAAAB1TANBgkqhkiG9w0B
+# AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
+# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
+# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzA1MjUxOTEy
+# MzBaFw0yNDAyMDExOTEyMzBaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
+# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
+# cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z
+# MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046N0YwMC0wNUUwLUQ5NDcxJTAjBgNV
+# BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB
+# AQUAA4ICDwAwggIKAoICAQDFfak57Oph9vuxtloABiLc6enT+yKH619b+OhGdkyh
+# gNzkX80KUGI/jEqOVMV4Sqt/UPFFidx2t7v2SETj2tAzuVKtDfq2HBpu80vZ0vyQ
+# DydVt4MDL4tJSKqgYofCxDIBrWzJJjgBolKdOJx1ut2TyOc+UOm7e92tVPHpjdg+
+# Omf31TLUf/oouyAOJ/Inn2ih3ASP0QYm+AFQjhYDNDu8uzMdwHF5QdwsscNa9PVS
+# GedLdDLo9jL6DoPF4NYo06lvvEQuSJ9ImwZfBGLy/8hpE7RD4ewvJKmM1+t6eQuE
+# sTXjrGM2WjkW18SgUZ8n+VpL2uk6AhDkCa355I531p0Jkqpoon7dHuLUdZSQO40q
+# mVIQ6qQCanvImTqmNgE/rPJ0rgr0hMPI/uR1T/iaL0mEq4bqak+3sa8I+FAYOI/P
+# C7V+zEek+sdyWtaX+ndbGlv/RJb5mQaGn8NunbkfvHD1Qt5D0rmtMOekYMq7QjYq
+# E3FEP/wAY4TDuJxstjsa2HXi2yUDEg4MJL6/JvsQXToOZ+IxR6KT5t5fB5FpZYBp
+# VLMma3pm5z6VXvkXrYs33NXJqVWLwiswa7NUFV87Es2sou9Idw3yAZmHIYWgOQ+D
+# IY1nY3aG5DODiwN1rJyEb+mbWDagrdVxcncr6UKKO49eoNTXEW+scUf6GwXG0KEy
+# mQIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFK/QXKNO35bBMOz3R5giX7Ala2OaMB8G
+# A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG
+# Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy
+# MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w
+# XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy
+# dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG
+# A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD
+# AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQBmRddqvQuyjRpx0HGxvOqffFrbgFAg0j82
+# v0v7R+/8a70S2V4t7yKYKSsQGI6pvt1A8JGmuZyjmIXmw23AkI5bZkxvSgws8rrB
+# tJw9vakEckcWFQb7JG6b618x0s9Q3DL0dRq46QZRnm7U6234lecvjstAow30dP0T
+# nIacPWKpPc3QgB+WDnglN2fdT1ruQ6WIVBenmpjpG9ypRANKUx5NRcpdJAQW2FqE
+# HTS3Ntb+0tCqIkNHJ5aFsF6ehRovWZp0MYIz9bpJHix0VrjdLVMOpe7wv62t90E3
+# UrE2KmVwpQ5wsMD6YUscoCsSRQZrA5AbwTOCZJpeG2z3vDo/huvPK8TeTJ2Ltu/I
+# tXgxIlIOQp/tbHAiN8Xptw/JmIZg9edQ/FiDaIIwG5YHsfm2u7TwOFyd6OqLw18Z
+# 5j/IvDPzlkwWJxk6RHJF5dS4s3fnyLw3DHBe5Dav6KYB4n8x/cEmD/R44/8gS5Pf
+# uG1srjLdyyGtyh0KiRDSmjw+fa7i1VPoemidDWNZ7ksNadMad4ZoDvgkqOV4A6a+
+# N8HIc/P6g0irrezLWUgbKXSN8iH9RP+WJFx5fBHE4AFxrbAUQ2Zn5jDmHAI3wYcQ
+# DnnEYP51A75WFwPsvBrfrb1+6a1fuTEH1AYdOOMy8fX8xKo0E0Ys+7bxIvFPsUpS
+# zfFjBolmhzCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI
+# hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
+# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
+# MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy
+# MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC
+# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
+# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
+# bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
+# AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg
+# M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF
+# dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6
+# GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp
+# Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu
+# yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E
+# XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0
+# lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q
+# GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ
+# +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA
+# PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw
+# EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG
+# NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV
+# MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj
+# cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK
+# BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
+# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
+# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
+# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
+# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
+# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG
+# 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x
+# M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC
+# VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449
+# xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM
+# nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS
+# PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d
+# Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn
+# GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs
+# QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL
+# jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL
+# 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNQ
+# MIICOAIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
+# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
+# b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn
+# MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjdGMDAtMDVFMC1EOTQ3MSUwIwYDVQQD
+# ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQBO
+# Ei+S/ZVFe6w1Id31m6Kge26lNKCBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD
+# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
+# IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6JpN3jAiGA8yMDIzMDgzMDIzMjMx
+# MFoYDzIwMjMwODMxMjMyMzEwWjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDomk3e
+# AgEAMAoCAQACAiLLAgH/MAcCAQACAhLTMAoCBQDom59eAgEAMDYGCisGAQQBhFkK
+# BAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJ
+# KoZIhvcNAQELBQADggEBAC/zAZ9IEqD3nHBUWwxFTfnDSRErnCJ6XGxH5jOVtR0t
+# 9pi7yCpApLpA2D12g1lcv4ugnwGpsbwmFjrcF4WlHemRa77qv409xNhNKrnh3H+U
+# X2hvy9Utp9LiJiqS7lOW5VN1Uv+LbnA+FWt//4J+YLv44D/dliUGjYX623X7KiEX
+# dbdXPR/Sn+W2YVQ19O8liKaFDnDnIAz+WLCfL6EaoGu4Te/Mr65Khy3YWTwQfXxr
+# gR/JMDzLzWossnGszYCN8S8d9X6mfzWuYv4JHLEiThW++WbMLeT2hhKPomcbvqU4
+# wPb/ylDrTrWuAr/fVndECXVjCIzYJiFwOWn/ZfN9FpQxggQNMIIECQIBATCBkzB8
+# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
+# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N
+# aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAdWpAs/Fp8npWgABAAAB
+# 1TANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE
+# MC8GCSqGSIb3DQEJBDEiBCAHS+EjBkJ/YPugQQv1D7eXVQOI4DWPIYpUxpssheDN
+# lTCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EINm/I4YM166JMM7EKIcYvlcb
+# r2CHjKC0LUOmpZIbBsH/MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
+# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
+# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
+# IDIwMTACEzMAAAHVqQLPxafJ6VoAAQAAAdUwIgQgBVj4mNvMkXn0xEeEe3i6urr/
+# L5Eh2p4uPk3zOcR/jHIwDQYJKoZIhvcNAQELBQAEggIAiKbnM5VQAwi5gWDevpEJ
+# oHiZRrb0HApEUkca54Oye2+5tyRfWo3ruZvLZueHWsbEzYXclOscHVPjyR1t6s+p
+# W13utx4mDab1BaJCBq1q9IH1HDfCf/kSMb0H02UDNBumrt1s5/oaiiY75S3kigpt
+# XHCKV/AKbi+4vORYdBwJhWvy4kug+zZHWAp7+K4rVeAaMbWOitriFPQaSdZlOkNT
+# VKMbYUtX6VEnhyfr5O390TxPJCdkDznT6rAf5FlQEkim7wb2gb/Osi73KF0dtnRd
+# 0ePNT4GxulYTrAhHsZmyuinh/FIyqJDtW3C+2eVt/lx6GJfAtDA/gVCe9mAL4bYv
+# 7vjocHeLvWJ8bc0PYMCRZXMsU2zkJqVJ7pZWq+z5hGQAHemrQj9hUZBXCEFVZ8dd
+# jEZbIsg2nwZOFakhvdAvvGlTXPbRMOCHblToXAKA8ksRbLob8CDL6Cstoy5VL3al
+# fOAWLj3FYITlvGvAYCtJzHrHdqyphL805Co1syR6YDopR8tDrxgWzJKAby5fIolP
+# 7SfunXsCa0n3xx80aaxR0apljIXZWBSMGZdJmVzASQFxlexNsU1PmaLmmpn0fih/
+# 567e+kyzFG1wiw5btwttSW9hKgCX+yze1B4IK4yquTQSfPikrCpbBaFZ0OlWmOnL
+# r0eKAKY3WDyxYgNyeg6KeCc=
+# SIG # End signature block
diff --git a/src/Paramore.Brighter.Analyzer/AnalyzerReleases.Shipped.md b/src/Paramore.Brighter.Analyzer/AnalyzerReleases.Shipped.md
new file mode 100644
index 0000000000..a32c29b2e2
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/AnalyzerReleases.Shipped.md
@@ -0,0 +1,11 @@
+## Release 1.0
+
+### New Rules
+
+Rule ID | Category | Severity | Notes
+--------|----------|----------|--------------------
+BRT001 | Design | Warning | ([BRT001](./docs/BRT001.md)) Request Type assignment is Missing
+BRT002 | Design | Warning | ([BRT002](./docs/BRT002.md)) RequestType is not child of IRequest
+BRT003 | Design | Warning | ([BRT003](./docs/BRT003.md)) MessagePump assignment is Missing
+BRT004 | Design | Warning | ([BRT004](./docs/BRT004.md)) Wrap attribute is applied to wrong Method
+BRT005 | Design | Warning | ([BRT005](./docs/BRT005.md)) UnWrap attribute is applied to wrong Method
\ No newline at end of file
diff --git a/src/Paramore.Brighter.Analyzer/AnalyzerReleases.Unshipped.md b/src/Paramore.Brighter.Analyzer/AnalyzerReleases.Unshipped.md
new file mode 100644
index 0000000000..5f282702bb
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/AnalyzerReleases.Unshipped.md
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/Paramore.Brighter.Analyzer/Analyzers/PublicationRequestTypeAssignmentAnalyzer.cs b/src/Paramore.Brighter.Analyzer/Analyzers/PublicationRequestTypeAssignmentAnalyzer.cs
new file mode 100644
index 0000000000..6ecb90de3a
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/Analyzers/PublicationRequestTypeAssignmentAnalyzer.cs
@@ -0,0 +1,81 @@
+#region License
+/* The MIT License (MIT)
+Copyright © 2026 Aboubakr Nasef
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the “Software”), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+#endregion
+
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Paramore.Brighter.Analyzer.Visitors.Operation;
+
+namespace Paramore.Brighter.Analyzer.Analyzers
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class PublicationRequestTypeAssignmentAnalyzer : DiagnosticAnalyzer
+ {
+ private const string RequestPublicationCategory = "Design";
+
+ public static DiagnosticDescriptor RequestTypeMissingRule = new DiagnosticDescriptor(
+ id: DiagnosticsIds.RequestTypeMissing,
+ title: "Request Type Missing",
+ messageFormat: "RequestType assignment is Missing from {0}",
+ category: RequestPublicationCategory,
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true
+ );
+ public static DiagnosticDescriptor WrongRequestTypeRule = new DiagnosticDescriptor(
+ id: DiagnosticsIds.WrongRequestType,
+ title: "Wrong Request Type",
+ messageFormat: "RequestType '{0}' is not child of IRequest",
+ category: RequestPublicationCategory,
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true
+ );
+ public override ImmutableArray SupportedDiagnostics { get { return [RequestTypeMissingRule, WrongRequestTypeRule]; } }
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+ context.RegisterOperationAction(AnalyzerOperation, OperationKind.ObjectCreation);
+ }
+
+ private void AnalyzerOperation(OperationAnalysisContext context)
+ {
+ var visitor = new RequestTypeAssignmentVisitor();
+ context.Operation.Accept(visitor);
+ if (visitor.IsPublicationType && !visitor.IsRequestTypeAssigned)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ RequestTypeMissingRule,
+ context.Operation.Syntax.GetLocation(),
+ visitor.PublicationName));
+ }
+ else if (visitor.IsNotTypeOfIRequest)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ WrongRequestTypeRule,
+ visitor.TypeOfLocation,
+ visitor.TypeOfName));
+ }
+ }
+ }
+}
diff --git a/src/Paramore.Brighter.Analyzer/Analyzers/SubscriptionConstructorAnalyzer.cs b/src/Paramore.Brighter.Analyzer/Analyzers/SubscriptionConstructorAnalyzer.cs
new file mode 100644
index 0000000000..9c03dbf795
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/Analyzers/SubscriptionConstructorAnalyzer.cs
@@ -0,0 +1,68 @@
+#region License
+/* The MIT License (MIT)
+Copyright © 2026 Aboubakr Nasef
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the “Software”), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+#endregion
+
+using System;
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Paramore.Brighter.Analyzer.Visitors.Operation;
+
+namespace Paramore.Brighter.Analyzer.Analyzers
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class SubscriptionConstructorAnalyzer : DiagnosticAnalyzer
+ {
+ private const string SubscriptionCategory = "Design";
+
+ public static DiagnosticDescriptor MessagePumpMissingRule = new DiagnosticDescriptor(
+ id: DiagnosticsIds.MessagePumpMissing,
+ title: "MessagePump Missing",
+ messageFormat: "MessagePump assignment is Missing from {0}",
+ category: SubscriptionCategory,
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true
+ );
+ public override ImmutableArray SupportedDiagnostics => [MessagePumpMissingRule];
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+ context.RegisterOperationAction(AnalyzeOperation, OperationKind.ObjectCreation);
+ }
+
+ private void AnalyzeOperation(OperationAnalysisContext context)
+ {
+ var visitor = new SubscriptionConstructorVisitor();
+ context.Operation.Accept(visitor);
+ if (visitor.IsSubscriptionType && visitor.IsMessagePumpDefault)
+ {
+ var diagnostic = Diagnostic.Create(
+ MessagePumpMissingRule,
+ context.Operation.Syntax.GetLocation(),
+ visitor.SubscriptionName);
+ context.ReportDiagnostic(diagnostic);
+ }
+ }
+ }
+}
diff --git a/src/Paramore.Brighter.Analyzer/Analyzers/WrapAttributeAnalyzer.cs b/src/Paramore.Brighter.Analyzer/Analyzers/WrapAttributeAnalyzer.cs
new file mode 100644
index 0000000000..0c603c7971
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/Analyzers/WrapAttributeAnalyzer.cs
@@ -0,0 +1,78 @@
+#region License
+/* The MIT License (MIT)
+Copyright © 2026 Aboubakr Nasef
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the “Software”), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+#endregion
+
+using System;
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Paramore.Brighter.Analyzer.Visitors.Operation;
+using Paramore.Brighter.Analyzer.Visitors.Symbol;
+
+namespace Paramore.Brighter.Analyzer.Analyzers
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class WrapAttributeAnalyzer : DiagnosticAnalyzer
+ {
+ private const string WrapAttributeCategory = "Design";
+
+ public static DiagnosticDescriptor WrapAttributeRule = new DiagnosticDescriptor(
+ id: DiagnosticsIds.WrapWithAttribute,
+ title: "WrapWithAttribute",
+ messageFormat: $"{{0}} should be applied '{BrighterAnalyzerGlobals.MapToMessage}' Method",
+ category: WrapAttributeCategory,
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true
+ );
+ public static DiagnosticDescriptor UnWrapWithAttributeRule = new DiagnosticDescriptor(
+ id: DiagnosticsIds.UnWrapWithAttribute,
+ title: "UnWrapWithAttribute",
+ messageFormat: $"{{0}} should be applied '{BrighterAnalyzerGlobals.MapToRequest}' Method",
+ category: WrapAttributeCategory,
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true
+ );
+ public override ImmutableArray SupportedDiagnostics => [WrapAttributeRule, UnWrapWithAttributeRule];
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+ context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method);
+ }
+
+ private void AnalyzeSymbol(SymbolAnalysisContext context)
+ {
+ var visitor = new WrapAttributeSymbolVisitor();
+ context.Symbol.Accept(visitor);
+ foreach (var item in visitor.WrongWrappedMapToRequest)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(WrapAttributeRule, item.Location, item.Name));
+ }
+ foreach (var item in visitor.WrongWrappedMapToMessage)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(UnWrapWithAttributeRule, item.Location, item.Name));
+ }
+
+ }
+ }
+}
diff --git a/src/Paramore.Brighter.Analyzer/BrighterAnalyzerGlobals.cs b/src/Paramore.Brighter.Analyzer/BrighterAnalyzerGlobals.cs
new file mode 100644
index 0000000000..4ad546154d
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/BrighterAnalyzerGlobals.cs
@@ -0,0 +1,43 @@
+#region License
+/* The MIT License (MIT)
+Copyright © 2026 Aboubakr Nasef
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the “Software”), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+
+#endregion
+
+namespace Paramore.Brighter.Analyzer
+{
+ public class BrighterAnalyzerGlobals
+ {
+ public const string PublicationClassName = "Publication";
+ public const string BrighterAssembly = "Paramore.Brighter";
+ public const string RequestTypeProperty = "RequestType";
+ public const string IRequestInterface = "IRequest";
+
+ public const string MessagePumpTypeEnumName = "MessagePumpType";
+ public const string SubscriptionClassName = "Subscription";
+
+ public const string MessageMapperInterface = "IAmAMessageMapper";
+ public const string UnwrapWithAttribute = "UnwrapWithAttribute";
+ public const string WrapWithAttribute = "WrapWithAttribute";
+ public const string MapToMessage = "MapToMessage";
+ public const string MapToRequest = "MapToRequest";
+ }
+}
diff --git a/src/Paramore.Brighter.Analyzer/DiagnosticsIds.cs b/src/Paramore.Brighter.Analyzer/DiagnosticsIds.cs
new file mode 100644
index 0000000000..96e86e3c18
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/DiagnosticsIds.cs
@@ -0,0 +1,36 @@
+#region License
+/* The MIT License (MIT)
+Copyright © 2026 Aboubakr Nasef
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the “Software”), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+
+#endregion
+
+
+namespace Paramore.Brighter.Analyzer
+{
+ public static class DiagnosticsIds
+ {
+ public const string RequestTypeMissing = "BRT001";
+ public const string WrongRequestType = "BRT002";
+ public const string MessagePumpMissing = "BRT003";
+ public const string WrapWithAttribute = "BRT004";
+ public const string UnWrapWithAttribute = "BRT005";
+ }
+}
diff --git a/src/Paramore.Brighter.Analyzer/Paramore.Brighter.Analyzer.csproj b/src/Paramore.Brighter.Analyzer/Paramore.Brighter.Analyzer.csproj
new file mode 100644
index 0000000000..71264c5b3a
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/Paramore.Brighter.Analyzer.csproj
@@ -0,0 +1,16 @@
+
+
+
+ $(BrighterNetStandardTargetFrameworks)
+ Aboubakr Nasef
+ Analyzers for the brighter library
+ Analyzer;Scheduler;Message Scheduling;Command Processor;Brighter
+ false
+ true
+ true
+
+
+
+
+
+
diff --git a/src/Paramore.Brighter.Analyzer/Visitors/Operation/RequestTypeAssignmentVisitor.cs b/src/Paramore.Brighter.Analyzer/Visitors/Operation/RequestTypeAssignmentVisitor.cs
new file mode 100644
index 0000000000..cc276c4ed1
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/Visitors/Operation/RequestTypeAssignmentVisitor.cs
@@ -0,0 +1,70 @@
+#region License
+/* The MIT License (MIT)
+Copyright © 2026 Aboubakr Nasef
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the “Software”), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+
+#endregion
+
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Operations;
+using Paramore.Brighter.Analyzer.Visitors.Symbol;
+
+
+namespace Paramore.Brighter.Analyzer.Visitors.Operation
+{
+ public class RequestTypeAssignmentVisitor : OperationWalker
+ {
+ public bool IsPublicationType { get; private set; }
+ public bool IsRequestTypeAssigned { get; private set; }
+ public string PublicationName { get; private set; }
+ public bool IsNotTypeOfIRequest { get; private set; }
+ public Location TypeOfLocation { get; private set; }
+ public string TypeOfName { get; private set; }
+
+ public override void VisitObjectCreation(IObjectCreationOperation operation)
+ {
+ if (operation.Type!.Accept(new ChildOfVisitor(BrighterAnalyzerGlobals.PublicationClassName, BrighterAnalyzerGlobals.BrighterAssembly)))
+ {
+ PublicationName = operation.Type.Name;
+ IsPublicationType = true;
+ base.VisitObjectCreation(operation);
+ }
+ }
+ public override void VisitPropertyReference(IPropertyReferenceOperation operation)
+ {
+ if (operation.Property.Name == BrighterAnalyzerGlobals.RequestTypeProperty)
+ {
+ IsRequestTypeAssigned = true;
+ base.VisitPropertyReference(operation);
+ }
+ }
+ public override void VisitTypeOf(ITypeOfOperation operation)
+ {
+ if (!operation.TypeOperand.AllInterfaces.Any(i => i.Name == BrighterAnalyzerGlobals.IRequestInterface && i.ContainingAssembly.Name == BrighterAnalyzerGlobals.BrighterAssembly))
+ {
+ TypeOfLocation = operation.Syntax.GetLocation();
+ TypeOfName = operation.TypeOperand.Name;
+ IsNotTypeOfIRequest = true;
+ }
+ base.VisitTypeOf(operation);
+ }
+ }
+}
diff --git a/src/Paramore.Brighter.Analyzer/Visitors/Operation/SubscriptionConstructorVisitor.cs b/src/Paramore.Brighter.Analyzer/Visitors/Operation/SubscriptionConstructorVisitor.cs
new file mode 100644
index 0000000000..58fd98a830
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/Visitors/Operation/SubscriptionConstructorVisitor.cs
@@ -0,0 +1,53 @@
+#region License
+/* The MIT License (MIT)
+Copyright © 2026 Aboubakr Nasef
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the “Software”), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+
+#endregion
+
+using Microsoft.CodeAnalysis.Operations;
+using Paramore.Brighter.Analyzer.Visitors.Symbol;
+
+namespace Paramore.Brighter.Analyzer.Visitors.Operation
+{
+ public class SubscriptionConstructorVisitor : OperationWalker
+ {
+ public bool IsMessagePumpDefault { get; private set; } = false;
+ public string SubscriptionName { get; private set; }
+ public bool IsSubscriptionType { get; private set; }
+
+ public override void VisitObjectCreation(IObjectCreationOperation operation)
+ {
+ if (operation.Type!.Accept(new ChildOfVisitor(BrighterAnalyzerGlobals.SubscriptionClassName, BrighterAnalyzerGlobals.BrighterAssembly)))
+ {
+ SubscriptionName = operation.Type.Name;
+ IsSubscriptionType = true;
+ base.VisitObjectCreation(operation);
+ }
+ }
+ public override void VisitArgument(IArgumentOperation operation)
+ {
+ if (operation.Value.Type.Name == BrighterAnalyzerGlobals.MessagePumpTypeEnumName && operation.ArgumentKind == ArgumentKind.DefaultValue)
+ {
+ IsMessagePumpDefault = true;
+ }
+ }
+ }
+}
diff --git a/src/Paramore.Brighter.Analyzer/Visitors/Symbol/ChildOfVisitor.cs b/src/Paramore.Brighter.Analyzer/Visitors/Symbol/ChildOfVisitor.cs
new file mode 100644
index 0000000000..c535b4d64f
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/Visitors/Symbol/ChildOfVisitor.cs
@@ -0,0 +1,45 @@
+#region License
+/* The MIT License (MIT)
+Copyright © 2026 Aboubakr Nasef
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the “Software”), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+
+#endregion
+using Microsoft.CodeAnalysis;
+
+namespace Paramore.Brighter.Analyzer.Visitors.Symbol
+{
+ public class ChildOfVisitor(string className, string assmeblyName) : SymbolVisitor
+ {
+ public override bool VisitNamedType(INamedTypeSymbol symbol)
+ {
+ var type = symbol;
+ var assembly = type.ContainingAssembly;
+ if (symbol.Name == className && assembly.Name == assmeblyName)
+ {
+ return true;
+ }
+ if (symbol.BaseType != null)
+ {
+ return symbol.BaseType.Accept(this);
+ }
+ return false;
+ }
+ }
+}
diff --git a/src/Paramore.Brighter.Analyzer/Visitors/Symbol/WrapAttributeSymbolVisitor.cs b/src/Paramore.Brighter.Analyzer/Visitors/Symbol/WrapAttributeSymbolVisitor.cs
new file mode 100644
index 0000000000..233f31ffa8
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/Visitors/Symbol/WrapAttributeSymbolVisitor.cs
@@ -0,0 +1,73 @@
+#region License
+/* The MIT License (MIT)
+Copyright © 2026 Aboubakr Nasef
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the “Software”), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. */
+
+#endregion
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using Microsoft.CodeAnalysis;
+using Paramore.Brighter.Analyzer.Visitors.Symbol;
+
+namespace Paramore.Brighter.Analyzer.Visitors.Symbol
+{
+ public class WrapAttributeSymbolVisitor : SymbolVisitor
+ {
+ private List<(string Name, Location Location)> _wrongWrappedMapToRequest = new();
+ public IReadOnlyList<(string Name, Location Location)> WrongWrappedMapToRequest => _wrongWrappedMapToRequest;
+ private List<(string Name, Location Location)> _wrongWrappedMapToMessage = new();
+ public IReadOnlyList<(string Name, Location Location)> WrongWrappedMapToMessage => _wrongWrappedMapToMessage;
+ public override void VisitMethod(IMethodSymbol symbol)
+ {
+ var isMessageMapperType = symbol.ContainingType.AllInterfaces
+ .Any(c => c.Name.StartsWith(BrighterAnalyzerGlobals.MessageMapperInterface)
+ && c.ContainingAssembly.Name.Equals(BrighterAnalyzerGlobals.BrighterAssembly));
+ if (!isMessageMapperType)
+ return;
+
+ if (symbol.Name.StartsWith(BrighterAnalyzerGlobals.MapToRequest))
+ {
+ ReportInvalidAttributes(symbol, BrighterAnalyzerGlobals.WrapWithAttribute, _wrongWrappedMapToRequest);
+ }
+ else if (symbol.Name.StartsWith(BrighterAnalyzerGlobals.MapToMessage))
+ {
+ ReportInvalidAttributes(symbol, BrighterAnalyzerGlobals.UnwrapWithAttribute, _wrongWrappedMapToMessage);
+ }
+
+ base.VisitMethod(symbol);
+ }
+ private void ReportInvalidAttributes(IMethodSymbol symbol, string invalidAttributeBase, List<(string, Location)> collection)
+ {
+ var visitor = new ChildOfVisitor(invalidAttributeBase, BrighterAnalyzerGlobals.BrighterAssembly);
+
+ foreach (var attr in symbol.GetAttributes())
+ {
+ if (attr.AttributeClass?.Accept(visitor) == true)
+ {
+ var location = attr.ApplicationSyntaxReference?.GetSyntax()?.GetLocation() ?? symbol.Locations[0];
+ collection.Add((attr.AttributeClass.Name, location));
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/Paramore.Brighter.Analyzer/docs/BRT001.md b/src/Paramore.Brighter.Analyzer/docs/BRT001.md
new file mode 100644
index 0000000000..e0bae21518
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/docs/BRT001.md
@@ -0,0 +1,24 @@
+# BRT001: Request Type Missing
+
+## Description
+This rule ensures that a `Publication` object creation has a `RequestType` assigned. Without specifying the request type, Brighter cannot determine which message type this publication or subscription is for.
+
+## Why is this a warning?
+Failing to specify the `RequestType` will lead to runtime errors or incorrect message routing, as the system won't know how to handle the publication or subscription.
+
+## How to fix
+Assign a valid request type to the `RequestType` property during object initialization.
+
+### Example
+```csharp
+// Warning: RequestType is missing
+
+var publication = new Publication {
+ // Missing RequestType
+};
+
+// Fixed: RequestType is assigned
+var publication = new Publication {
+ RequestType = typeof(MyRequest)
+};
+```
diff --git a/src/Paramore.Brighter.Analyzer/docs/BRT002.md b/src/Paramore.Brighter.Analyzer/docs/BRT002.md
new file mode 100644
index 0000000000..07e1d32260
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/docs/BRT002.md
@@ -0,0 +1,23 @@
+# BRT002: Wrong Request Type
+
+## Description
+This rule ensures that the type assigned to `RequestType` in a `Publication` implements the `IRequest` interface.
+
+## Why is this a warning?
+Brighter requires all request types to implement `IRequest`. Using a type that doesn't implement this interface will cause internal errors when attempting to route messages.
+
+## How to fix
+Ensure the type used in `RequestType` implements `Paramore.Brighter.IRequest`.
+
+### Example
+```csharp
+// Error: MyType does not implement IRequest
+var publication = new Publication {
+ RequestType = typeof(MyType)
+};
+
+// Fixed: MyRequest implements IRequest
+var publication = new Publication {
+ RequestType = typeof(MyRequest)
+};
+```
diff --git a/src/Paramore.Brighter.Analyzer/docs/BRT003.md b/src/Paramore.Brighter.Analyzer/docs/BRT003.md
new file mode 100644
index 0000000000..070998d35f
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/docs/BRT003.md
@@ -0,0 +1,25 @@
+# BRT003: MessagePump Missing
+
+## Description
+This rule identifies when a `Subscription` is created without specifying a `MessagePumpType`.
+
+## Why is this a warning?
+A subscription needs a message pump to process incoming messages. If not specified, the subscription will not function as expected in a service host environment.
+
+## How to fix
+Assign a valid message pump type to the `MessagePumpType` property during initialization.
+
+### Example
+```csharp
+// Error: MessagePumpType is missing
+var subscription = new Subscription(
+"MySubscription", ...
+);
+
+// Fixed: MessagePumpType is assigned
+var subscription = new Subscription(
+ "MySubscription",
+ messagePumpType: MessagePumpType.Reactor,
+ ...
+);
+```
diff --git a/src/Paramore.Brighter.Analyzer/docs/BRT004.md b/src/Paramore.Brighter.Analyzer/docs/BRT004.md
new file mode 100644
index 0000000000..5071f3fad5
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/docs/BRT004.md
@@ -0,0 +1,43 @@
+# BRT004: WrapWithAttribute Correct Usage
+
+## Description
+This rule ensures that the "Wrap" attributes are correctly applied to methods that map domain requests back to messages (typically `MapToMessage`).
+
+## Why is this a warning?
+Applying a "Wrap" attribute to the wrong mapping method can lead to unexpected behavior in the middleware pipeline, such as attempting to wrap a message into a request when it's already a request, or vice-versa.
+
+## How to fix
+Ensure that attributes intended for wrapping are applied to the `MapToMessage` method.
+
+### Example
+Check your `IAmAMessageMapper ,IAmAMessageMapperAsync` implementation to ensure attributes are on the correct method.
+```csharp
+ public class WrapWithMapperSample: IAmAMessageMapper
+ {
+ /* ommited code */
+
+ // Error: Attributes should be assigned to the MapToMessage method
+ [Compress(0)]
+ [ClaimCheck(0)]
+ public SampleEvent MapToRequest(Message message)
+ {
+ /* ommited code*/
+ }
+ /* ommited code */
+ }
+
+ // Fixed: Attributes assigned to the MapToMessage method
+ public class WrapWithMapperSample: IAmAMessageMapper
+ {
+ /* ommited code */
+
+ [Compress(0)]
+ [ClaimCheck(0)]
+ public Message MapToMessage(SampleEvent request, Publication publication)
+ {
+ /* ommited code*/
+ }
+
+ /* ommited code */
+ }
+```
diff --git a/src/Paramore.Brighter.Analyzer/docs/BRT005.md b/src/Paramore.Brighter.Analyzer/docs/BRT005.md
new file mode 100644
index 0000000000..bf25e7eb17
--- /dev/null
+++ b/src/Paramore.Brighter.Analyzer/docs/BRT005.md
@@ -0,0 +1,42 @@
+# BRT005: UnWrapWithAttribute Correct Usage
+
+## Description
+This rule ensures that "Unwrap" attributes are correctly applied to methods that map messages to domain requests (typically `MapToRequest`).
+
+## Why is this a warning?
+Applying an "Unwrap" attribute to the wrong mapping method can disrupt the flow of message processing in Brighter's pipeline.
+
+## How to fix
+Ensure that attributes intended for unwrapping are applied to the `MapToRequest` method.
+
+### Example
+Check your `IAmAMessageMapper , IAmAMessageMapperAsync` implementation to ensure attributes are on the correct method.
+```csharp
+ public class UnwrapWithMapperSample: IAmAMessageMapper
+ {
+ /*
+ ommited code
+ */
+
+ // Error: Attributes should be assigned to the MapToRequest method
+ [Decompress(0)]
+ public Message MapToMessage(SampleEvent request, Publication publication)
+ {
+ /* ommited code*/
+ }
+ /* ommited code */
+ }
+ // Fixed: Attributes assigned to the MapToRequest method
+ public class UnwrapWithMapperSample: IAmAMessageMapper
+ {
+ /* ommited code */
+
+ [Decompress(0)]
+ public SampleEvent MapToRequest(Message message)
+ {
+ /* ommited code*/
+ }
+
+ /* ommited code */
+ }
+```
diff --git a/src/Paramore.Brighter.ServiceActivator.Control.Api/Paramore.Brighter.ServiceActivator.Control.Api.csproj b/src/Paramore.Brighter.ServiceActivator.Control.Api/Paramore.Brighter.ServiceActivator.Control.Api.csproj
index 092ad45f5a..428c4eabd0 100644
--- a/src/Paramore.Brighter.ServiceActivator.Control.Api/Paramore.Brighter.ServiceActivator.Control.Api.csproj
+++ b/src/Paramore.Brighter.ServiceActivator.Control.Api/Paramore.Brighter.ServiceActivator.Control.Api.csproj
@@ -1,4 +1,4 @@
-
+
ASP.NET Core API extensions for Paramore.Brighter Service Activator Control module. Provides REST endpoints for querying service activator node status and health information.
@@ -16,8 +16,4 @@
-
-
-
-
diff --git a/tests/Paramore.Brighter.Analyzer.Tests/Analyzers/BaseAnalyzerTest.cs b/tests/Paramore.Brighter.Analyzer.Tests/Analyzers/BaseAnalyzerTest.cs
new file mode 100644
index 0000000000..7f3e4da868
--- /dev/null
+++ b/tests/Paramore.Brighter.Analyzer.Tests/Analyzers/BaseAnalyzerTest.cs
@@ -0,0 +1,24 @@
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Testing;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Testing;
+using Paramore.Brighter.Analyzer.Analyzers;
+
+namespace Paramore.Brighter.Analyzer.Tests.Analyzers
+{
+ public abstract class BaseAnalyzerTest where T : DiagnosticAnalyzer, new()
+ {
+ protected CSharpAnalyzerTest testContext;
+ protected BaseAnalyzerTest()
+ {
+ testContext = new CSharpAnalyzerTest
+ {
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net90
+ };
+ testContext.TestState.OutputKind = OutputKind.ConsoleApplication;
+ testContext.TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(Publication).Assembly.Location));
+ testContext.CompilerDiagnostics = CompilerDiagnostics.None;
+ }
+ }
+}
diff --git a/tests/Paramore.Brighter.Analyzer.Tests/Analyzers/PublicationRequestTypeAssignmentAnalyzerTest.cs b/tests/Paramore.Brighter.Analyzer.Tests/Analyzers/PublicationRequestTypeAssignmentAnalyzerTest.cs
new file mode 100644
index 0000000000..a8e904138a
--- /dev/null
+++ b/tests/Paramore.Brighter.Analyzer.Tests/Analyzers/PublicationRequestTypeAssignmentAnalyzerTest.cs
@@ -0,0 +1,98 @@
+using Microsoft.CodeAnalysis.Testing;
+using Paramore.Brighter.Analyzer.Analyzers;
+using Paramore.Brighter.Analyzer.Tests.Analyzers;
+
+
+namespace Paramore.Brighter.Analyzer.Test.Analyzers
+{
+ public class PublicationRequestTypeAssignmentAnalyzerTest : BaseAnalyzerTest
+ {
+
+ [Fact]
+ public async Task When_Initializing_Publication_WithOut_RequestType()
+ {
+
+ testContext.TestCode = /* lang=c#-test */ """
+using Paramore.Brighter;
+namespace TestNamespace
+{
+var publication = {|#0:new PublicationTest()|};
+
+ public class PublicationTest : Publication
+ {
+ }
+ }
+""";
+
+ testContext.ExpectedDiagnostics.Add(new DiagnosticResult(PublicationRequestTypeAssignmentAnalyzer.RequestTypeMissingRule).WithLocation(0).WithArguments("PublicationTest"));
+ await testContext.RunAsync();
+ }
+
+ [Fact]
+ public async Task When_Initializing_Publication_With_Right_RequestType()
+ {
+ testContext.TestCode = /* lang=c#-test */ """
+using Paramore.Brighter;
+namespace TestNamespace
+{
+var publication =new PublicationTest()
+{
+RequestType = typeof(EventSample)
+};
+
+ public class PublicationTest : Publication
+ {
+ }
+ public class EventSample : Event
+{
+ public EventSample(Id id) : base(id)
+ {
+ }
+}
+ }
+""";
+ await testContext.RunAsync();
+ }
+
+ [Fact]
+ public async Task When_Initializing_Publication_With_Wrong_RequestType()
+ {
+ testContext.TestCode = /* lang=c#-test */ """
+using Paramore.Brighter;
+namespace TestNamespace
+{
+var publication = new PublicationTest()
+{
+RequestType = {|#0:typeof(EventSample)|}
+};
+
+ public class PublicationTest : Publication
+ {}
+ public class EventSample{}
+ }
+""";
+ testContext.ExpectedDiagnostics.Add(new DiagnosticResult(PublicationRequestTypeAssignmentAnalyzer.WrongRequestTypeRule).WithLocation(0).WithArguments("EventSample"));
+
+ await testContext.RunAsync();
+ }
+
+ [Fact]
+ public async Task When_Initializing_Non_Publication_Type()
+ {
+ testContext.TestCode = /* lang=c#-test */ """
+ using System.Collections.Generic;
+ namespace TestNamespace
+ {
+ public class AnyClass
+ {
+ public void Method()
+ {
+ var list = new List(); // Should be ignored
+ }
+ }
+ }
+""";
+ await testContext.RunAsync();
+ }
+ }
+}
diff --git a/tests/Paramore.Brighter.Analyzer.Tests/Analyzers/SubscriptionConstructorAnalyzerTest.cs b/tests/Paramore.Brighter.Analyzer.Tests/Analyzers/SubscriptionConstructorAnalyzerTest.cs
new file mode 100644
index 0000000000..8d2bf4d8a2
--- /dev/null
+++ b/tests/Paramore.Brighter.Analyzer.Tests/Analyzers/SubscriptionConstructorAnalyzerTest.cs
@@ -0,0 +1,83 @@
+using Microsoft.CodeAnalysis.Testing;
+using Paramore.Brighter.Analyzer.Analyzers;
+using Paramore.Brighter.Analyzer.Tests.Analyzers;
+
+namespace Paramore.Brighter.Analyzer.Test.Analyzers
+{
+ public class SubscriptionConstructorAnalyzerTest : BaseAnalyzerTest
+ {
+ [Fact]
+ public async Task When_Initializing_Subscription_With_MessagePump()
+ {
+
+ testContext.TestCode = /* lang=c#-test */ """
+ using Paramore.Brighter;
+ namespace TestNamespace
+ {
+ var subscription = {|#0:new SubscriptionTest("name", "name", "key", messagePumpType: MessagePumpType.Reactor)|};
+
+ public class SubscriptionTest : Subscription
+ {
+ public SubscriptionTest(SubscriptionName subscriptionName, ChannelName channelName, RoutingKey routingKey, Type? requestType = null, Func? getRequestType = null, int bufferSize = 1, int noOfPerformers = 1, TimeSpan? timeOut = null, int requeueCount = -1, TimeSpan? requeueDelay = null, int unacceptableMessageLimit = 0, MessagePumpType messagePumpType = MessagePumpType.Unknown, IAmAChannelFactory? channelFactory = null, OnMissingChannel makeChannels = OnMissingChannel.Create, TimeSpan? emptyChannelDelay = null, TimeSpan? channelFailureDelay = null) : base(subscriptionName, channelName, routingKey, requestType, getRequestType, bufferSize, noOfPerformers, timeOut, requeueCount, requeueDelay, unacceptableMessageLimit, messagePumpType, channelFactory, makeChannels, emptyChannelDelay, channelFailureDelay)
+ {
+ }
+ }
+ }
+""";
+
+ await testContext.RunAsync();
+ }
+
+ [Fact]
+ public async Task When_Initializing_Subscription_WithOut_MessagePump()
+ {
+
+ testContext.TestCode = /* lang=c#-test */ """
+ using Paramore.Brighter;
+ namespace TestNamespace
+ {
+ var subscription = {|#0:new SubscriptionTest("name", "name", "key")|};
+
+ public class SubscriptionTest : Subscription
+ {
+ public SubscriptionTest(SubscriptionName subscriptionName, ChannelName channelName, RoutingKey routingKey, Type? requestType = null, Func? getRequestType = null, int bufferSize = 1, int noOfPerformers = 1, TimeSpan? timeOut = null, int requeueCount = -1, TimeSpan? requeueDelay = null, int unacceptableMessageLimit = 0, MessagePumpType messagePumpType = MessagePumpType.Unknown, IAmAChannelFactory? channelFactory = null, OnMissingChannel makeChannels = OnMissingChannel.Create, TimeSpan? emptyChannelDelay = null, TimeSpan? channelFailureDelay = null) : base(subscriptionName, channelName, routingKey, requestType, getRequestType, bufferSize, noOfPerformers, timeOut, requeueCount, requeueDelay, unacceptableMessageLimit, messagePumpType, channelFactory, makeChannels, emptyChannelDelay, channelFailureDelay)
+ {
+ }
+ }
+ }
+""";
+
+ testContext.ExpectedDiagnostics.Add(new DiagnosticResult(SubscriptionConstructorAnalyzer.MessagePumpMissingRule).WithLocation(0).WithArguments("SubscriptionTest"));
+ await testContext.RunAsync();
+ }
+ [Fact]
+ public async Task When_Initializing_SubscriptionNested_WithOut_MessagePump()
+ {
+
+ testContext.TestCode = /* lang=c#-test */ """
+ using Paramore.Brighter;
+ namespace TestNamespace
+ {
+ var subscription = {|#0:new SubscriptionTestNested("name", "name", "key")|};
+
+ public class SubscriptionTest : Subscription
+ {
+ public SubscriptionTest(SubscriptionName subscriptionName, ChannelName channelName, RoutingKey routingKey, Type? requestType = null, Func? getRequestType = null, int bufferSize = 1, int noOfPerformers = 1, TimeSpan? timeOut = null, int requeueCount = -1, TimeSpan? requeueDelay = null, int unacceptableMessageLimit = 0, MessagePumpType messagePumpType = MessagePumpType.Unknown, IAmAChannelFactory? channelFactory = null, OnMissingChannel makeChannels = OnMissingChannel.Create, TimeSpan? emptyChannelDelay = null, TimeSpan? channelFailureDelay = null) : base(subscriptionName, channelName, routingKey, requestType, getRequestType, bufferSize, noOfPerformers, timeOut, requeueCount, requeueDelay, unacceptableMessageLimit, messagePumpType, channelFactory, makeChannels, emptyChannelDelay, channelFailureDelay)
+ {
+ }
+ }
+ public class SubscriptionTestNested : SubscriptionTest
+ {
+ public SubscriptionTestNested(SubscriptionName subscriptionName, ChannelName channelName, RoutingKey routingKey, Type? requestType = null, Func? getRequestType = null, int bufferSize = 1, int noOfPerformers = 1, TimeSpan? timeOut = null, int requeueCount = -1, TimeSpan? requeueDelay = null, int unacceptableMessageLimit = 0, MessagePumpType messagePumpType = MessagePumpType.Unknown, IAmAChannelFactory? channelFactory = null, OnMissingChannel makeChannels = OnMissingChannel.Create, TimeSpan? emptyChannelDelay = null, TimeSpan? channelFailureDelay = null) : base(subscriptionName, channelName, routingKey, requestType, getRequestType, bufferSize, noOfPerformers, timeOut, requeueCount, requeueDelay, unacceptableMessageLimit, messagePumpType, channelFactory, makeChannels, emptyChannelDelay, channelFailureDelay)
+ {
+ }
+ }
+ }
+""";
+
+ testContext.ExpectedDiagnostics.Add(new DiagnosticResult(SubscriptionConstructorAnalyzer.MessagePumpMissingRule).WithLocation(0).WithArguments("SubscriptionTestNested"));
+ await testContext.RunAsync();
+ }
+
+ }
+}
diff --git a/tests/Paramore.Brighter.Analyzer.Tests/Analyzers/WrapAttributeAnalyzerTest.cs b/tests/Paramore.Brighter.Analyzer.Tests/Analyzers/WrapAttributeAnalyzerTest.cs
new file mode 100644
index 0000000000..8ad50ccffd
--- /dev/null
+++ b/tests/Paramore.Brighter.Analyzer.Tests/Analyzers/WrapAttributeAnalyzerTest.cs
@@ -0,0 +1,46 @@
+using Microsoft.CodeAnalysis.Testing;
+using Paramore.Brighter.Analyzer.Analyzers;
+using Paramore.Brighter.Analyzer.Tests.Analyzers;
+
+namespace Paramore.Brighter.Analyzer.Test.Analyzers
+{
+ public class WrapAttributeAnalyzerTest: BaseAnalyzerTest
+ {
+
+ [Fact]
+ public async Task When_Adding_Attribute_To_MessageMapper()
+ {
+
+ testContext.TestCode = /* lang=c#-test */ """
+ using Paramore.Brighter;
+ using Paramore.Brighter.Transforms.Attributes;
+ namespace TestNamespace
+ {
+ public class WrapWithSample: IAmAMessageMapper
+ {
+ public IRequestContext? Context { get; set ; }
+
+ [{|#0:Compress(0)|}]
+ public SampleEvent MapToRequest(Message message)
+ {
+ return new SampleEvent(message.Id);
+ }
+ [{|#1:Decompress(0)|}]
+ public Message MapToMessage(SampleEvent request, Publication publication)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+
+ public class SampleEvent(Id id) : Event(id)
+ {
+ }
+""";
+
+ testContext.ExpectedDiagnostics.Add(new DiagnosticResult(WrapAttributeAnalyzer.WrapAttributeRule ).WithLocation(0).WithArguments("CompressAttribute"));
+ testContext.ExpectedDiagnostics.Add(new DiagnosticResult(WrapAttributeAnalyzer.UnWrapWithAttributeRule ).WithLocation(1).WithArguments("DecompressAttribute"));
+ await testContext.RunAsync();
+ }
+ }
+}
diff --git a/tests/Paramore.Brighter.Analyzer.Tests/Paramore.Brighter.Analyzer.Tests.csproj b/tests/Paramore.Brighter.Analyzer.Tests/Paramore.Brighter.Analyzer.Tests.csproj
new file mode 100644
index 0000000000..9efa9a02df
--- /dev/null
+++ b/tests/Paramore.Brighter.Analyzer.Tests/Paramore.Brighter.Analyzer.Tests.csproj
@@ -0,0 +1,33 @@
+
+
+
+ $(BrighterTestNineOnlyTargetFrameworks)
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+