RonSijm.Syringe.Fluxor.Lib 1.1.0

Prefix Reserved
dotnet add package RonSijm.Syringe.Fluxor.Lib --version 1.1.0
                    
NuGet\Install-Package RonSijm.Syringe.Fluxor.Lib -Version 1.1.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="RonSijm.Syringe.Fluxor.Lib" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RonSijm.Syringe.Fluxor.Lib" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="RonSijm.Syringe.Fluxor.Lib" />
                    
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 RonSijm.Syringe.Fluxor.Lib --version 1.1.0
                    
#r "nuget: RonSijm.Syringe.Fluxor.Lib, 1.1.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 RonSijm.Syringe.Fluxor.Lib@1.1.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=RonSijm.Syringe.Fluxor.Lib&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=RonSijm.Syringe.Fluxor.Lib&version=1.1.0
                    
Install as a Cake Tool

RonSijm.Syringe

A powerful dependency injection library for .NET that extends Microsoft's built-in DI container with advanced features like implicit wiring, dynamic service registration, optional dependencies, and extensive extensibility points.

Get it? A Syringe is used to inject things. Yes jokes are better when you have to explain them...

codecov

See the architecture diagrams for the package boundaries and runtime registration flow.

Features Overview

  • Implicit Wiring - Automatically register all classes in an assembly with a single line of code
  • Dynamic Service Registration - Add services at runtime after the container is built
  • Optional Dependencies - Optional<T> wrapper for dependencies that may not be available
  • Lazy Resolution - Lazy<T> support for deferred service instantiation
  • Attribute-Based Registration - Control registration behavior with attributes like [DontRegister] and [Singleton]
  • Configuration-Based Registration - Configure service registration via appsettings.json
  • Assembly Bootstrapping - IBootstrapper interface for library initialization
  • Extensibility Points - Hook into service resolution and build events
  • Property Injection - Populate properties marked with [Inject], with optional Required and Key support
  • Method Injection Validation - Verify [FromServices] parameters on Minimal API handlers at build time
  • Keyed Services - Full support for .NET 8+ keyed services
  • Scoped Services - Proper scope management with CreateScope()

Installation

dotnet add package RonSijm.Syringe

Quick Start

The simplest way to use Syringe is with implicit wiring:

var services = new ServiceCollection();
services.WireImplicit<MyClass>(); // Registers all classes in the assembly containing MyClass
var provider = services.BuildServiceProvider();

Default ServiceCollection Extensions:

<a id='snippet-CodeExample-DefaultServiceCollection-WireByType'></a>

var serviceCollection = new ServiceCollection();
serviceCollection.WireImplicit(typeof(ClassA));
var serviceProvider = serviceCollection.BuildServiceProvider();

<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/DefaultServiceCollection/InitiationMethods/TestWireImplicit_Type.cs#L10-L16' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-DefaultServiceCollection-WireByType' title='Start of snippet'>anchor</a></sup>

<a id='snippet-CodeExample-DefaultServiceCollection-WireByTypeGeneric'></a>

var serviceCollection = new ServiceCollection();
serviceCollection.WireImplicit<ClassA>();
var serviceProvider = serviceCollection.BuildServiceProvider();

<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/DefaultServiceCollection/InitiationMethods/TestWireImplicit_GenericType.cs#L10-L16' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-DefaultServiceCollection-WireByTypeGeneric' title='Start of snippet'>anchor</a></sup>

<a id='snippet-CodeExample-DefaultServiceCollection-WireByAssembly'></a>

var serviceCollection = new ServiceCollection();
var assembly = typeof(ClassA).Assembly;
serviceCollection.WireImplicit(assembly, ServiceLifetime.Transient, []);
var serviceProvider = serviceCollection.BuildServiceProvider();

<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/DefaultServiceCollection/InitiationMethods/TestWireImplicit_Assembly.cs#L10-L15' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-DefaultServiceCollection-WireByAssembly' title='Start of snippet'>anchor</a></sup>

Scoped: (default)

<a id='snippet-CodeExample-DefaultScope'></a>

return typeof(ClassWithGuidA).WireImplicit().BuildServiceProvider();

<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/Attributes/LifetimeTests/LifetimeTests_Scoped.cs#L10-L12' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-DefaultScope' title='Start of snippet'>anchor</a></sup>

