Davasorus.Utility.DotNet.Config 2026.2.2.5

There is a newer version of this package available.
See the version list below for details.
dotnet add package Davasorus.Utility.DotNet.Config --version 2026.2.2.5
                    
NuGet\Install-Package Davasorus.Utility.DotNet.Config -Version 2026.2.2.5
                    
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="Davasorus.Utility.DotNet.Config" Version="2026.2.2.5" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Davasorus.Utility.DotNet.Config" Version="2026.2.2.5" />
                    
Directory.Packages.props
<PackageReference Include="Davasorus.Utility.DotNet.Config" />
                    
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 Davasorus.Utility.DotNet.Config --version 2026.2.2.5
                    
#r "nuget: Davasorus.Utility.DotNet.Config, 2026.2.2.5"
                    
#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 Davasorus.Utility.DotNet.Config@2026.2.2.5
                    
#: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=Davasorus.Utility.DotNet.Config&version=2026.2.2.5
                    
Install as a Cake Addin
#tool nuget:?package=Davasorus.Utility.DotNet.Config&version=2026.2.2.5
                    
Install as a Cake Tool

Davasorus.Utility.DotNet.Config

Davasorus.Utility.DotNet.Config provides utilities for loading, creating, converting, and updating configuration files (JSON, XML, and YAML) in .NET 8+ applications. It is designed for use with dependency injection (DI) and follows a strict service/client separation: end users should only interact with the Service interfaces; the Service classes handle all client interactions, error handling, and logging.

Features

  • Load, create, convert, and update configuration files in JSON, XML, and YAML formats
  • Full CRUD operations for each format (Load, Create, Convert, Update)
  • Robust error handling and logging (SQS integration)
  • Designed for DI: Service = Scoped, Client = Transient

Dependency Injection Setup

Use the fluent configuration API to register services easily:

using Davasorus.Utility.DotNet.Config.Configuration;

// Register only JSON services (Load, Create, Convert, Update)
services.AddConfigServices(config => config.AddJsonServices());

// Register only XML services (Load, Create, Convert, Update)
services.AddConfigServices(config => config.AddXmlServices());

// Register only YAML services (Load, Create, Convert, Update)
services.AddConfigServices(config => config.AddYamlServices());

// Register all services (JSON + XML + YAML)
services.AddConfigServices(config => config.AddAllServices());

// Or chain them explicitly
services.AddConfigServices(config => config
    .AddJsonServices()
    .AddXmlServices()
    .AddYamlServices());

Alternative: Individual Service Registration

You can also register services individually if you need fine-grained control:

// JSON services
services.AddJsonConfigServices();

// XML services
services.AddXmlConfigServices();

// YAML services
services.AddYamlConfigServices();

// All services
services.AddAllConfigServices();

Manual Registration (Legacy)

If you prefer manual registration:

// JSON
services.AddScoped<ILoadJSONService, LoadJSONService>();
services.AddTransient<ILoadJSONClient, LoadJSONClient>();
services.AddScoped<ICreateJSONService, CreateJSONService>();
services.AddTransient<ICreateJSONClient, CreateJSONClient>();
services.AddScoped<IConvertToJSONService, ConvertToJSONService>();
services.AddTransient<IConvertToJSONClient, ConvertToJSONClient>();
services.AddScoped<IUpdateJSONService, UpdateJSONService>();
services.AddTransient<IUpdateJSONClient, UpdateJSONClient>();

// XML
services.AddScoped<ILoadXmlService, LoadXmlService>();
services.AddTransient<ILoadXMLClient, LoadXMLClient>();
services.AddScoped<ICreateXMLService, CreateXMLService>();
services.AddTransient<ICreateXMLClient, CreateXMLClient>();
services.AddScoped<IConvertToXMLService, ConvertToXMLService>();
services.AddTransient<IConvertToXMLClient, ConvertToXMLClient>();
services.AddScoped<IUpdateXMLService, UpdateXMLService>();
services.AddTransient<IUpdateXMLClient, UpdateXMLClient>();

