Cocoar.Capabilities 0.2.0-alpha.0

This is a prerelease version of Cocoar.Capabilities.
There is a newer version of this package available.
See the version list below for details.
The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package Cocoar.Capabilities --version 0.2.0-alpha.0
                    
NuGet\Install-Package Cocoar.Capabilities -Version 0.2.0-alpha.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Cocoar.Capabilities" Version="0.2.0-alpha.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Cocoar.Capabilities" Version="0.2.0-alpha.0" />
                    
Directory.Packages.props
<PackageReference Include="Cocoar.Capabilities" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Cocoar.Capabilities --version 0.2.0-alpha.0
                    
#r "nuget: Cocoar.Capabilities, 0.2.0-alpha.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Cocoar.Capabilities@0.2.0-alpha.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Cocoar.Capabilities&version=0.2.0-alpha.0&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Cocoar.Capabilities&version=0.2.0-alpha.0&prerelease
                    
Install as a Cake Tool

Cocoar.Capabilities

A general-purpose capabilities system for .NET that enables type-safe, composable capability attachment to any object. Perfect for cross-project extensibility without circular dependencies.

.NET 9.0 Tests Coverage

๐ŸŽฏ What is it?

Cocoar.Capabilities allows you to attach typed capabilities to any object and retrieve them later in a type-safe manner. Think of it as a strongly-typed, high-performance property bag that enables cross-project extensibility patterns.

This library implements Capability Composition: a component/extension-object style where a subject carries a typed, immutable bag of capabilities (policies/behaviors). It's composition-over-inheritance, with exact-type lookups and cross-project contributions.

Architecture Pattern

The design maps to well-established patterns:

  • Subject โ†” Host object (any class)
  • Capability โ†” Component/extension/role (behavior or policy)
  • CapabilityBag โ†” Extension registry on the host
  • AddAs<TContract>() โ†” Register under an extension interface
  • Primary marker โ†” Ensures exactly one "role of kind Primary" is present

Related Patterns: Extension Object, Role Object, Component-Based Design, Strategy/Policy, Composition over Inheritance

Key Benefits

  • ๐Ÿ”’ Type Safe: Compile-time guarantees for capability-subject relationships
  • โšก Zero Allocation: Optimized hot paths with Array.Empty<T>() and type-safe casting
  • ๐Ÿงต Thread Safe: Immutable by design - no locks needed
  • ๐Ÿ”Œ Extensible: Any project can define capabilities for any subject type
  • ๐Ÿ“ฆ Lightweight: Zero dependencies, AOT-friendly

๐Ÿš€ Quick Start

Installation

# When published to NuGet
dotnet add package Cocoar.Capabilities

# For now, reference the project directly
<ProjectReference Include="path/to/Cocoar.Capabilities/Cocoar.Capabilities.csproj" />

Basic Usage

using Cocoar.Capabilities;

// 1. Define capabilities for your domain
public record LogLevelCapability<T>(LogLevel Level) : ICapability<T>;
public record CacheCapability<T>(TimeSpan Ttl) : ICapability<T>;

// 2. Create a capability bag
var userService = new UserService();
var bag = Composer.For(userService)
    .Add(new LogLevelCapability<UserService>(LogLevel.Debug))
    .Add(new CacheCapability<UserService>(TimeSpan.FromMinutes(5)))
    .Build();

// 3. Use capabilities later
if (bag.TryGet<LogLevelCapability<UserService>>(out var logLevel))
{
    logger.SetLevel(logLevel.Level);
}

if (bag.TryGet<CacheCapability<UserService>>(out var cache))
{
    ConfigureCache(cache.Ttl);
}

๐Ÿ“š Core Concepts

1. Capabilities

A capability represents a piece of functionality or configuration that can be attached to a subject:

// Generic capability - works with any subject type T
public record MyCapability<T>(string Value) : ICapability<T>;

// Specific capability - only works with UserService
public record UserCapability(int UserId) : ICapability<UserService>;

2. Subjects

A subject is any object that can have capabilities attached. No special interfaces or base classes required:

// Any class can be a subject
public class UserService { }
public class DatabaseConfig { }
public class PaymentProcessor { }

3. Capability Bags

A capability bag is an immutable container that stores capabilities for a specific subject:

var bag = Composer.For(myObject)
    .Add(new SomeCapability<MyObject>("value"))
    .Add(new AnotherCapability<MyObject>(42))
    .Build(); // Immutable from this point

๐Ÿ”ง API Reference

Building Capability Bags

// Create a builder for any object
var builder = Composer.For(myObject);

// Add capabilities by concrete type
builder.Add(new MyCapability<MyObject>("value"));

// Add capabilities by interface/contract (for exact-type retrieval)
builder.AddAs<IMyCapability<MyObject>>(new ConcreteCapability<MyObject>());

// Build immutable bag (one-shot operation)
var bag = builder.Build();

