Muonroi.BuildingBlock
1.6.5
dotnet add package Muonroi.BuildingBlock --version 1.6.5
NuGet\Install-Package Muonroi.BuildingBlock -Version 1.6.5
<PackageReference Include="Muonroi.BuildingBlock" Version="1.6.5" />
<PackageVersion Include="Muonroi.BuildingBlock" Version="1.6.5" />
<PackageReference Include="Muonroi.BuildingBlock" />
paket add Muonroi.BuildingBlock --version 1.6.5
#r "nuget: Muonroi.BuildingBlock, 1.6.5"
#:package Muonroi.BuildingBlock@1.6.5
#addin nuget:?package=Muonroi.BuildingBlock&version=1.6.5
#tool nuget:?package=Muonroi.BuildingBlock&version=1.6.5
Muonroi Building Block
Introduction
This library provides entities such as User
, Role
, Permission
, and Language
, and comes with built-in Dependency Injection features, Bearer Token management, JSON handling utilities, string conversion, and localization for multiple languages. It is designed to accelerate the development of .NET applications following a clean architecture.
Prerequisites
- .NET SDK 9.0 or higher must be installed. You can download it from the official website.
- Build the project with
dotnet build
and create a NuGet package withdotnet pack
.
Quick Start
You can also use the muonroibase.template
dotnet template:
dotnet new install muonroibase.template
dotnet new muonroibase -n YourNewProjectName -C MyCoreName
See the Samples/Program.cs
file for a minimal setup example.
Additional cache samples are provided in the Samples
folder:
MemoryCache
demonstrates basic in-memory caching.MultiLevelCache
shows how to combine memory and distributed caches.RedisCache
illustrates using Redis as a distributed cache.MultipleCache
demonstrates using bothIMemoryCache
andIMultiLevelCacheService
.
Usage Guide
1. Configuring Program.cs
Here's how to configure your Program.cs
file to use the library's features:
using System.Reflection;
using Muonroi.BuildingBlock.External;
using Muonroi.BuildingBlock.External.Common.Constants;
using Muonroi.BuildingBlock.External.Cors;
using Muonroi.BuildingBlock.External.DI;
using Muonroi.BuildingBlock.External.Entity;
using Muonroi.BuildingBlock.External.Entity.DataSample;
using Muonroi.BuildingBlock.External.Logging;
using Muonroi.BuildingBlock.External.Messaging;
using Muonroi.BuildingBlock.External.Caching.Distributed.MultiLevel;
using Muonroi.BuildingBlock.External.Grpc;
using Muonroi.BuildingBlock.External.Consul;
using Serilog;
// Replace with your project's using statements
// using YourProject.Data;
// using YourProject.Permissions;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
Assembly assembly = Assembly.GetExecutingAssembly();
ConfigurationManager configuration = builder.Configuration;
builder.AddAppConfiguration();
builder.AddAutofacConfiguration();
builder.Host.UseSerilog((context, services, loggerConfiguration) =>
{
MSerilogAction.Configure(context, services, loggerConfiguration, false);
});
Log.Information("Starting {ApplicationName} API up", builder.Environment.ApplicationName);
try
{
IServiceCollection services = builder.Services;
// Register services
services.AddApplication(assembly);
services.AddInfrastructure(configuration);
services.SwaggerConfig(builder.Environment.ApplicationName);
services.AddScopeServices(typeof(<YourDbContext>).Assembly);
services.AddValidateBearerToken<<YourDbContext>, MTokenInfo, <YourPermissionEnum>>(configuration);
services.AddDbContextConfigure<<YourDbContext>, <YourPermissionEnum>>(configuration);
services.AddCors(configuration);
services.AddPermissionFilter<<YourPermissionEnum>>();
services.ConfigureMapper();
// Optional Integrations
services.AddMultiLevelCaching(configuration);
services.AddMessageBus(configuration, assembly);
services.AddGrpcServer();
services.AddServiceDiscovery(configuration, builder.Environment);
WebApplication app = builder.Build();
await app.UseServiceDiscoveryAsync(builder.Environment);
app.UseCors("MAllowDomains");
app.UseDefaultMiddleware<<YourDbContext>, <YourPermissionEnum>>();
app.AddLocalization(assembly);
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.ConfigureEndpoints();
app.MigrateDatabase<<YourDbContext>>();
await app.RunAsync();
}
catch (Exception ex)
{
Log.Fatal(ex, "Unhandled exception: {Message}", ex.Message);
}
finally
{
Log.Information("Shut down {ApplicationName} complete", builder.Environment.ApplicationName);
await Log.CloseAndFlushAsync();
}
2. Example appsettings.json
Configuration
Here's a comprehensive example of the appsettings.json
configuration:
{
"DatabaseConfigs": {
"DbType": "Sqlite",
"ConnectionStrings": {
"SqliteConnectionString": "Your encrypted connection string by secret key",
"MongoDbConnectionString": "Your encrypted connection string by secret key",
"SqlServerConnectionString": "Your encrypted connection string by secret key",
"MySqlConnectionString": "Your encrypted connection string by secret key",
"PostgreSqlConnectionString": "Your encrypted connection string by secret key"
}
},
"ApiKey": "",
"CacheConfigs": {
"CacheType": "MultiLevel"
},
"RedisConfigs": {
"Enable": true,
"Host": "Your host encrypted by secret key",
"Port": "Your port encrypted by secret key",
"Password": "Your password encrypted by secret key",
"Expire": 30,
"KeyPrefix": "Your prefix encrypted by secret key",
"AllMethodsEnableCache": false
},
"TokenConfigs": {
"Issuer": "https://exampledomain.com",
"Audience": "https://searchpartners.exampledomain.com",
"SigningKeys": "",
"UseRsa": true,
"ExpiryMinutes": 30,
"EnableCookieAuth": true,
"CookieName": "AuthToken",
"CookieSameSite": "Lax",
"PublicKey": "-----BEGIN PUBLIC KEY-----\n...-----END PUBLIC KEY-----",
"PrivateKey": "-----BEGIN RSA PRIVATE KEY-----\n...-----END RSA PRIVATE KEY-----"
},
"PaginationConfigs": {
"DefaultPageIndex": 1,
"DefaultPageSize": 10,
"MaxPageSize": 100
},
"ResourceSetting": {
"ResourceName": "Resources.ErrorMessages",
"lang": "vi-VN"
},
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Elastic.Serilog.Sinks" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft.AspNetCore": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{ "Name": "Console" },
{
"Name": "File",
"Args": { "path": "logs/log-.json", "formatter": "Serilog.Formatting.Elasticsearch.ElasticsearchJsonFormatter, Serilog.Formatting.Elasticsearch" }
},
{
"Name": "Elasticsearch",
"Args": {
"bootstrapMethod": "Silent",
"nodes": [ "http://localhost:9200" ],
"dataStream": "logs-muonroi-default",
"ilmPolicy": "muonroi-policy"
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
"Properties": {
"Application": "MyApplication"
}
},
"MAllowDomains": "https://localhost:52182,http://localhost:4200",
"GrpcServices": {
"Services": {
"YourService1": { "Uri": "http://localhost:5001" },
"YourService2": { "Uri": "http://localhost:5002" }
}
},
"ConsulConfigs": {
"ServiceName": "MyService",
"ConsulAddress": "http://localhost:8500",
"ServiceAddress": "http://localhost",
"ServicePort": 5000
},
"MessageBusConfigs": {
"BusType": "RabbitMq",
"RabbitMq": {
"Host": "localhost",
"VirtualHost": "/",
"Username": "guest",
"Password": "guest"
},
"Kafka": {
"Host": "localhost:9092",
"Topic": "sample-topic",
"GroupId": "sample-group"
}
},
"BackgroundJobConfigs": {
"JobType": "Hangfire",
"ConnectionString": "Your job storage connection string"
},
"KubernetesConfigs": {
"ClusterType": "K8s",
"ClusterEndpoint": "https://your-cluster-api"
},
"SecretKey": "Your secret key used to encrypt important values",
"EnableEncryption": true
}
3. Providing Resource Files
Create a Resources
directory in your project and place localization JSON files inside it. Each file should follow the pattern ErrorMessages-<culture>.json
(e.g., ErrorMessages-en-US.json
) and contain key-value pairs of error codes and messages. Ensure the ResourceSetting.ResourceName
value in appsettings.json
matches the base file name.
Main Components
- Entities: Core data models like
MUser
,MRole
,MPermission
, andMLanguage
. - Dependency Injection: Pre-configured DI using Autofac for managing application services and lifecycles.
- Authentication & Authorization: Robust token-based security with JWT, refresh tokens, and a flexible permission system.
- Data Access: Generic repositories (
MRepository<T>
) and query classes (MQuery<T>
) for Entity Framework Core, along with Dapper integration for performance-critical queries. - Caching: Multi-level caching support (in-memory and Redis) to improve performance.
- Service Discovery: Consul integration for registering and discovering services in a microservices environment.
- Message Bus: MassTransit integration for both RabbitMQ and Kafka to enable asynchronous communication and Saga patterns.
- Background Jobs: Configuration helpers for background job schedulers like Hangfire or Quartz.
- gRPC: Helpers to easily configure gRPC servers and clients.
- Logging: Centralized logging with Serilog, including out-of-the-box support for Elasticsearch.
- Cryptography:
MCryptographyExtension
uses SHA-256 for secure hashing and AES for configuration encryption. - Localization: Built-in support for multiple languages using JSON resource files.
Integrations
Redis Caching
Configure caching via CacheConfigs
and RedisConfigs
in your settings.
// In Program.cs
services.AddMultiLevelCaching(configuration);
gRPC Integration
Use helpers in External/Grpc
to configure gRPC. The GrpcHandler
class provides extension methods for registering servers and clients from appsettings.json
.
Consul Integration
Service discovery support is provided in External/Consul
. The ConsulHandler
registers and deregisters your service with Consul automatically. Call AddServiceDiscovery
and UseServiceDiscoveryAsync
in Program.cs
.
Message Bus (Kafka/RabbitMQ)
Configure your message broker via MessageBusConfigs
. The helper in External/Messaging
uses MassTransit to select the broker. Register consumers and sagas easily:
// In Program.cs
services.AddMessageBus(configuration, Assembly.GetExecutingAssembly(), cfg => {
/* Optional Saga/Consumer configuration */
});
Use PublishWithAuthContext
on IPublishEndpoint
to automatically include authentication headers when publishing messages.
Background Job Configuration
Configure background jobs using the BackgroundJobConfigs
section. Choose Hangfire
or Quartz
and specify a connection string for job storage.
// Example for Hangfire with SQL Server
services.AddHangfire(x => x.UseSqlServerStorage(configuration["BackgroundJobConfigs:ConnectionString"]));
app.UseHangfireDashboard();
BackgroundJob.Enqueue(() => Console.WriteLine("Hello from Hangfire"));
Kubernetes Integration
Specify deployment details for k8s
or k3s
clusters under KubernetesConfigs
, including the cluster type and API endpoint.
Documentation
This project uses DocFX to generate documentation.
- To build the documentation, run:
docfx build
- To preview the documentation locally, run:
docfx serve _site
For more details, see the guides in the docs
directory:
- Permission System Guide
- Permission Tree Guide
- Data Layer Guide
- Gateway Configuration
- Multi-Tenant Guide
- Cache Guide
- Token Guide
- Appsettings Guide
- Background Jobs Guide
Formatting
Run dotnet format Muonroi.BuildingBlock.sln
to apply the coding style defined in .editorconfig
.
Contribution
Please submit pull requests or open issues on GitHub to contribute or report bugs for this project.
License
This library is licensed under the MIT License. Please see the LICENSE
file for more details.
Product | Versions 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. |
-
net9.0
- Asp.Versioning.Mvc (>= 8.1.0)
- Autofac (>= 8.3.0)
- Autofac.Extensions.DependencyInjection (>= 10.0.0)
- Azure.Core (>= 1.46.2)
- Azure.Identity (>= 1.14.0)
- BCrypt.Net-Core (>= 1.6.0)
- BouncyCastle.NetCore (>= 2.2.1)
- Castle.Core (>= 5.2.1)
- Consul (>= 1.7.14.7)
- Dapper (>= 2.1.66)
- Dapper.Extensions.Caching.Redis (>= 5.3.1)
- Dapper.Extensions.MSSQL (>= 5.3.1)
- Dapper.Extensions.MySQL (>= 5.3.1)
- Dapper.Extensions.NetCore (>= 5.3.1)
- Dapper.Extensions.PostgreSQL (>= 5.3.1)
- DnsClient (>= 1.8.0)
- Elastic.Serilog.Sinks (>= 8.18.1)
- FluentValidation (>= 12.0.0)
- FluentValidation.AspNetCore (>= 11.3.1)
- FluentValidation.DependencyInjectionExtensions (>= 12.0.0)
- FreeRedis (>= 1.4.0)
- Google.Protobuf (>= 3.31.1)
- Grpc.AspNetCore.Server (>= 2.71.0)
- Grpc.Core.Api (>= 2.71.0)
- Grpc.Net.Client (>= 2.71.0)
- Grpc.Net.ClientFactory (>= 2.71.0)
- Grpc.Net.Common (>= 2.71.0)
- JetBrains.Annotations (>= 2024.3.0)
- MassTransit (>= 8.4.1)
- MassTransit.Kafka (>= 8.4.1)
- MassTransit.RabbitMQ (>= 8.4.1)
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 9.0.5)
- Microsoft.AspNetCore.Authentication.OpenIdConnect (>= 9.0.5)
- Microsoft.AspNetCore.Identity.EntityFrameworkCore (>= 9.0.5)
- Microsoft.AspNetCore.OpenApi (>= 9.0.5)
- Microsoft.Bcl.AsyncInterfaces (>= 9.0.5)
- Microsoft.Bcl.Cryptography (>= 9.0.5)
- Microsoft.Data.SqlClient (>= 6.0.2)
- Microsoft.Data.SqlClient.SNI.runtime (>= 6.0.2)
- Microsoft.Data.Sqlite.Core (>= 9.0.5)
- Microsoft.EntityFrameworkCore (>= 9.0.5)
- Microsoft.EntityFrameworkCore.Abstractions (>= 9.0.5)
- Microsoft.EntityFrameworkCore.Analyzers (>= 9.0.5)
- Microsoft.EntityFrameworkCore.InMemory (>= 9.0.5)
- Microsoft.EntityFrameworkCore.Relational (>= 9.0.5)
- Microsoft.EntityFrameworkCore.Sqlite (>= 9.0.5)
- Microsoft.EntityFrameworkCore.Sqlite.Core (>= 9.0.5)
- Microsoft.EntityFrameworkCore.SqlServer (>= 9.0.5)
- Microsoft.Extensions.Caching.Abstractions (>= 9.0.5)
- Microsoft.Extensions.Caching.Memory (>= 9.0.5)
- Microsoft.Extensions.Caching.StackExchangeRedis (>= 9.0.5)
- Microsoft.Extensions.Configuration (>= 9.0.5)
- Microsoft.Extensions.Configuration.Abstractions (>= 9.0.5)
- Microsoft.Extensions.Configuration.Binder (>= 9.0.5)
- Microsoft.Extensions.Configuration.EnvironmentVariables (>= 9.0.5)
- Microsoft.Extensions.Configuration.FileExtensions (>= 9.0.5)
- Microsoft.Extensions.Configuration.Json (>= 9.0.5)
- Microsoft.Extensions.DependencyInjection (>= 9.0.5)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.5)
- Microsoft.Extensions.DependencyModel (>= 9.0.5)
- Microsoft.Extensions.FileProviders.Abstractions (>= 9.0.5)
- Microsoft.Extensions.FileProviders.Physical (>= 9.0.5)
- Microsoft.Extensions.Hosting.Abstractions (>= 9.0.5)
- Microsoft.Extensions.Http (>= 9.0.5)
- Microsoft.Extensions.Http.Diagnostics (>= 9.5.0)
- Microsoft.Extensions.Http.Resilience (>= 9.5.0)
- Microsoft.Extensions.Identity.Stores (>= 9.0.5)
- Microsoft.Extensions.Logging (>= 9.0.5)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.5)
- Microsoft.Extensions.ObjectPool (>= 9.0.5)
- Microsoft.Extensions.Options (>= 9.0.5)
- Microsoft.Extensions.Primitives (>= 9.0.5)
- Microsoft.Extensions.Resilience (>= 9.5.0)
- Microsoft.Identity.Client (>= 4.72.1)
- Microsoft.Identity.Client.Extensions.Msal (>= 4.72.1)
- Microsoft.IdentityModel.Abstractions (>= 8.12.0)
- Microsoft.IdentityModel.JsonWebTokens (>= 8.12.0)
- Microsoft.IdentityModel.Logging (>= 8.12.0)
- Microsoft.IdentityModel.Protocols.OpenIdConnect (>= 8.12.0)
- Microsoft.IdentityModel.Tokens (>= 8.12.0)
- Microsoft.OpenApi (>= 1.6.24)
- MongoDB.Bson (>= 3.4.0)
- MongoDB.Driver (>= 3.4.0)
- MySql.Data (>= 9.3.0)
- MySql.EntityFrameworkCore (>= 9.0.3)
- MySqlConnector (>= 2.4.0)
- NetJSON (>= 1.4.4)
- Npgsql (>= 9.0.3)
- Npgsql.EntityFrameworkCore.PostgreSQL (>= 9.0.4)
- Polly (>= 8.5.2)
- Polly.Core (>= 8.5.2)
- Polly.Extensions.Http (>= 3.0.0)
- Pomelo.EntityFrameworkCore.MySql (>= 9.0.0-preview.3.efcore.9.0.0)
- RestEase (>= 1.6.4)
- Serilog (>= 4.3.0)
- Serilog.AspNetCore (>= 9.0.0)
- Serilog.Enrichers.Environment (>= 3.0.1)
- Serilog.Enrichers.Process (>= 3.0.0)
- Serilog.Enrichers.Thread (>= 4.0.0)
- Serilog.Exceptions (>= 8.4.0)
- Serilog.Expressions (>= 5.0.0)
- Serilog.Extensions.Hosting (>= 9.0.0)
- Serilog.Formatting.Compact (>= 3.0.0)
- Serilog.Formatting.Elasticsearch (>= 10.0.0)
- Serilog.Settings.Configuration (>= 9.0.0)
- Serilog.Sinks.Console (>= 6.0.0)
- Serilog.Sinks.Debug (>= 3.0.0)
- Serilog.Sinks.File (>= 7.0.0)
- SharpCompress (>= 0.40.0)
- Snappier (>= 1.2.0)
- SQLitePCLRaw.bundle_e_sqlite3 (>= 2.1.11)
- SQLitePCLRaw.core (>= 2.1.11)
- StackExchange.Redis (>= 2.8.37)
- Swashbuckle.AspNetCore (>= 8.1.4)
- Swashbuckle.AspNetCore.Swagger (>= 8.1.4)
- Swashbuckle.AspNetCore.SwaggerGen (>= 8.1.4)
- Swashbuckle.AspNetCore.SwaggerUI (>= 8.1.4)
- System.Buffers (>= 4.6.1)
- System.Configuration.ConfigurationManager (>= 9.0.5)
- System.Data.SqlClient (>= 4.9.0)
- System.Diagnostics.DiagnosticSource (>= 9.0.5)
- System.Diagnostics.EventLog (>= 9.0.5)
- System.IdentityModel.Tokens.Jwt (>= 8.12.0)
- System.Linq.Async (>= 6.0.1)
- System.Memory (>= 4.6.3)
- System.Reflection.Emit (>= 4.7.0)
- System.Reflection.Emit.ILGeneration (>= 4.7.0)
- System.Reflection.Emit.Lightweight (>= 4.7.0)
- System.Security.Cryptography.Pkcs (>= 9.0.5)
- System.Text.Json (>= 9.0.5)
- System.Threading.Tasks.Extensions (>= 4.6.3)
- ZstdSharp.Port (>= 0.8.5)
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.6.5 | 22 | 7/27/2025 | |
1.6.4 | 482 | 7/23/2025 | |
1.6.3 | 183 | 7/20/2025 | |
1.6.2 | 90 | 6/22/2025 | |
1.6.1 | 274 | 6/10/2025 | |
1.6.0 | 78 | 6/7/2025 | |
1.5.11 | 190 | 4/29/2025 | |
1.5.10 | 200 | 4/17/2025 | |
1.5.9.9 | 222 | 3/31/2025 | |
1.5.9.8 | 476 | 3/25/2025 | |
1.5.9.7 | 173 | 3/17/2025 | |
1.5.9.6 | 155 | 3/13/2025 | |
1.5.9.5 | 158 | 3/9/2025 | |
1.5.9.4 | 231 | 3/6/2025 | |
1.5.9.3 | 226 | 3/5/2025 | |
1.5.9.2 | 117 | 2/23/2025 | |
1.5.9.1 | 179 | 2/18/2025 | |
1.5.9 | 120 | 2/17/2025 | |
1.5.8 | 116 | 2/11/2025 | |
1.5.7 | 141 | 2/11/2025 | |
1.5.6 | 138 | 2/9/2025 | |
1.5.5 | 141 | 2/9/2025 | |
1.5.4 | 167 | 2/5/2025 | |
1.5.3 | 165 | 2/3/2025 | |
1.5.2 | 151 | 2/3/2025 | |
1.5.1.1 | 170 | 2/2/2025 | |
1.5.1 | 140 | 2/2/2025 | |
1.5.0 | 133 | 1/24/2025 | |
1.4.9 | 141 | 1/24/2025 | |
1.4.8 | 146 | 1/23/2025 | |
1.4.7 | 153 | 11/30/2024 | |
1.4.6 | 139 | 11/30/2024 | |
1.4.5 | 146 | 11/30/2024 | |
1.4.4 | 142 | 11/26/2024 | |
1.4.3 | 154 | 11/23/2024 | |
1.4.2 | 144 | 11/19/2024 | |
1.4.1 | 144 | 11/16/2024 | |
1.4.0 | 136 | 11/16/2024 | |
1.3.9 | 150 | 11/15/2024 | |
1.3.8 | 146 | 11/15/2024 | |
1.3.7 | 145 | 11/15/2024 | |
1.3.6 | 158 | 11/11/2024 | |
1.3.5 | 159 | 11/11/2024 | |
1.3.4 | 158 | 11/11/2024 | |
1.3.3 | 154 | 11/10/2024 | |
1.3.2 | 141 | 11/10/2024 | |
1.3.1 | 150 | 11/10/2024 | |
1.3.0 | 152 | 11/10/2024 | |
1.2.9 | 153 | 11/8/2024 | |
1.2.8 | 160 | 10/27/2024 | |
1.2.7 | 182 | 10/27/2024 | |
1.2.6 | 192 | 10/27/2024 | |
1.2.5 | 180 | 10/27/2024 | |
1.2.4 | 201 | 10/26/2024 | |
1.2.3 | 184 | 10/25/2024 | |
1.2.2 | 201 | 10/18/2024 | |
1.2.1 | 208 | 10/1/2024 | |
1.2.0 | 217 | 9/8/2024 | |
1.1.9 | 198 | 9/2/2024 | |
1.1.8 | 204 | 9/2/2024 | |
1.1.7 | 196 | 9/1/2024 | |
1.1.6 | 197 | 9/1/2024 | |
1.1.5 | 206 | 9/1/2024 | |
1.1.4 | 205 | 9/1/2024 | |
1.1.3 | 207 | 9/1/2024 | |
1.1.2 | 202 | 9/1/2024 | |
1.1.1 | 198 | 8/31/2024 | |
1.1.0 | 194 | 8/29/2024 | |
1.0.9 | 195 | 8/29/2024 | |
1.0.8 | 278 | 8/27/2024 | |
1.0.7 | 235 | 8/26/2024 | |
1.0.6 | 208 | 8/26/2024 | |
1.0.5 | 227 | 8/25/2024 | |
1.0.4 | 222 | 8/25/2024 | |
1.0.3 | 232 | 8/25/2024 | |
1.0.2 | 222 | 8/25/2024 | |
1.0.1 | 217 | 8/25/2024 | |
1.0.0 | 359 | 8/25/2024 |
Custom error message friendly.