// YAML
services.AddScoped<ILoadYAMLService, LoadYAMLService>();
services.AddTransient<ILoadYAMLClient, LoadYAMLClient>();
services.AddScoped<ICreateYAMLService, CreateYAMLService>();
services.AddTransient<ICreateYAMLClient, CreateYAMLClient>();
services.AddScoped<IConvertToYAMLService, ConvertToYAMLService>();
services.AddTransient<IConvertToYAMLClient, ConvertToYAMLClient>();
services.AddScoped<IUpdateYAMLService, UpdateYAMLService>();
services.AddTransient<IUpdateYAMLClient, UpdateYAMLClient>();

Note: Only use the Service interfaces (ILoadJSONService, ICreateJSONService, ILoadXmlService, ILoadYAMLService, etc.) in your application code. The Service classes manage all communication with their respective Client classes internally.

Example Usage

Load JSON File

public class MyConfigLoader
{
    private readonly ILoadJSONService _loadJsonService;
    public MyConfigLoader(ILoadJSONService loadJsonService)
    {
        _loadJsonService = loadJsonService;
    }

    public async Task<MyConfigModel?> LoadConfigAsync(string path, string fileName)
    {
        return await _loadJsonService.LoadDataFromJsonFile<MyConfigModel>(path, fileName);
    }
}

Create JSON File

public class MyConfigCreator
{
    private readonly ICreateJSONService _createJsonService;
    public MyConfigCreator(ICreateJSONService createJsonService)
    {
        _createJsonService = createJsonService;
    }

    public async Task CreateConfigAsync(string path, string fileName, MyConfigModel config)
    {
        await _createJsonService.CreateJsonFileAsync(path, fileName, config);
    }
}

Convert File or Text to JSON

public class MyConverter
{
    private readonly IConvertToJSONService _convertToJsonService;
    public MyConverter(IConvertToJSONService convertToJsonService)
    {
        _convertToJsonService = convertToJsonService;
    }

    public async Task<string?> ConvertFileAsync(string filePath)
    {
        return await _convertToJsonService.ConvertFileToJsonAsync(filePath);
    }

    public async Task<string?> ConvertTextAsync(string text)
    {
        return await _convertToJsonService.ConvertTextToJsonInMemoryAsync(text);
    }
}

Update JSON File (Merge, Overwrite, or Add)

public class MyConfigUpdater
{
    private readonly IUpdateJSONService _updateJsonService;
    public MyConfigUpdater(IUpdateJSONService updateJsonService)
    {
        _updateJsonService = updateJsonService;
    }

    // Merge new data into existing JSON file
    public async Task UpdateConfigAsync(string path, string fileName, MyConfigModel newData)
    {
        await _updateJsonService.UpdateJsonFileAsync(path, fileName, newData);
    }

    // Overwrite JSON file with new data
    public async Task OverwriteConfigAsync(string path, string fileName, MyConfigModel newData)
    {
        await _updateJsonService.OverwriteJsonFileAsync(path, fileName, newData);
    }

    // Add new data to JSON file (adds new keys only)
    public async Task AddToConfigAsync(string path, string fileName, MyConfigModel newData)
    {
        await _updateJsonService.AddToJSONFileAsync(path, fileName, newData);
    }
}

Load XML File

public class MyXmlLoader
{
    private readonly ILoadXmlService _loadXmlService;
    public MyXmlLoader(ILoadXmlService loadXmlService)
    {
        _loadXmlService = loadXmlService;
    }

    // Load and deserialize an XML file
    public async Task<MyConfigModel?> LoadConfigAsync(string path, string fileName)
    {
        return await _loadXmlService.LoadDataFromXmlFile<MyConfigModel>(path, fileName);
    }

    // Load descendants matching an attribute and value
    public async Task<List<XElement>?> LoadDescendantsAsync(string path, string parent, string searchAttribute, string searchValue)
    {
        return await _loadXmlService.LoadDescendants(path, parent, searchAttribute, searchValue);
    }

    // Load attributes matching an attribute name
    public async Task<List<XAttribute>?> LoadElementsAsync(string path, string parent, string searchAttribute)
    {
        return await _loadXmlService.LoadElements(path, parent, searchAttribute);
    }
}

Create, Convert, and Update XML Files

// Create XML file
await _createXmlService.CreateXmlFileAsync(path, fileName, config);