Transient:

<a id='snippet-CodeExample-TransientScope'></a>

return typeof(ClassWithGuidA).WireImplicit().WithLifetime(ServiceLifetime.Transient).BuildServiceProvider();

<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/Attributes/LifetimeTests/LifetimeTests_Transient.cs#L10-L12' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-TransientScope' title='Start of snippet'>anchor</a></sup>

Singleton:

<a id='snippet-CodeExample-SingletonScope'></a>

return typeof(ClassWithGuidA).WireImplicit().WithLifetime(ServiceLifetime.Singleton).BuildServiceProvider();

<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/Attributes/LifetimeTests/LifetimeTests_Singleton.cs#L10-L12' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-SingletonScope' title='Start of snippet'>anchor</a></sup>

<a id='snippet-CodeExample-Parameters-registerAsTypesOnly'></a>

var serviceCollection = new ServiceCollection();
serviceCollection.WireImplicit<ClassA>(registerAsTypesOnly: [typeof(ClassWithInterfaceA)]);
var serviceProvider = serviceCollection.BuildServiceProvider();

<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/DefaultServiceCollection/Parameters/TestWireImplicit_RegisterAsTypesOnly_ClassWithInterface.cs#L11-L15' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-Parameters-registerAsTypesOnly' title='Start of snippet'>anchor</a></sup>

<a id='snippet-CodeExample-Parameters-registerBothTypeAndInterfaces'></a>

var serviceCollection = new ServiceCollection();
serviceCollection.WireImplicit<ClassA>(registerBothTypeAndInterfaces: [typeof(ClassWithInterfaceA)]);
var serviceProvider = serviceCollection.BuildServiceProvider();

<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/DefaultServiceCollection/Parameters/TestWireImplicit_RegisterBothTypeAndInterfaces_ClassWithInterface.cs#L10-L14' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-Parameters-registerBothTypeAndInterfaces' title='Start of snippet'>anchor</a></sup>

Extension Methods

By doing a WireImplicit - The extension will return a SyringeServiceCollectionAndRegistration object. This is an object that keeps track of the last registion that you've made, allowing you to append extra configuration to that specific registration:

<a id='snippet-CodeExample-DontRegisterTypesExtension'></a>

var serviceCollection = new SyringeServiceCollection();
serviceCollection.WireImplicit<ClassA>().DontRegisterTypes(typeof(ClassA));

<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/CustomCollection/Parameters/TestWireImplicit_DontRegisterAsTypes_Class.cs#L11-L14' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-DontRegisterTypesExtension' title='Start of snippet'>anchor</a></sup>

<a id='snippet-CodeExample-DontRegisterTypesWithInterfaces'></a>

var serviceCollection = new SyringeServiceCollection();
serviceCollection.WireImplicit<ClassA>().DontRegisterTypesWithInterfaces(typeof(InterfaceFor_ClassWithInterfaceA));

<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/CustomCollection/Parameters/TestWireImplicit_DontRegisterTypesWithInterface_InterfaceFor_ClassWithInterface.cs#L11-L14' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-DontRegisterTypesWithInterfaces' title='Start of snippet'>anchor</a></sup>

<a id='snippet-CodeExample-RegisterAsTypesOnly'></a>

var serviceCollection = new SyringeServiceCollection();
serviceCollection.WireImplicit<ClassA>().RegisterAsTypesOnly(typeof(ClassWithInterfaceA));

<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/CustomCollection/Parameters/TestWireImplicit_RegisterAsTypesOnly_ClassWithInterface.cs#L11-L14' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-RegisterAsTypesOnly' title='Start of snippet'>anchor</a></sup>

<a id='snippet-CodeExample-RegisterBothTypeAndInterfaces'></a>

var serviceCollection = new SyringeServiceCollection();
serviceCollection.WireImplicit<ClassA>().RegisterBothTypeAndInterfaces(typeof(ClassWithInterfaceA));

<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/CustomCollection/Parameters/TestWireImplicit_RegisterBothTypeAndInterfaces_ClassWithInterface.cs#L10-L13' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-RegisterBothTypeAndInterfaces' title='Start of snippet'>anchor</a></sup>

Registration Extensions