// โŒ This throws - builder is unusable after Build()
var bag2 = builder.Build(); // InvalidOperationException

Retrieving Capabilities

var bag = /* ... built bag ... */;

// Try to get a capability (safe)
if (bag.TryGet<MyCapability<MyObject>>(out var capability))
{
    Console.WriteLine(capability.Value);
}

// Get required capability (throws if missing)
var required = bag.GetRequired<MyCapability<MyObject>>();

// Get all capabilities of a type
var allCapabilities = bag.GetAll<MyCapability<MyObject>>();

// Check if capability exists
bool exists = bag.Contains<MyCapability<MyObject>>();

// Count capabilities of a type
int count = bag.Count<MyCapability<MyObject>>();

// Total capability count across all types
int total = bag.TotalCapabilityCount;

Convenience Extensions

// Execute action only if capability exists
bag.Use<MyObject, MyCapability<MyObject>>(cap => 
{
    Console.WriteLine($"Found: {cap.Value}");
});

// Transform capability value (returns default if missing)
var result = bag.Transform<MyObject, MyCapability<MyObject>, string>(cap => 
    cap.Value.ToUpper());

๐ŸŽฏ Real-World Examples

Configuration System

// Define configuration-specific capabilities
public record ExposeAsCapability<T>(Type ContractType) : ICapability<T>;
public record SingletonLifetimeCapability<T> : ICapability<T>;
public record HealthCheckCapability<T>(string Name) : ICapability<T>;

// Create configuration with capabilities
var dbConfig = new DatabaseConfig { ConnectionString = "..." };
var configBag = Composer.For(dbConfig)
    .Add(new ExposeAsCapability<DatabaseConfig>(typeof(IDbConfig)))
    .Add(new SingletonLifetimeCapability<DatabaseConfig>())
    .Add(new HealthCheckCapability<DatabaseConfig>("database"))
    .Build();

// Process capabilities in your DI registration code
if (configBag.Contains<SingletonLifetimeCapability<DatabaseConfig>>())
{
    services.AddSingleton(configBag.Subject);
}

foreach (var expose in configBag.GetAll<ExposeAsCapability<DatabaseConfig>>())
{
    services.AddSingleton(expose.ContractType, _ => configBag.Subject);
}

Plugin Architecture

// Plugin can automatically contribute capabilities
public class SecurityPlugin<T> : ICapabilityPlugin<T>
{
    public void ContributeCapabilities(CapabilityBagBuilder<T> builder)
    {
        builder.Add(new AuthenticationCapability<T>());
        builder.Add(new AuthorizationCapability<T>("DefaultPolicy"));
    }
}

// Host discovers and applies plugins
var bag = PluginSystem.CreateWithPlugins(myObject)
    .Add(new CustomCapability<MyObject>()) // Your own capabilities
    .Build();

Web Framework Integration

// Declarative endpoint configuration
var controllerBag = Composer.For(new UsersController())
    .Add(new RouteCapability<UsersController>("/api/users"))
    .Add(new AuthorizeCapability<UsersController>("AdminPolicy"))
    .Add(new RateLimitCapability<UsersController>(100))
    .Build();

// Framework processes capabilities automatically
ProcessWebCapabilities(controllerBag);

๐Ÿ“– See Advanced Examples for complete implementations including:

  • Primary Capability Pattern - Extensible "exactly one" constraint system
  • Cross-Assembly Plugin Architecture - Automatic capability discovery
  • Complete Configuration System - Full DI integration with validation and health checks
  • Web Framework Integration - Declarative routing, auth, caching, and rate limiting
  • Event-Driven Architecture - Handler orchestration with ordering and error handling

โšก Advanced Features

Ordered Capabilities

Capabilities can implement IOrderedCapability for predictable ordering:

public record PriorityCapability<T>(int Priority, string Name) : ICapability<T>, IOrderedCapability
{
    public int Order => Priority; // Lower values run first
}

var bag = Composer.For(myObject)
    .Add(new PriorityCapability<MyObject>(100, "Last"))
    .Add(new PriorityCapability<MyObject>(1, "First"))  
    .Add(new PriorityCapability<MyObject>(50, "Middle"))
    .Build();

var ordered = bag.GetAll<PriorityCapability<MyObject>>();
// Returns: ["First", "Middle", "Last"]

Contract-Based Retrieval

Use AddAs<T>() when you need to retrieve capabilities by interface:

public interface IValidationCapability<T> : ICapability<T>
{
    bool IsValid { get; }
}

public record EmailValidationCapability<T> : IValidationCapability<T>
{
    public bool IsValid => true;
}

var bag = Composer.For(myObject)
    // Register concrete type under interface contract
    .AddAs<IValidationCapability<MyObject>>(new EmailValidationCapability<MyObject>())
    .Build();

// Retrieve by interface
var validator = bag.GetRequired<IValidationCapability<MyObject>>();

Cross-Project Extensibility