// Convert file to XML
string? xmlPath = await _convertToXmlService.ConvertFileToXmlAsync(filePath);

// Convert text to XML in memory
string? xml = await _convertToXmlService.ConvertTextToXmlInMemoryAsync(text);

// Update (merge), overwrite, or add to XML file
await _updateXmlService.UpdateXmlFileAsync(path, fileName, data);
await _updateXmlService.OverwriteXmlFileAsync(path, fileName, data);
await _updateXmlService.AddToXmlFileAsync(path, fileName, data);

Load YAML File

public class MyYamlLoader
{
    private readonly ILoadYAMLService _loadYamlService;
    public MyYamlLoader(ILoadYAMLService loadYamlService)
    {
        _loadYamlService = loadYamlService;
    }

    public async Task<MyConfigModel?> LoadConfigAsync(string path, string fileName)
    {
        return await _loadYamlService.LoadDataFromYamlFile<MyConfigModel>(path, fileName);
    }
}

Create, Convert, and Update YAML Files

// Create YAML file
await _createYamlService.CreateYamlFileAsync(path, fileName, config);

// Convert file to YAML
string? yamlPath = await _convertToYamlService.ConvertFileToYamlAsync(filePath);

// Convert text to YAML in memory
string? yaml = await _convertToYamlService.ConvertTextToYamlInMemoryAsync(text);

// Update (merge), overwrite, or add to YAML file
await _updateYamlService.UpdateYamlFileAsync(path, fileName, data);
await _updateYamlService.OverwriteYamlFileAsync(path, fileName, data);
await _updateYamlService.AddToYAMLFileAsync(path, fileName, data);

Error Handling

  • All errors are logged using ILogger and SQS.
  • Service methods handle exceptions and provide diagnostics.

Requirements

  • .NET 8.0 or later

License

MIT License

IConfiguration Provider Integration

In addition to the typed Service-style API (ILoadJsonService<T> etc.), the package ships YAML and XML IConfigurationProvider implementations for Microsoft.Extensions.Configuration. Register them on an IConfigurationBuilder and bind to IOptions<T> / IOptionsMonitor<T> per the standard framework pattern.

YAML and XML providers

using Davasorus.Utility.DotNet.Config.Configuration.Providers;
using Microsoft.Extensions.Configuration;

var config = new ConfigurationBuilder()
    .AddYamlFile("appsettings.yaml", optional: false, reloadOnChange: true)
    .AddXmlFile("legacy.xml", optional: true)
    .Build();

Both extensions accept absolute or relative paths. reloadOnChange: true is handled by Microsoft's FileConfigurationProvider base class — when the file changes, the provider re-invokes Load(Stream), fires IConfiguration.GetReloadToken(), and IOptionsMonitor<T> listeners receive the new values.

Flattening conventions

