Releases: cocoar-dev/Cocoar.Capabilities
Release list
v1.2.1
Bug Fix: Get / GetOrThrow Semantics
Get<T>() on ScopeOwnerApi and ScopeAnchorsApi was incorrectly throwing instead of returning null — making it identical to GetOrThrow<T>(). This violated the standard .NET convention where Get returns a default value on failure and a *OrThrow / *Required variant throws.
Before (broken):
scope.Owner.Get<MyOwner>(); // threw InvalidOperationException
scope.Owner.GetOrThrow<MyOwner>(); // threw InvalidOperationException (alias)After (fixed):
scope.Owner.Get<MyOwner>(); // returns null
scope.Owner.GetOrThrow<MyOwner>(); // throws InvalidOperationExceptionAffected APIs:
ScopeOwnerApi.Get<T>()— now returnsT?ScopeAnchorsApi.Get<T>()— now returnsT?ScopeAnchorsApi.Get(string)— now returnsobject?
Internal callers (ComposeFor<T>(), GetRequiredCompositionFor<T>(), GetRequiredComposerFor<T>(), and named anchor equivalents) updated to use GetOrThrow for correct fail-fast semantics.
Documentation
- Added VitePress documentation site with full guide, API reference, and examples
Full Changelog: v1.2.0...v1.2.1
v1.2.0
Release v1.2.0 - Strongly-Typed Scopes
🎉 What's New
Strongly-Typed Scopes
The headline feature of this release! Create scopes with compile-time type safety:
// New: Strongly-typed scope
var configManager = new ConfigurationManager();
using var scope = new CapabilityScope<ConfigurationManager>(configManager);
// No generic parameters needed!
var owner = scope.Owner.Get(); // Returns ConfigurationManager directly
var composer = scope.Owner.Compose();
if (scope.Owner.TryGetComposition(out var composition)) { /* ... */ }Benefits:
- 🎯 Compile-time type safety - catch errors early
- 🚀 Cleaner API - no generic type parameters needed
- 🏗️ Domain-specific scopes - inherit to create custom scope types
- ✅ Full backward compatibility - existing code works unchanged
API Consistency Improvements
Try-Pattern Methods - Safe retrieval without exceptions:
// Owner API
if (scope.Owner.TryGetComposition(out var composition)) { /* use it */ }
if (scope.Owner.TryGetComposer(out var composer)) { /* use it */ }
// Anchors API (both typed and named)
if (scope.Anchors.TryGetCompositionFor<ILogger>(out var comp)) { /* use it */ }
if (scope.Anchors.TryGetComposition("logger", out var comp)) { /* use it */ }Required-Pattern Methods - Throw when not found:
var composition = scope.Owner.GetRequiredComposition(); // Throws if not found
var composer = scope.Anchors.GetRequiredComposerFor<ILogger>();Clearer Naming - *For<T>() suffix for typed operations:
// New preferred methods
scope.Anchors.ComposeFor<ILogger>()
scope.Anchors.GetCompositionFor<ILogger>()
scope.Anchors.GetComposerFor<ILogger>()
// Old methods still work but marked obsolete with migration guidance
scope.Anchors.Compose<ILogger>() // ⚠️ Use ComposeFor<T>() insteadComplete Feature Set
CapabilityScope:
- Constructor with owner:
new CapabilityScope<T>(owner) - Constructor with options:
new CapabilityScope<T>(owner, options) - Inherits from
CapabilityScopefor full compatibility - Strongly-typed
Ownerproperty returnsScopeOwnerApi<TOwner>
ScopeOwnerApi:
Get()- Get owner (no generic needed!)TryGet(out TOwner)- Try-patternCompose()- Create composerGetComposition()/TryGetComposition()/GetRequiredComposition()GetComposer()/TryGetComposer()/GetRequiredComposer()
ScopeOwnerApi Enhancements:
ComposeFor<T>()/GetCompositionFor<T>()/GetComposerFor<T>()TryGetComposition()/TryGetComposer()GetRequiredComposerFor<T>()
ScopeAnchorsApi Enhancements:
ComposeFor<T>()/GetCompositionFor<T>()/GetComposerFor<T>()TryGetCompositionFor<T>()/TryGetComposer()for typed anchorsTryGetComposition(string)/TryGetComposer(string)for named anchorsGetRequiredCompositionFor<T>()/GetRequiredComposerFor<T>()GetRequiredComposition(string)/GetRequiredComposer(string)
🔄 Migration Guide
Strongly-Typed Scopes
// Before
using var scope = new CapabilityScope();
scope.Owner.Set(myObject);
var owner = scope.Owner.Get<MyType>();
// After (optional, but recommended for type safety)
using var scope = new CapabilityScope<MyType>(myObject);
var owner = scope.Owner.Get(); // Type-safe!New Method Names
Update to new *For methods when you see obsolete warnings:
// Old (still works, but obsolete)
scope.Anchors.Compose<ILogger>()
scope.Anchors.GetComposition<ILogger>()
// New (recommended)
scope.Anchors.ComposeFor<ILogger>()
scope.Anchors.GetCompositionFor<ILogger>()All obsolete methods include helpful messages guiding you to the new names.
✅ Backward Compatibility
Zero Breaking Changes! All existing code continues to work:
- ✅
CapabilityScopeworks exactly as before - ✅ All existing methods preserved
- ✅ Obsolete attributes guide migration at your own pace
- ✅ 296 tests passing (including all backward compatibility tests)
📚 Documentation
Full documentation available:
- README - Quick start with strongly-typed scopes
- API Reference - Complete API documentation
- Examples - Detailed examples and patterns
- CHANGELOG - Detailed change list
📦 Installation
dotnet add package Cocoar.Capabilities --version 1.2.0🙏 Feedback
We'd love to hear your feedback on the new strongly-typed scopes! Please open an issue or start a discussion.
Full Changelog: v1.1.0...v1.2.0
v1.1.0
What's New
Owner and Anchors API
Associate scopes with context objects for better composition control:
- Owner API (
scope.Owner.*) - Manage a single distinguished owner with safety guarantees - Anchors API (
scope.Anchors.*) - Manage typed and named anchors - Weak reference storage prevents memory leaks
- Fluent chaining with
.Scopeproperty - Direct composition via
Owner.Compose()andAnchors.Compose<T>()
Using* Extension Methods
New fluent convenience methods for inline capability usage:
UsingFirst<T>()/UsingFirstOrDefault<T>()- Use first capabilityUsingLast<T>()/UsingLastOrDefault<T>()- Use last capabilityUsingEach<T>()- Execute for each capabilityUsingAll<T>()- Execute with full collection- Action-based overloads return
ICompositionfor chaining - Function-based overloads return results directly
v1.0.0 - First public release
🚀 Features
- 🎯 Type-Safe Composition - Attach multiple capabilities to any object with full type safety
- ⚡ High Performance - Zero-allocation lookups and minimal overhead
- 🔒 Immutable Compositions - Thread-safe by design with immutable capability collections
- 🎨 Flexible Registration - Support for multiple contract types per capability
- 📦 Primary Capabilities - Enforce single "primary" capability per subject
- 🔄 Recomposition - Modify existing compositions safely
- 🎭 Custom Ordering - Control capability resolution order with flexible ordering strategies
- 🗂️ Registry Support - Optional registries for managing compositions across your application