Different projects can add capabilities to the same subject without dependencies:

// Core.dll - Defines the subject
namespace MyApp.Core
{
    public class UserService { }
}

// DI.dll - Adds DI capabilities  
namespace MyApp.DI
{
    public record SingletonCapability<T> : ICapability<T>;
}

// Web.dll - Adds web capabilities
namespace MyApp.Web  
{
    public record RouteCapability<T>(string Template) : ICapability<T>;
}

// Composition.dll - Composes everything
var userService = new UserService();
var bag = Composer.For(userService)
    .Add(new SingletonCapability<UserService>())  // From DI.dll
    .Add(new RouteCapability<UserService>("/api/users"))  // From Web.dll
    .Build();

๐Ÿšจ Important Notes

Exact Type Matching

The system uses exact type matching - it only finds capabilities registered under the exact same type:

// โŒ This won't work
builder.Add(new ConcreteCapability<T>());
bag.TryGet<ICapability<T>>(out _); // Returns false!

// โœ… Use AddAs<T> for interface retrieval
builder.AddAs<ICapability<T>>(new ConcreteCapability<T>());
bag.TryGet<ICapability<T>>(out _); // Returns true!

Builder Lifecycle

Builders are single-use - they become unusable after Build():

var builder = Composer.For(myObject);
var bag1 = builder.Build();  // โœ… Works
var bag2 = builder.Build();  // โŒ Throws InvalidOperationException

Thread Safety

  • Capability Bags: Thread-safe (immutable)
  • Builders: NOT thread-safe (single-threaded use only)
var bag = builder.Build(); // Thread-safe from this point

// โœ… Safe to use from multiple threads
Task.Run(() => bag.TryGet<MyCapability<T>>(out _));
Task.Run(() => bag.GetAll<MyCapability<T>>());

๐Ÿ”ง Integration Patterns

For Library Authors

When creating a library that uses Cocoar.Capabilities:

  1. Define your domain capabilities:
namespace MyLibrary.Capabilities
{
    public record MyLibraryCapability<T>(string Setting) : ICapability<T>;
    public record AnotherCapability<T>(int Value) : ICapability<T>;
}
  1. Accept capability bags in your APIs:
public void Configure<T>(ICapabilityBag<T> capabilityBag)
{
    // Process capabilities to configure behavior
    if (capabilityBag.TryGet<MyLibraryCapability<T>>(out var cap))
    {
        // Apply configuration
    }
}
  1. Provide fluent builders (optional):
public static class MyLibraryExtensions
{
    public static CapabilityBagBuilder<T> WithMyFeature<T>(
        this CapabilityBagBuilder<T> builder, string setting)
    {
        return builder.Add(new MyLibraryCapability<T>(setting));
    }
}

// Usage:
var bag = Composer.For(myObject)
    .WithMyFeature("custom-setting")  // Your extension method
    .Build();

For Consumers

When using a library that accepts capability bags:

  1. Create capability bags for your objects:
var myObject = new MyClass();
var bag = Composer.For(myObject)
    .Add(new RequiredCapability<MyClass>("value"))
    .Add(new OptionalCapability<MyClass>(42))
    .Build();
  1. Pass to library APIs:
library.Configure(bag);

๐Ÿงช Testing

The library includes comprehensive tests covering all scenarios:

  • Comprehensive unit tests covering core functionality, edge cases, and performance
  • 100% coverage on core types
  • Thread safety tests for concurrent access
  • Performance tests validating zero-allocation claims

Run tests:

dotnet test

๐Ÿ“ฆ Project Structure

Cocoar.Capabilities/
โ”œโ”€โ”€ ICapability.cs              # Core capability interfaces
โ”œโ”€โ”€ ICapabilityBag.cs           # Capability bag contract
โ”œโ”€โ”€ CapabilityBag.cs            # Implementation with Dictionary<Type, Array>
โ”œโ”€โ”€ CapabilityBagBuilder.cs     # Fluent builder with one-shot lifecycle
โ”œโ”€โ”€ IOrderedCapability.cs       # Ordering support
โ”œโ”€โ”€ Composer.cs                 # Helper factory methods
โ””โ”€โ”€ Extensions/
    โ””โ”€โ”€ CapabilityBagExtensions.cs # Convenience extension methods

๐Ÿค Contributing

Contributions welcome! The library is designed to be:

  • Stable: Core APIs are locked and won't change
  • Extensible: New features can be added without breaking existing code
  • Well-tested: All changes require comprehensive tests

๐Ÿ“„ License

Apache-2.0 License - Use it anywhere, commercial or personal.


๐ŸŽฏ What's Next?

This library provides the foundation for capability-driven architectures. Build your own domain-specific wrapper APIs on top for the best developer experience!

Example integration: Check out how Cocoar.Configuration uses this library to provide type-safe configuration management with capabilities.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 0 10/10/2025

See CHANGELOG.md for detailed release notes.