Both providers project parsed content through JsonNode (via the package's existing YamlJsonBridge / XmlJsonBridge) and then flatten the tree per Microsoft.Extensions.Configuration.Json conventions:

  • Nested object keys use a colon separator: Db:Host, Db:Port.
  • Array elements use numeric indices: Servers:0, Servers:1.
  • String leaves are unwrapped (no JSON quotes around the value).
  • null leaves are preserved as keys with null values.
  • Empty objects and arrays contribute no keys.

One deliberate divergence from Microsoft conventions: boolean scalars are emitted as "True" / "False" (matching bool.ToString()) rather than the JSON literal forms "true" / "false". IConfiguration.GetValue<bool>(...) accepts both casings on the read path, so this only affects raw inspection of the flattened map (e.g., logging or CLI dumps), where capitalized values are friendlier for human eyes.

XML root-element wrapping

The XML provider uses the same conventions as the package's XmlJsonBridge. The XML root element's local name becomes the top-level key, so <Root><Host>x</Host></Root> flattens to Root:Host = "x" (not just Host = "x"). This matches the rest of the package's XML handling and gives JSON-compatible binding behavior.

File-watch for typed Service consumers

Consumers using the typed ILoadYamlService<T> / ILoadXmlService<T> / ILoadJsonService<T> API (rather than IConfiguration) can subscribe to file-change events via IConfigFileWatcher:

using Davasorus.Utility.DotNet.Config;

using var watcher = ConfigFileWatcherFactory.Create("config.yaml");
watcher.Changed += async (_, e) =>
{
    var fresh = await loadYamlService.LoadDataFromYamlFileAsync<MyConfig>(
        e.FilePath,
        CancellationToken.None);
    // hand-off to caller's reload logic
};

The watcher is consumer-owned: dispose it (e.g., via using) when no longer needed. Failing to dispose leaks the underlying FileSystemWatcher native handle. The watcher is constructed via the static ConfigFileWatcherFactory.Create(filePath) rather than DI registration so the lifetime contract is explicit — DI containers don't always dispose Singletons cleanly on shutdown, and the right lifetime for a watcher is almost always tied to a specific consumer-side component rather than application-global.

No JSON IConfiguration provider

Microsoft.Extensions.Configuration.Json is canonical for JSON. This package does not ship an AddJsonFile extension; use Microsoft's. The package's value-add for JSON is on the write path — merge semantics, atomic writes, schema-friendly serialization — exposed via ILoadJsonService<T> / IUpdateJsonService<T> / ICreateJsonService<T>, all of which are unaffected.

Upgrade Notes — Group B (2026.2.x → 2026.3.x)

Group B refactors the package's architecture without changing the package's high-level capabilities. Three behaviour changes affect consumers:

1. Reporting goes through IConfigErrorReporter; SQS is auto-detected

ISqsService is no longer a constructor parameter on any Service or Client. All cross-cutting error reporting goes through the new IConfigErrorReporter abstraction. Consumers who want to swap in a different reporting sink (Application Insights, file logger, no-op for tests, etc.) register their own implementation as IConfigErrorReporter in DI; the package picks it up.

The default registration is automatic and depends on what's already in your DI container:

  • If an ISqsService is registered before AddJsonConfigServices / AddXmlConfigServices / AddYamlConfigServices / AddAllConfigServices / AddConfigServices(...) runs, the package registers SqsConfigErrorReporter as the default — preserves the pre-Group-B Trello-via-SQS behaviour.
  • Otherwise, the package registers NullConfigErrorReporter (no-op) as the default.

In both cases the registration uses TryAddSingleton, so a host-registered reporter still wins.

Hosts that already had ISqsService registered before Group B keep getting SQS reporting without changing any DI wiring code. Hosts that want SQS reporting but are NOT going through one of the Add*ConfigServices extensions can call services.AddConfigSqsErrorReporter() directly.

2. Every async method gains CancellationToken cancellationToken = default

Source-compatible at call-sites that omit the parameter; binary-incompatible (recompile against the new assembly).

3. Client interfaces split path / Stream methods; path takes (string fullPath)

The most visible breaking change. Each Client's path-based method (Load, Create, Update, Convert × JSON / XML / YAML) now takes a single string fullPath argument instead of (string filePath, string fileName). The two-argument shape stays on Service interfaces. With Group A's PathSafety.ResolveSafePath validating in the Service layer, the Client receives a pre-validated, pre-combined full path.

Code that calls Service interfaces (the recommended pattern) is unaffected. Code that calls Client interfaces directly must update:

// Before
client.LoadDataFromJsonFile(filePath, fileName);

// After
client.LoadDataFromJsonFileAsync(Path.Combine(filePath, fileName), cancellationToken);

Each Client also gains parallel Stream overloads on Load / Create / Convert (Update remains path-only). Net-additive surface — no source-compat impact on the Stream side.

4. YAML processing unified on YamlJsonBridge

Group A introduced YamlJsonBridge and routed YAML Convert and YAML Update through it, but YAML Load and YAML Create still used YamlDotNet.Serialization.IDeserializer / ISerializer directly with the camelCase naming convention. Group B finishes the unification — all four YAML operations now go through YamlJsonBridge + JsonSerializer with JsonSafety.DefaultReadOptions / DefaultWriteOptions.

Two consumer-visible effects:

  1. YAML Load matches keys case-insensitively instead of strictly camelCase-to-PascalCase. Case-insensitive is a superset, so existing YAML keeps deserializing — and YAML keys whose casing was rejected before (e.g. Name: … → C# Name property) now succeed.
  2. YAML Create writes PascalCase keys instead of camelCase. The byte contents of newly-written YAML files differ — Name: Test instead of name: Test. Round-tripping through YAML Load works because of the case-insensitive deserialize, but external tooling that reads the YAML directly (linters, yq, other deserializers) sees the new casing. This change brings YAML Create in line with what YAML Convert and YAML Update already produce after Group A.

The behaviour is now consistent across the four YAML operations and matches how the JSON stack handles property names.

Upgrade Notes — Group C (2026.x → 2026.x)

Group C lands two correctness/observability changes and a logging refactor. None of them change public types, DI surface, or method signatures.

1. All path-based writes are now atomic

Create / Update / Add / Overwrite / Convert path facades for JSON / XML / YAML route through a temp + fsync + rename helper. The window where a crash could leave a config file partially written is gone — observers always see either the old file or the new file, never partial.

External processes (antivirus, sync clients, file watchers) may briefly see *.{guid}.tmp files in the same directory as the target while a write is in progress. The Guid suffix prevents collisions across concurrent writers; the temp file is deleted on success or on cleanup if the rename fails.

The synchronous fsync adds ~1ms to each write — invisible for typical config writes (rare, small).

2. Writes are skipped when content is byte-identical to existing

If an Update / Create / Overwrite / Convert produces bytes that match what's already on disk, the write is skipped entirely. LastWriteTime does not change; downstream filesystem watchers do not fire.

This is the new default. Consumers who need to force a LastWriteTime bump (e.g. as a heartbeat for a process-of-record that polls mtime) should call File.SetLastWriteTimeUtc(path, DateTime.UtcNow) directly — that's the right tool for the job.

When a skip happens, the package emits an EventId 1201 (Config write skipped: content unchanged for '...') at Debug level. Operators investigating "I edited the config but nothing reloaded" can filter on that EventId.

3. Per-file ACLs may not survive writes

The atomic temp + rename pattern replaces the file (and its ACL) on every real write. Inherited-from-directory ACLs work as before; explicit per-file ACLs are cleared. If a real consumer needs ACL preservation, file an issue — the helper has a clean place to add a preserveAcl knob.

4. EventId scheme for log entries is now stable

Range Domain
1000-1099 Group A — correctness events
1100-1199 Group B — operation events
1200-1299 Group C — performance / write events
1300+ Group D — feature events (reserved)

Documented in the ConfigLog class XML comment. Operators get filterable events; Group D's analyzer pack (CA1848) will lock in the convention going forward.

Upgrade Notes — Group F (2026.x → 2026.x)

Group F renames the package's C# namespace from Tyler.Utility.DotNet.Config.* to Davasorus.Utility.DotNet.Config.* and converts all all-caps acronym folders, types, and files (JSON/XML/YAML) to PascalCase (Json/Xml/Yaml). No behaviour changes.

1. Update using directives

Every consumer reference of the form

using Tyler.Utility.DotNet.Config.<...>;

becomes

using Davasorus.Utility.DotNet.Config.<...>;

Mechanical find/replace across consumer code. The package-id (NuGet install) was already Davasorus.Utility.DotNet.Config; this aligns the C# namespace with the package brand.

2. Update type names containing acronyms

The all-caps JSON, XML, and YAML segments in type and method names are now PascalCase: Json, Xml, Yaml. Examples:

Before After
ILoadJSONService ILoadJsonService
LoadJSONClient LoadJsonClient
IUpdateXMLClient IUpdateXmlClient
ConvertToYAMLService ConvertToYamlService

XML types that were already PascalCase (ILoadXmlService, LoadXmlService) are unchanged. Method names that were already correct (UpdateJsonFileAsync, OverwriteYamlFileAsync) are unchanged.

Mechanical find/replace; the C# compiler surfaces every breakage with a clear "could not find type" error pointing at the call site.

Stepping into the package via SourceLink now lands in Json/Load/Client/ instead of JSON/Load/Client/. Cosmetic — no semantic effect. Stack traces from production errors show the new paths.

4. String-literal type lookups break silently

If consumer code uses Type.GetType or Assembly.GetType with literal strings like "Tyler.Utility.DotNet.Config.JSON.Load.Service.LoadJSONService", those return null after the rename. Update the strings to the new namespace and casing. (Most consumers don't do this; [JsonDerivedType] or other discriminator-based polymorphism uses consumer-controlled strings, not full type names.)

Upgrade Notes — Group D-A (2026.x → 2026.x)

Group D-A adds IConfiguration providers for YAML and XML and an IConfigFileWatcher for typed-Service-API consumers. All additions are net-new types; no existing API changes. There is, however, one consumer-facing wrinkle worth flagging.

AddXmlFile ambiguity with Microsoft.Extensions.Configuration.Xml

Microsoft.Extensions.Configuration.Xml.XmlConfigurationExtensions ships with the .NET runtime and exposes its own AddXmlFile(...) extension. Critically, that static class lives in the Microsoft.Extensions.Configuration namespace (NOT Microsoft.Extensions.Configuration.Xml), and it is transitively pulled in via Microsoft.Extensions.Configuration.FileExtensions — which this package now depends on.

Result: as soon as a consumer file imports both Microsoft.Extensions.Configuration AND Davasorus.Utility.DotNet.Config.Configuration.Providers, calls to builder.AddXmlFile(...) become ambiguous and the C# compiler reports CS0104.

Disambiguation options — pick whichever fits your codebase style:

// Option A: type alias
using XmlConfigurationExtensions = Davasorus.Utility.DotNet.Config.Configuration.Providers.XmlConfigurationExtensions;

XmlConfigurationExtensions.AddXmlFile(builder, "config.xml");

// Option B: fully-qualified static call
Davasorus.Utility.DotNet.Config.Configuration.Providers.XmlConfigurationExtensions.AddXmlFile(builder, "config.xml");

// Option C: drop one of the conflicting using directives in this file
//   (e.g., remove `using Microsoft.Extensions.Configuration;` if you don't need
//   IConfigurationBuilder etc. by short name in this particular file)

AddYamlFile does not have this problem — Microsoft does not ship a YAML IConfiguration provider.

Behavioral differences vs. Microsoft.Extensions.Configuration.Xml

The two AddXmlFile implementations have distinct binding conventions:

  • Microsoft's: uses XML attributes on a flat root element to define keys; awkward semantics for non-attribute children and type-binding.
  • This package's: projects through XmlJsonBridge for JSON-compatible binding (attributes prefixed @, mixed text under #text, same-named siblings become arrays, root element's local name becomes the top-level key). XXE protection is inherited from the package's hardened reader settings.

Choose one or the other for a given consumer; mixing them in the same IConfigurationBuilder is technically possible but not recommended.

IConfigFileWatcher is the package's only new IDisposable

The architectural-invariants tripwire NoServiceOrClientImplementsIDisposable continues to enforce that no *Service or *Client class implements IDisposable. PhysicalConfigFileWatcher is intentionally outside the tripwire's scope (different name pattern); it owns a native FileSystemWatcher handle and IDisposable on it is honest, not cargo-culted.

EventId reservation

EventIds 1300–1310 are reserved for D-A telemetry. The current implementation does not allocate any of these — providers and the watcher do not log at the package level — but the reservation stands so future incremental additions stay in the documented range.

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 was computed.  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.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Davasorus.Utility.DotNet.Config:

Package Downloads
SA.OpenSearchTool.Business

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2026.2.2.14 39 5/17/2026
2026.2.2.13 36 5/15/2026
2026.2.2.12 105 5/10/2026
2026.2.2.11 100 5/6/2026
2026.2.2.10 92 5/5/2026
2026.2.2.9 90 5/5/2026
2026.2.2.8 98 5/4/2026
2026.2.2.7 84 5/4/2026
2026.2.2.6 96 5/3/2026
2026.2.2.5 91 5/2/2026
2026.2.2.4 104 5/2/2026
2026.2.2.3 92 5/2/2026
2026.2.2.2 89 5/1/2026
2026.2.2.1 97 5/1/2026
2026.2.1.4 119 4/16/2026
2026.2.1.3 853 4/11/2026
2026.2.1.2 142 4/9/2026
2026.2.1.1 158 4/1/2026
2026.1.3.5 101 3/29/2026
2026.1.3.4 110 3/24/2026
Loading failed