A SyringeServiceCollection contains a internal List<IWireSyringeServiceDescriptorExtension> Extensions { get; set; } = []; - which lets you add your own BeforeBuildServiceProvider Extensions

Example

You can put these kind of configurations in your appsettings.json:

Don't register classes with this interface:

Usecase:

Imagine you have an IProcessor (Interface), DatabaseProcessor and APIProcessor that implement this interface - In your appsettings.json you can register or unregister a specific implementation for this interface.

<a id='snippet-CodeExample-ConfigurationNotWithInterfaces'></a>

var appsetting = """
                 {
                     "DependencyInjection": {
                         "Assembly:RonSijm.Syringe.ExamplesC": {
                         "Type:ExampleCInterface": "None"
                         }
                     }
                 }
                 """.ToConfiguration();

<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/Config/ExamplesC_InterfaceRegistrationNone.cs#L11-L23' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-ConfigurationNotWithInterfaces' title='Start of snippet'>anchor</a></sup>

Don't register these specific classes:

Usecase:

Imagine you have an IProcessor (Interface), DatabaseProcessor and APIProcessor and UserProcessor - In your appsettings.json you can register or unregister a specific implementations for this interface.

When your consumer takes an IEnumerable<IProcessor> - you can register or unregister specific processors, and only run selected processors.

<a id='snippet-CodeExample-ConfigurationNotSpecificClass'></a>

var appsetting = """
                 {
                     "DependencyInjection": {
                         "Assembly:RonSijm.Syringe.ExamplesC": {
                         "Type:ExampleC2": "None",
                         "Type:ExampleC3": "None",
                         }
                     }
                 }
                 """.ToConfiguration();

<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/Config/ExamplesC_TypeRegistrationNone.cs#L11-L24' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-ConfigurationNotSpecificClass' title='Start of snippet'>anchor</a></sup>


SyringeServiceProvider

The SyringeServiceProvider is an enhanced service provider that wraps Microsoft's DI container and adds powerful features like dynamic service registration at runtime.

Basic Usage

// Create with options
var provider = new SyringeServiceProvider(options =>
{
    options.BuildOnConstruct = true; // Build immediately (default)
    options.Services.WireImplicit<MyClass>();
});

// Or create from an existing service collection
var services = new ServiceCollection();
services.AddSingleton<IMyService, MyService>();
var provider = new SyringeServiceProvider(services);

Dynamic Service Registration

One of the most powerful features is the ability to add services after the container is built:

var provider = new SyringeServiceProvider();

// Later, dynamically load more services
var newServices = new ServiceCollection();
newServices.AddSingleton<INewService, NewService>();
await provider.LoadServiceDescriptors(newServices);
provider.Build(); // Rebuild to include new services

This is particularly useful for plugin architectures or lazy-loading scenarios.


Optional Dependencies

The Optional<T> wrapper allows you to inject dependencies that may not be registered. This is especially useful in Blazor where injecting null throws an exception.

public class MyComponent
{
    private readonly Optional<IOptionalService> _optionalService;

    public MyComponent(Optional<IOptionalService> optionalService)
    {
        _optionalService = optionalService;
    }

    public void DoWork()
    {
        if (_optionalService.HasValue)
        {
            _optionalService.Value.DoSomething();
        }
    }
}

Optional<T> is automatically registered when using SyringeServiceProvider.


Lazy Resolution

Syringe supports Lazy<T> for deferred service instantiation:

public class MyService
{
    private readonly Lazy<IExpensiveService> _expensiveService;

    public MyService(Lazy<IExpensiveService> expensiveService)
    {
        _expensiveService = expensiveService;
    }

    public void DoWork()
    {
        // Service is only created when first accessed
        _expensiveService.Value.Process();
    }
}

Attribute-Based Registration

Control how classes are registered using attributes:

Don't Register Attribute

Prevent a class from being automatically registered:

[Registration.DontRegister]
public class InternalHelper
{
    // This class won't be registered by WireImplicit
}

Lifetime Attributes

Specify the service lifetime:

[Lifetime.Singleton]
public class MySingletonService : IMyService
{
    // Will be registered as singleton
}

IBootstrapper Interface

The IBootstrapper interface allows libraries to define their own service registrations that are automatically discovered and executed:

public class MyLibraryBootstrapper : IBootstrapper
{
    public Task<IEnumerable<ServiceDescriptor>> Bootstrap()
    {
        var services = new ServiceCollection();
        services.AddSingleton<IMyLibraryService, MyLibraryService>();
        services.AddScoped<IRepository, Repository>();

        return Task.FromResult<IEnumerable<ServiceDescriptor>>(services);
    }
}

When an assembly is loaded, Syringe automatically discovers and executes all IBootstrapper implementations.


ILoadAfterExtension

Hook into the assembly loading process with ILoadAfterExtension:

public class MyLoadExtension : BaseLoadAfterExtension
{
    public override void AssembliesLoaded(List<Assembly> loadedAssemblies)
    {
        // Called after assemblies are loaded
        Console.WriteLine($"Loaded {loadedAssemblies.Count} assemblies");
    }

    public override void DescriptorsLoaded(List<ServiceDescriptor> loadedDescriptors)
    {
        // Called after service descriptors are registered
        Console.WriteLine($"Registered {loadedDescriptors.Count} services");
    }
}

Extensibility Points

ISyringeAfterBuildExtension

Execute code after the service provider is built:

public class MyAfterBuildExtension : ISyringeAfterBuildExtension
{
    private SyringeServiceProvider _provider;

    public void SetReference(SyringeServiceProvider serviceProvider)
    {
        _provider = serviceProvider;
    }

    public void Process(List<ServiceDescriptor> loadedDescriptors, bool isInitialBuild)
    {
        // Called after Build() is called
    }
}

// Register the extension
var provider = new SyringeServiceProvider(options =>
{
    options.WithAfterBuildExtension<MyAfterBuildExtension>();
});

ISyringeServiceProviderAfterServiceExtension

Decorate or modify services after they are resolved:

public class MyAfterServiceExtension : ISyringeServiceProviderAfterServiceExtension
{
    public void SetReference(SyringeServiceProvider serviceProvider) { }

    public void Decorate(Type serviceType, object service)
    {
        // Called after every service resolution
    }
}

// Register the extension
var provider = new SyringeServiceProvider(options =>
{
    options.WithAfterGetService<MyAfterServiceExtension>();
});

Property Injection

The PropertyInjectionAfterServiceExtension populates public writable properties marked with an InjectAttribute after a service is resolved. The attribute is matched by name, so you can define your own:

public class InjectAttribute : Attribute
{
    public bool Required { get; set; }
    public object Key { get; set; }
}

Then mark the properties you want injected:

public class MyService
{
    [Inject]
    public IOptionalDependency Optional { get; set; }

    [Inject(Required = true)]
    public IMandatoryDependency Mandatory { get; set; }

    [Inject(Required = true, Key = "primary")]
    public IKeyedDependency Primary { get; set; }
}

Enable property injection by adding the extension:

var provider = new SyringeServiceProvider(options =>
{
    options.WithAfterGetService<PropertyInjectionAfterServiceExtension>();
});
  • Required = true throws an InvalidOperationException when the property cannot be resolved.
  • Key is typed as object, so strings, enums, ints, or any other type accepted by Microsoft's keyed-service APIs all work.
  • Both Required and Key are discovered reflectively, so they remain optional on your own InjectAttribute.

Validate Property Injection on Build

PropertyInjectionAfterServiceExtension also plugs into ValidateOnBuild. Any registered service with [Inject(Required = true)] properties (including keyed ones) is verified during the build, without instantiating the services:

var provider = new SyringeServiceProvider(options =>
{
    options.WithAfterGetService<PropertyInjectionAfterServiceExtension>();
    options.ValidateOnBuild = true;
});

If any required property is missing, the build fails with the standard AggregateException("Some services are not able to be constructed", …) containing one InvalidOperationException per missing dependency.


Method Injection Validation

ASP.NET Core Minimal APIs let you inject services straight into static endpoint handlers via [FromServices]:

public static class FootballEndpoints
{
    public static async Task<IResult> GetFootballAsync(
        int footballId,
        [FromServices] IGetFootballService service,
        CancellationToken cancellationToken)
    {
        // ...
    }
}

These handlers are not registered as services, so a missing dependency only surfaces at request time. MethodInjectionValidationExtension lets you validate them during ValidateOnBuild:

var extension = new MethodInjectionValidationExtension()
    .AddMethod(FootballEndpoints.GetFootballAsync);

var provider = new SyringeServiceProvider(options =>
{
    options.WithAfterGetService(extension);
    options.ValidateOnBuild = true;
});

For every registered method, each parameter marked with [FromServices] or [FromKeyedServices(key)] is checked against the container. Missing parameters are aggregated into a single InvalidOperationException that is bundled into the standard AggregateException thrown by ValidateOnBuild.

Like InjectAttribute, both attributes are matched by name, so Microsoft's FromKeyedServicesAttribute works out of the box and you can supply your own FromServicesAttribute without referencing ASP.NET Core.

Works with the default Microsoft DI provider

The same validation is also exposed as a ValidateMethodInjection extension method that works on any IServiceProvider — including the stock ServiceProvider returned by IServiceCollection.BuildServiceProvider() — without needing SyringeServiceProvider at all:

var services = new ServiceCollection();
services.AddSingleton<IGetFootballService, GetFootballService>();

var provider = services.BuildServiceProvider();
provider.ValidateMethodInjection(FootballEndpoints.GetFootballAsync);

Internally it queries Microsoft's public IServiceProviderIsService / IServiceProviderIsKeyedService introspection contracts (no reflection into framework internals), and throws the same AggregateException("Some services are not able to be constructed", …) shape that ValidateOnBuild produces.

Validating every registered Minimal API endpoint

You usually don't want to list handlers by hand. Every endpoint registered through MapGet / MapPost / etc. stores its handler's MethodInfo on the endpoint metadata, so you can hand the whole set to ValidateMethodInjection in one go:

var app = builder.Build();

app.MapGet("/Footballs/{FootballId:int}", FootballEndpoints.GetFootballAsync);
// ...other MapXxx calls...

var handlerMethods = app.DataSources
    .SelectMany(ds => ds.Endpoints)
    .Select(e => e.Metadata.GetMetadata<MethodInfo>())
    .Where(m => m != null)
    .ToArray();

app.Services.ValidateMethodInjection(handlerMethods);

app.Run();

This catches missing [FromServices] dependencies on application start instead of at the first request that hits the endpoint.


Keyed Services

Syringe fully supports .NET 8+ keyed services:

var provider = new SyringeServiceProvider(services =>
{
    services.AddKeyedSingleton<IMyService, ServiceA>("A");
    services.AddKeyedSingleton<IMyService, ServiceB>("B");
});

var serviceA = provider.GetKeyedService<IMyService>("A");
var serviceB = provider.GetKeyedService<IMyService>("B");

Scoped Services

Create and manage scopes properly:

var provider = new SyringeServiceProvider();

using (var scope = provider.CreateScope())
{
    var scopedService = scope.ServiceProvider.GetService<IMyScopedService>();
    // Use scoped service
}
// Scope is disposed, scoped services are cleaned up

Global Settings

Configure default behavior globally:

// Set default service lifetime (default is Scoped)
SyringeGlobalSettings.DefaultServiceLifetime = ServiceLifetime.Transient;

// Control whether types with interfaces are also registered as themselves
SyringeGlobalSettings.RegisterAsTypeWhenTypeHasInterfaces = true;

Fluxor Integration

Syringe includes optional Fluxor integration for state management:

var provider = new SyringeServiceProvider(options =>
{
    options.UseFluxor(fluxorOptions =>
    {
        fluxorOptions.ScanAssemblies(typeof(Program).Assembly);
    });
});

Install the Fluxor integration package:

dotnet add package RonSijm.Syringe.Fluxor

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  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 is compatible.  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.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on RonSijm.Syringe.Fluxor.Lib:

Package Downloads
RonSijm.Syringe.Fluxor

A Fluxor library for to make it easier to do dependency injection.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 86 10/5/2026
1.0.0 207 2/11/2026
1.0.0-RC1.7 84 2/1/2026
1.0.0-RC1.6 76 1/30/2026
1.0.0-Alpha1.5 303 4/9/2025
1.0.0-Alpha1.4 310 4/7/2025
1.0.0-Alpha1.3 192 4/7/2025
1.0.0-Alpha1.2 197 4/7/2025
1.0.0-Alpha1.1 193 4/6/2025
1.0.0-Alpha1 278 3/31/2025