From 06f4e698ada72cddc9f30f21d33620dd877bf193 Mon Sep 17 00:00:00 2001 From: Leandro Monaco Date: Tue, 31 May 2022 16:15:29 +1000 Subject: [PATCH 01/56] Delete AwsLambdaFunction/src/ServiceName.API/Properties directory --- .../Properties/launchSettings.json | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 AwsLambdaFunction/src/ServiceName.API/Properties/launchSettings.json diff --git a/AwsLambdaFunction/src/ServiceName.API/Properties/launchSettings.json b/AwsLambdaFunction/src/ServiceName.API/Properties/launchSettings.json deleted file mode 100644 index c378fd4..0000000 --- a/AwsLambdaFunction/src/ServiceName.API/Properties/launchSettings.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "profiles": { - "ServiceName.API": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:62060;http://localhost:62061" - }, - "Mock Lambda Test Tool": { - "commandName": "Executable", - "commandLineArgs": "--port 5050", - "workingDirectory": ".\\bin\\$(Configuration)\\net6.0", - "executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe" - } - } -} \ No newline at end of file From 24036ccff9cde396382ce458b96ff97bcde106cc Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Fri, 3 Jun 2022 15:08:36 +1000 Subject: [PATCH 02/56] [MicroserviceTemplate] Added new services Updated folder structure --- .../src/ServiceName.API/Program.cs | 25 +++--- .../ServiceName.API/ServiceName.API.csproj | 1 - .../src/ServiceName.API/appsettings.json | 12 +-- .../Common/Interfaces/ILoggingService.cs | 9 -- .../Common/Interfaces/ISettingsService.cs | 7 -- .../SettingItems/Queries/GetSettingsQuery.cs | 25 ------ .../CQRS/Commands/SaveSettingsCommand.cs | 33 +++++++ .../CQRS/Queries/GetSettingsQuery.cs | 29 +++++++ .../Common/Behaviours/LoggingBehaviour.cs | 45 ++++++++++ .../Interfaces/IAuthenticationService.cs | 2 +- .../Common/Interfaces/ICachingService.cs | 8 ++ .../Interfaces/IConfigurationService.cs | 13 +++ .../Common/Interfaces/ILoggingService.cs | 18 ++++ .../Common/Interfaces/ISettingsRepository.cs | 10 +++ .../ConfigureServices.cs | 8 +- .../ServiceName.Core/Model/SettingGroup.cs | 12 +++ .../src/ServiceName.Core/Model/Settings.cs | 10 +++ .../ServiceName.Core.csproj} | 8 -- .../src/ServiceName.Domain/Model/Settings.cs | 8 -- .../ServiceName.Domain.csproj | 9 -- .../AWS/LoggingServiceAws.cs | 53 ----------- .../BasicAuthenticationHandler.cs | 2 +- .../MockAuthenticationService.cs} | 4 +- .../Caching/InMemoryCachingService.cs | 28 ++++++ .../Caching/RedisCachingService.cs | 20 +++++ .../AwsParameterStoreConfigurationService.cs | 19 ++++ ...EnvironmentVariableConfigurationService.cs | 19 ++++ .../Configuration/FileConfigurationService.cs | 22 +++++ .../JsonStringConfigurationService.cs | 19 ++++ .../ConfigureServices.cs | 34 +++++--- .../Logging/CloudWatchLoggingService.cs | 51 +++++++++++ .../Logging/SeqLoggingService.cs | 35 ++++++++ .../DynamoDbSettingsRepository.cs | 87 +++++++++++++++++++ .../Repositories/MockSettingsRepository.cs | 24 +++++ .../Repositories/SettingsRepositoryAws.cs | 12 --- .../Repositories/SettingsRepositoryMock.cs | 12 --- .../ServiceName.Infrastructure.csproj | 5 +- MicroserviceTemplate/src/ServiceTemplate.sln | 22 ++--- MicroserviceTemplate/src/nginx.conf | 25 ++++++ MicroserviceTemplate/src/tye.yaml | 79 +++++++++++++++++ 40 files changed, 667 insertions(+), 197 deletions(-) delete mode 100644 MicroserviceTemplate/src/ServiceName.Application/Common/Interfaces/ILoggingService.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Application/Common/Interfaces/ISettingsService.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Application/SettingItems/Queries/GetSettingsQuery.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs rename MicroserviceTemplate/src/{ServiceName.Application => ServiceName.Core}/Common/Interfaces/IAuthenticationService.cs (74%) create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ICachingService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IConfigurationService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ILoggingService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ISettingsRepository.cs rename MicroserviceTemplate/src/{ServiceName.Application => ServiceName.Core}/ConfigureServices.cs (62%) create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Model/SettingGroup.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Model/Settings.cs rename MicroserviceTemplate/src/{ServiceName.Application/ServiceName.Application.csproj => ServiceName.Core/ServiceName.Core.csproj} (72%) delete mode 100644 MicroserviceTemplate/src/ServiceName.Domain/Model/Settings.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Domain/ServiceName.Domain.csproj delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/AWS/LoggingServiceAws.cs rename MicroserviceTemplate/src/ServiceName.Infrastructure/{Handlers => Authentication}/BasicAuthenticationHandler.cs (97%) rename MicroserviceTemplate/src/ServiceName.Infrastructure/{Mock/AuthenticationServiceMock.cs => Authentication/MockAuthenticationService.cs} (96%) create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Caching/InMemoryCachingService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Caching/RedisCachingService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/AwsParameterStoreConfigurationService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/EnvironmentVariableConfigurationService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/FileConfigurationService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/JsonStringConfigurationService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/CloudWatchLoggingService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/SeqLoggingService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/MockSettingsRepository.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryAws.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryMock.cs create mode 100644 MicroserviceTemplate/src/nginx.conf create mode 100644 MicroserviceTemplate/src/tye.yaml diff --git a/MicroserviceTemplate/src/ServiceName.API/Program.cs b/MicroserviceTemplate/src/ServiceName.API/Program.cs index 423b5d1..cec0899 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Program.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Program.cs @@ -1,10 +1,11 @@ using MediatR; +using Microsoft.AspNetCore.Mvc; using Microsoft.OpenApi.Models; -using Settings.Application; -using Settings.Application.Common.Interfaces; -using Settings.Application.Interfaces; -using Settings.Application.SettingItems.Queries; -using Settings.Infrastructure; +using ServiceName.Core; +using ServiceName.Core.CQRS.Commands; +using ServiceName.Core.CQRS.Queries; +using ServiceName.Core.Model; +using ServiceName.Infrastructure; var builder = WebApplication.CreateBuilder(args); @@ -48,11 +49,9 @@ }); }); -//Clean Architecute: Injects Services - -builder.Services.AddApplicationServices(builder.Configuration, builder.Logging); -builder.Services.AddInfrastructureServices(builder.Configuration, builder.Logging); - +//Clean Architecute: Service Injection +builder.Services.AddApplicationServices(); +builder.Services.AddInfrastructureServices(); var app = builder.Build(); @@ -69,9 +68,7 @@ app.MapControllers(); -app.MapGet("/settings", (IMediator mediator) => mediator.Send(new GetSettingsQueryRequest())); -//app.MapGet("/settings", (ISettingsService settingsService) => settingsService.GetSettings()); -//app.MapGet("/token", (IAuthenticationService authenticationService) => authenticationService.GenerateToken(1)); - +app.MapGet("/settings", (IMediator mediator) => mediator.Send(new GetSettingsQueryRequest() { TenantId = Guid.Parse("2e861859-1be6-4c0d-bfce-1e9d9d31a1c9") })); +app.MapPost("/settings", (IMediator mediator, [FromBody] Settings settings) => mediator.Send(new SaveSettingsCommandRequest() { TenantId = Guid.Parse("2e861859-1be6-4c0d-bfce-1e9d9d31a1c9"), Settings=settings })); app.Run(); diff --git a/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj b/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj index 3b63dc5..d11d15f 100644 --- a/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj +++ b/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj @@ -15,7 +15,6 @@ - \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.json index ec04bc1..28faa2f 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.json @@ -1,9 +1,9 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, "AllowedHosts": "*" } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Application/Common/Interfaces/ILoggingService.cs b/MicroserviceTemplate/src/ServiceName.Application/Common/Interfaces/ILoggingService.cs deleted file mode 100644 index 6c107de..0000000 --- a/MicroserviceTemplate/src/ServiceName.Application/Common/Interfaces/ILoggingService.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Settings.Application.Common.Interfaces -{ - public interface ILoggingService - { - void LogInformation(string message); - void LogWarning(string message); - void LogError(string message); - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Application/Common/Interfaces/ISettingsService.cs b/MicroserviceTemplate/src/ServiceName.Application/Common/Interfaces/ISettingsService.cs deleted file mode 100644 index eedac74..0000000 --- a/MicroserviceTemplate/src/ServiceName.Application/Common/Interfaces/ISettingsService.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Settings.Application.Interfaces -{ - public interface ISettingsRepository - { - Task GetSettingsAsync(); - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Application/SettingItems/Queries/GetSettingsQuery.cs b/MicroserviceTemplate/src/ServiceName.Application/SettingItems/Queries/GetSettingsQuery.cs deleted file mode 100644 index fd86838..0000000 --- a/MicroserviceTemplate/src/ServiceName.Application/SettingItems/Queries/GetSettingsQuery.cs +++ /dev/null @@ -1,25 +0,0 @@ -using MediatR; -using Settings.Application.Interfaces; - -namespace Settings.Application.SettingItems.Queries -{ - public record GetSettingsQueryRequest :IRequest - { - - } - - public class GetSettingsQueryHandler : IRequestHandler - { - ISettingsRepository _settingsRepository; - - public GetSettingsQueryHandler(ISettingsRepository settingsRepository) - { - _settingsRepository = settingsRepository; - } - - public async Task Handle(GetSettingsQueryRequest request, CancellationToken cancellationToken) - { - return await _settingsRepository.GetSettingsAsync(); - } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs new file mode 100644 index 0000000..62703ce --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MediatR; +using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Model; + +namespace ServiceName.Core.CQRS.Commands +{ + public record SaveSettingsCommandRequest : IRequest + { + public Guid TenantId { get; set; } + public Settings Settings { get; set; } + } + + public class CreateTodoListCommandHandler : IRequestHandler + { + private readonly ISettingsRepository _settingsRepository; + + public CreateTodoListCommandHandler(ISettingsRepository settingsRepository) + { + _settingsRepository = settingsRepository; + } + + public async Task Handle(SaveSettingsCommandRequest request, CancellationToken cancellationToken) + { + var result = await _settingsRepository.SaveSettingsAsync(request.TenantId, request.Settings); + return result; + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs new file mode 100644 index 0000000..3cfd6b9 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs @@ -0,0 +1,29 @@ +using System.Text.Json; +using MediatR; +using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Model; + +namespace ServiceName.Core.CQRS.Queries +{ + public record GetSettingsQueryRequest : IRequest + { + public Guid TenantId { get; set; } + } + public class GetSettingsQueryHandler : IRequestHandler + { + ISettingsRepository _settingsRepository; + IConfigurationService _configurationService; + + public GetSettingsQueryHandler(ISettingsRepository settingsRepository, IConfigurationService configurationService) + { + _settingsRepository = settingsRepository; + _configurationService = configurationService; + } + + public async Task Handle(GetSettingsQueryRequest request, CancellationToken cancellationToken) + { + var settings = await _settingsRepository.GetSettingsAsync(request.TenantId); + return settings; + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs new file mode 100644 index 0000000..f59a1e5 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs @@ -0,0 +1,45 @@ +using System.Diagnostics; +using System.Text.Json; +using MediatR; +using ServiceName.Core.Common.Interfaces; + +namespace ServiceName.Core.Common.Behaviours +{ + public class LoggingBehaviour : IPipelineBehavior where TRequest : IRequest + { + ILoggingService _loggingService; + private readonly Stopwatch _timer; + + public LoggingBehaviour(ILoggingService loggingService) + { + _loggingService = loggingService; + _timer = new Stopwatch(); + } + + public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) + { + var requestId = Guid.NewGuid(); + var requestName = typeof(TRequest).Name; + + await _loggingService.LogInformationAsync(@"requestId {requestId} + requestName {requestName} + type {requestType} + payload {requestPayload}", requestId, requestName, "Request", JsonSerializer.Serialize(request)); + + _timer.Start(); + var response = await next(); + _timer.Stop(); + + await _loggingService.LogInformationAsync(@"requestId {requestId} + requestName {requestName} + type {requestType} + payload {requestPayload} {elapsedMilliseconds}", requestId, requestName, "Response", JsonSerializer.Serialize(response), _timer.ElapsedMilliseconds); + + return response; + } + + + } + + +} diff --git a/MicroserviceTemplate/src/ServiceName.Application/Common/Interfaces/IAuthenticationService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IAuthenticationService.cs similarity index 74% rename from MicroserviceTemplate/src/ServiceName.Application/Common/Interfaces/IAuthenticationService.cs rename to MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IAuthenticationService.cs index 0de1cac..d395c3e 100644 --- a/MicroserviceTemplate/src/ServiceName.Application/Common/Interfaces/IAuthenticationService.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IAuthenticationService.cs @@ -1,4 +1,4 @@ -namespace Settings.Application.Common.Interfaces +namespace ServiceName.Core.Common.Interfaces { public interface IAuthenticationService { diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ICachingService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ICachingService.cs new file mode 100644 index 0000000..e9b59c0 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ICachingService.cs @@ -0,0 +1,8 @@ +namespace ServiceName.Core.Common.Interfaces +{ + public interface ICachingService + { + T Get(string key); + void Set(string key, object value); + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IConfigurationService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IConfigurationService.cs new file mode 100644 index 0000000..1e1bf1e --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IConfigurationService.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.Configuration; + +namespace ServiceName.Core.Common.Interfaces +{ + /// + /// https://docs.microsoft.com/en-us/dotnet/core/extensions/configuration + /// + public interface IConfigurationService + { + void LoadFromSource(params string[] configurationSources); + public IConfiguration Configuration { get; } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ILoggingService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ILoggingService.cs new file mode 100644 index 0000000..2b33ae1 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ILoggingService.cs @@ -0,0 +1,18 @@ +namespace ServiceName.Core.Common.Interfaces +{ + /// + /// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-6.0 + /// + public interface ILoggingService + { + /// + /// + /// + /// Log Message + /// Allows Structure Logging: https://softwareengineering.stackexchange.com/questions/312197/benefits-of-structured-logging-vs-basic-logging + /// + Task LogInformationAsync(string message, params object[] propertyValues); + Task LogWarningAsync(string message, params object[] propertyValues); + Task LogErrorAsync(string message, params object[] propertyValues); + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ISettingsRepository.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ISettingsRepository.cs new file mode 100644 index 0000000..ae47f8f --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ISettingsRepository.cs @@ -0,0 +1,10 @@ +using ServiceName.Core.Model; + +namespace ServiceName.Core.Common.Interfaces +{ + public interface ISettingsRepository + { + Task GetSettingsAsync(Guid tenantId); + Task SaveSettingsAsync(Guid tenantId, Settings settings); + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Application/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs similarity index 62% rename from MicroserviceTemplate/src/ServiceName.Application/ConfigureServices.cs rename to MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs index fefe706..02d58b3 100644 --- a/MicroserviceTemplate/src/ServiceName.Application/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs @@ -1,16 +1,16 @@ using System.Reflection; using MediatR; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; +using ServiceName.Core.Common.Behaviours; -namespace Settings.Application +namespace ServiceName.Core { public static class ConfigureServices { - public static IServiceCollection AddApplicationServices(this IServiceCollection services, IConfiguration configuration, ILoggingBuilder logging) + public static IServiceCollection AddApplicationServices(this IServiceCollection services) { services.AddMediatR(Assembly.GetExecutingAssembly()); + services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehaviour<,>)); return services; } } diff --git a/MicroserviceTemplate/src/ServiceName.Core/Model/SettingGroup.cs b/MicroserviceTemplate/src/ServiceName.Core/Model/SettingGroup.cs new file mode 100644 index 0000000..f9c45b7 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Model/SettingGroup.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace ServiceName.Core.Model +{ + public class SettingGroup + { + [JsonPropertyName("Setting1")] + public string Setting1 { get; set; } + [JsonPropertyName("Setting2")] + public string Setting2 { get; set; } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Model/Settings.cs b/MicroserviceTemplate/src/ServiceName.Core/Model/Settings.cs new file mode 100644 index 0000000..099deef --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Model/Settings.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace ServiceName.Core.Model +{ + public class Settings + { + [JsonPropertyName("Group1")] + public SettingGroup Group1 { get; set; } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Application/ServiceName.Application.csproj b/MicroserviceTemplate/src/ServiceName.Core/ServiceName.Core.csproj similarity index 72% rename from MicroserviceTemplate/src/ServiceName.Application/ServiceName.Application.csproj rename to MicroserviceTemplate/src/ServiceName.Core/ServiceName.Core.csproj index 1ae2dee..8f7dd8b 100644 --- a/MicroserviceTemplate/src/ServiceName.Application/ServiceName.Application.csproj +++ b/MicroserviceTemplate/src/ServiceName.Core/ServiceName.Core.csproj @@ -12,12 +12,4 @@ - - - - - - - - diff --git a/MicroserviceTemplate/src/ServiceName.Domain/Model/Settings.cs b/MicroserviceTemplate/src/ServiceName.Domain/Model/Settings.cs deleted file mode 100644 index ee78372..0000000 --- a/MicroserviceTemplate/src/ServiceName.Domain/Model/Settings.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Settings.Domain.Model -{ - public class Settings - { - public string Setting1 { get; set; } - public string Setting2 { get; set; } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Domain/ServiceName.Domain.csproj b/MicroserviceTemplate/src/ServiceName.Domain/ServiceName.Domain.csproj deleted file mode 100644 index 27ac386..0000000 --- a/MicroserviceTemplate/src/ServiceName.Domain/ServiceName.Domain.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - net6.0 - enable - enable - - - diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/AWS/LoggingServiceAws.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/AWS/LoggingServiceAws.cs deleted file mode 100644 index ad5b9c3..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/AWS/LoggingServiceAws.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Amazon.CloudWatchLogs; -using Serilog; -using Settings.Application.Common.Interfaces; - -namespace Settings.Infrastructure.AWS -{ - internal class LoggingServiceAws : ILoggingService - { - public LoggingServiceAws() - { - //// remove default logging providers - //builder.Logging.ClearProviders(); - - //var awsConfig = new AmazonCloudWatchLogsConfig() { ServiceURL = "http://host.docker.internal:4566" }; - //awsConfig.UseHttp = true; - //awsConfig.RegionEndpoint = Amazon.RegionEndpoint.APSoutheast1; - //var client = new AmazonCloudWatchLogsClient("aws", "aws", awsConfig); - - //// Serilog configuration - //var logger = new LoggerConfiguration() - // .MinimumLevel.Verbose() - // .WriteTo.AmazonCloudWatch( - // logGroup: "/logs", - // logStreamPrefix: DateTime.UtcNow.ToString("yyyyMMddHHmmssfff"), - // cloudWatchClient: client - // ).CreateLogger(); - //// Register Serilog - //builder.Logging.AddSerilog(logger); - - //Serilog.Debugging.SelfLog.Enable(Console.Error); - } - - public void LogError(string message) - { - throw new NotImplementedException(); - } - - public void LogInformation(string message) - { - throw new NotImplementedException(); - } - - public void LogWarning(string message) - { - throw new NotImplementedException(); - } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Handlers/BasicAuthenticationHandler.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/BasicAuthenticationHandler.cs similarity index 97% rename from MicroserviceTemplate/src/ServiceName.Infrastructure/Handlers/BasicAuthenticationHandler.cs rename to MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/BasicAuthenticationHandler.cs index 8093b91..42459df 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Handlers/BasicAuthenticationHandler.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/BasicAuthenticationHandler.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace Settings.Infrastructure.Handlers +namespace ServiceName.Infrastructure.Authentication { public class BasicAuthenticationHandler : AuthenticationHandler { diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Mock/AuthenticationServiceMock.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/MockAuthenticationService.cs similarity index 96% rename from MicroserviceTemplate/src/ServiceName.Infrastructure/Mock/AuthenticationServiceMock.cs rename to MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/MockAuthenticationService.cs index 145040b..d289b8b 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Mock/AuthenticationServiceMock.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/MockAuthenticationService.cs @@ -2,9 +2,9 @@ using System.Security.Claims; using System.Text; using Microsoft.IdentityModel.Tokens; -using Settings.Application.Common.Interfaces; +using ServiceName.Core.Common.Interfaces; -namespace Settings.Infrastructure.Mock +namespace ServiceName.Infrastructure.Authentication { /// /// https://dotnetcoretutorials.com/2020/01/15/creating-and-validating-jwt-tokens-in-asp-net-core/ diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Caching/InMemoryCachingService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Caching/InMemoryCachingService.cs new file mode 100644 index 0000000..a12d14b --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Caching/InMemoryCachingService.cs @@ -0,0 +1,28 @@ +using Microsoft.Extensions.Caching.Memory; +using ServiceName.Core.Common.Interfaces; + +namespace ServiceName.Infrastructure.Caching +{ + /// + ///https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-6.0 + /// + public class InMemoryCachingService : ICachingService + { + private readonly IMemoryCache _memoryCache; + + public InMemoryCachingService() + { + _memoryCache = new MemoryCache(new MemoryCacheOptions()); + } + + public T Get(string key) + { + return (T)_memoryCache.Get(key); + } + + public void Set(string key, object value) + { + _memoryCache.Set(key, value); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Caching/RedisCachingService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Caching/RedisCachingService.cs new file mode 100644 index 0000000..47188e7 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Caching/RedisCachingService.cs @@ -0,0 +1,20 @@ +using ServiceName.Core.Common.Interfaces; + +namespace ServiceName.Infrastructure.Caching +{ + /// + /// https://github.com/redis/redis-om-dotnet + /// + public class RedisCachingService : ICachingService + { + public T Get(string key) + { + throw new NotImplementedException(); + } + + public void Set(string key, object value) + { + throw new NotImplementedException(); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/AwsParameterStoreConfigurationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/AwsParameterStoreConfigurationService.cs new file mode 100644 index 0000000..fd1c4be --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/AwsParameterStoreConfigurationService.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Configuration; +using ServiceName.Core.Common.Interfaces; + +namespace ServiceName.Infrastructure.Configuration +{ + /// + /// https://github.com/aws/aws-dotnet-extensions-configuration + /// + public class AwsParameterStoreConfigurationService : IConfigurationService + { + IConfiguration _configuration; + public IConfiguration Configuration => _configuration; + + public void LoadFromSource(params string[] configurationSources) + { + throw new NotImplementedException(); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/EnvironmentVariableConfigurationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/EnvironmentVariableConfigurationService.cs new file mode 100644 index 0000000..1c1a2de --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/EnvironmentVariableConfigurationService.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Configuration; +using ServiceName.Core.Common.Interfaces; + +namespace ServiceName.Infrastructure.Configuration +{ + internal class EnvironmentVariableConfigurationService : IConfigurationService + { + IConfiguration _configuration; + public IConfiguration Configuration => _configuration; + + public void LoadFromSource(params string[] configurationSources) + { + _configuration = new ConfigurationBuilder() + .AddEnvironmentVariables() + .Build(); + } + + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/FileConfigurationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/FileConfigurationService.cs new file mode 100644 index 0000000..f538809 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/FileConfigurationService.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using System.Text; +using Microsoft.Extensions.Configuration; +using ServiceName.Core.Common.Interfaces; + +namespace ServiceName.Infrastructure.Configuration +{ + public class FileConfigurationService : IConfigurationService + { + IConfiguration _configuration; + public IConfiguration Configuration => _configuration; + + public void LoadFromSource(params string[] configurationSources) + { + _configuration = new ConfigurationBuilder() + .SetBasePath(Assembly.GetExecutingAssembly().Location) + .AddJsonFile(configurationSources[0], optional: false, reloadOnChange: true) + .Build(); + } + + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/JsonStringConfigurationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/JsonStringConfigurationService.cs new file mode 100644 index 0000000..2d6a03b --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/JsonStringConfigurationService.cs @@ -0,0 +1,19 @@ +using System.Text; +using Microsoft.Extensions.Configuration; +using ServiceName.Core.Common.Interfaces; + +namespace ServiceName.Infrastructure.Configuration +{ + public class JsonStringConfigurationService : IConfigurationService + { + IConfiguration _configuration; + public IConfiguration Configuration => _configuration; + + public void LoadFromSource(params string[] configurationSources) + { + _configuration = new ConfigurationBuilder() + .AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(configurationSources[0]))) + .Build(); //https://github.com/dotnet/runtime/issues/36018 + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs index 4ce5142..0a04140 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs @@ -2,27 +2,35 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Settings.Application.Interfaces; -using Settings.Infrastructure.Handlers; -using Settings.Infrastructure.Mock; -using Settings.Infrastructure.Repositories; +using ServiceName.Core.Common.Interfaces; +using ServiceName.Infrastructure.Authentication; +using ServiceName.Infrastructure.Configuration; +using ServiceName.Infrastructure.Logging; +using ServiceName.Infrastructure.Repositories; -namespace Settings.Infrastructure +namespace ServiceName.Infrastructure { public static class ConfigureServices { - public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, IConfiguration configuration, ILoggingBuilder logging) + public static IServiceCollection AddInfrastructureServices(this IServiceCollection services) { - services.AddScoped(); - //services.AddScoped(); - services.AddSingleton(); + //For Development Environment + //services.AddScoped(); + services.AddScoped(); + services.AddSingleton(); + services.AddSingleton(); + //services.AddSingleton(); + + //For Cloud Environment + //services.AddScoped(); + //services.AddSingleton(); // Authentication - services.AddAuthentication("BasicAuthentication") - .AddScheme("BasicAuthentication", null); - services.AddAuthorization(); - + //services.AddAuthentication("BasicAuthentication") + // .AddScheme("BasicAuthentication", null); + //services.AddAuthorization(); + return services; } } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/CloudWatchLoggingService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/CloudWatchLoggingService.cs new file mode 100644 index 0000000..575f9fc --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/CloudWatchLoggingService.cs @@ -0,0 +1,51 @@ +using Amazon.CloudWatchLogs; +using Serilog; +using Serilog.Sinks.AwsCloudWatch; +using ServiceName.Core.Common.Interfaces; + +namespace ServiceName.Infrastructure.Logging +{ + internal class CloudWatchLoggingService : ILoggingService + { + + public CloudWatchLoggingService() + { + //// remove default logging providers + //builder.Logging.ClearProviders(); + + var awsConfig = new AmazonCloudWatchLogsConfig() { ServiceURL = "http://host.docker.internal:4566" }; + awsConfig.UseHttp = true; + awsConfig.RegionEndpoint = Amazon.RegionEndpoint.APSoutheast1; + var client = new AmazonCloudWatchLogsClient("aws", "aws", awsConfig); + + // Serilog configuration + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Verbose() + .WriteTo.AmazonCloudWatch( + logGroup: "/logs", + logStreamPrefix: DateTime.UtcNow.ToString("yyyyMMddHHmmssfff"), + cloudWatchClient: client + ).CreateLogger(); + + //Serilog.Debugging.SelfLog.Enable(Console.Error); + + //// Register Serilog + //builder.Logging.AddSerilog(logger); + } + + public async Task LogErrorAsync(string message, params object[] propertyValues) + { + Log.Error(message, propertyValues); + } + + public async Task LogInformationAsync(string message, params object[] propertyValues) + { + Log.Information(message, propertyValues); + } + + public async Task LogWarningAsync(string message, params object[] propertyValues) + { + Log.Warning(message, propertyValues); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/SeqLoggingService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/SeqLoggingService.cs new file mode 100644 index 0000000..6f0654f --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/SeqLoggingService.cs @@ -0,0 +1,35 @@ +using Serilog; +using ServiceName.Core.Common.Interfaces; + +namespace ServiceName.Infrastructure.Logging +{ + public class SeqLoggingService : ILoggingService + { + /// + /// Note: Seq already performs asynchronous batching natively. + /// https://github.com/serilog/serilog-sinks-async + /// + public SeqLoggingService() + { + Log.Logger = new LoggerConfiguration() + .WriteTo.Console() + .WriteTo.Seq("http://localhost:5341") + .CreateLogger(); + } + + public async Task LogErrorAsync(string message, params object[] propertyValues) + { + Log.Error(message, propertyValues); + } + + public async Task LogInformationAsync(string message, params object[] propertyValues) + { + Log.Information(message, propertyValues); + } + + public async Task LogWarningAsync(string message, params object[] propertyValues) + { + Log.Warning(message, propertyValues); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs new file mode 100644 index 0000000..eca65d8 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs @@ -0,0 +1,87 @@ +using System.Text.Json; +using Amazon; +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.Model; +using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Model; + +namespace ServiceName.Infrastructure.Repositories +{ + /// + //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.DotNet.html + //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html + //aws --endpoint-url=http://localhost:4566 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST + //awslocal dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST + /// + public class DynamoDbSettingsRepository : ISettingsRepository + { + AmazonDynamoDBClient _amazonDynamoDBClient; + string _dynamoTableName = "ServiceName_Setting"; + + public DynamoDbSettingsRepository() + { + AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig + { + RegionEndpoint = RegionEndpoint.GetBySystemName("ap-southeast-2"), + UseHttp = true, + ServiceURL = "http://localhost:4566" + }; + + _amazonDynamoDBClient = new AmazonDynamoDBClient("123", "123", clientConfig); + } + + public async Task GetSettingsAsync(Guid tenantId) + { + var getRequest = new GetItemRequest + { + TableName = _dynamoTableName, + Key = new Dictionary() { { "TenantId", new AttributeValue { S = tenantId.ToString().ToLower() } } }, + }; + + var response = await _amazonDynamoDBClient.GetItemAsync(getRequest); + + //If there is no settings configured for the tenant create a new one + if (response.Item.Count.Equals(0)) + { + var newSettings = new Settings() + { + Group1 = new SettingGroup() + { + Setting1 = "a", + Setting2 = "b" + }, + }; + + await SaveSettings(tenantId, newSettings); + + return newSettings; + } + + var existingSettings = JsonSerializer.Deserialize(response.Item["Settings"].S, new JsonSerializerOptions() { MaxDepth = 0, PropertyNameCaseInsensitive = true }); + + return existingSettings; + } + + public async Task SaveSettingsAsync(Guid tenantId, Settings settings) + { + await SaveSettings(tenantId, settings); + + return true; + } + + private async Task SaveSettings(Guid tenantId, Settings settings) + { + var putRequest = new PutItemRequest + { + TableName = _dynamoTableName, + Item = new Dictionary() + { + { "TenantId", new AttributeValue { S = tenantId.ToString() } }, + { "Settings", new AttributeValue { S = JsonSerializer.Serialize(settings).ToLower() } } + } + }; + + var response = await _amazonDynamoDBClient.PutItemAsync(putRequest); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/MockSettingsRepository.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/MockSettingsRepository.cs new file mode 100644 index 0000000..13b76cc --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/MockSettingsRepository.cs @@ -0,0 +1,24 @@ +using System.Text.Json; +using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Model; + +namespace ServiceName.Infrastructure.Repositories +{ + public class MockSettingsRepository : ISettingsRepository + { + public async Task GetSettingsAsync(Guid tenantId) + { + var settings = new Settings() { + Group1 = new SettingGroup() { + Setting1 = "a", + Setting2 = "b" } + }; + return await Task.FromResult(settings); + } + + public async Task SaveSettingsAsync(Guid tenantId, Settings settings) + { + return await Task.FromResult(true); + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryAws.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryAws.cs deleted file mode 100644 index f310ee7..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryAws.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Settings.Application.Interfaces; - -namespace Settings.Infrastructure.Repositories -{ - public class SettingsRepositoryAws : ISettingsRepository - { - Task ISettingsRepository.GetSettingsAsync() - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryMock.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryMock.cs deleted file mode 100644 index ebbc480..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryMock.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Settings.Application.Interfaces; - -namespace Settings.Infrastructure.Repositories -{ - public class SettingsRepositoryMock : ISettingsRepository - { - public async Task GetSettingsAsync() - { - return await Task.FromResult(new Domain.Model.Settings() { Setting1 = "a", Setting2 = "b" }); - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj b/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj index 81126e9..22d2823 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj @@ -7,6 +7,8 @@ + + @@ -14,11 +16,12 @@ + - + diff --git a/MicroserviceTemplate/src/ServiceTemplate.sln b/MicroserviceTemplate/src/ServiceTemplate.sln index b9c7559..1c05b97 100644 --- a/MicroserviceTemplate/src/ServiceTemplate.sln +++ b/MicroserviceTemplate/src/ServiceTemplate.sln @@ -5,11 +5,15 @@ VisualStudioVersion = 17.2.32505.173 MinimumVisualStudioVersion = 15.0.26124.0 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.API", "ServiceName.API\ServiceName.API.csproj", "{DD4C16FE-1C12-4B91-92D1-184329282E65}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.Application", "ServiceName.Application\ServiceName.Application.csproj", "{8F3CDF89-14BD-4553-BD25-B347CB5E1C89}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.Infrastructure", "ServiceName.Infrastructure\ServiceName.Infrastructure.csproj", "{9B43FACF-50A2-4332-9642-3F723488DE40}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.Domain", "ServiceName.Domain\ServiceName.Domain.csproj", "{64B2E9A0-8C17-4F93-8E8D-C55AF7C5B632}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{99A702BB-D8C1-4F89-9B4E-EB891693FD7A}" + ProjectSection(SolutionItems) = preProject + nginx.conf = nginx.conf + tye.yaml = tye.yaml + EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.Infrastructure", "ServiceName.Infrastructure\ServiceName.Infrastructure.csproj", "{9B43FACF-50A2-4332-9642-3F723488DE40}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.Core", "ServiceName.Core\ServiceName.Core.csproj", "{3733AF37-1B4D-4178-B4A8-46702493BCEC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,18 +25,14 @@ Global {DD4C16FE-1C12-4B91-92D1-184329282E65}.Debug|Any CPU.Build.0 = Debug|Any CPU {DD4C16FE-1C12-4B91-92D1-184329282E65}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD4C16FE-1C12-4B91-92D1-184329282E65}.Release|Any CPU.Build.0 = Release|Any CPU - {8F3CDF89-14BD-4553-BD25-B347CB5E1C89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8F3CDF89-14BD-4553-BD25-B347CB5E1C89}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8F3CDF89-14BD-4553-BD25-B347CB5E1C89}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8F3CDF89-14BD-4553-BD25-B347CB5E1C89}.Release|Any CPU.Build.0 = Release|Any CPU - {64B2E9A0-8C17-4F93-8E8D-C55AF7C5B632}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {64B2E9A0-8C17-4F93-8E8D-C55AF7C5B632}.Debug|Any CPU.Build.0 = Debug|Any CPU - {64B2E9A0-8C17-4F93-8E8D-C55AF7C5B632}.Release|Any CPU.ActiveCfg = Release|Any CPU - {64B2E9A0-8C17-4F93-8E8D-C55AF7C5B632}.Release|Any CPU.Build.0 = Release|Any CPU {9B43FACF-50A2-4332-9642-3F723488DE40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9B43FACF-50A2-4332-9642-3F723488DE40}.Debug|Any CPU.Build.0 = Debug|Any CPU {9B43FACF-50A2-4332-9642-3F723488DE40}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B43FACF-50A2-4332-9642-3F723488DE40}.Release|Any CPU.Build.0 = Release|Any CPU + {3733AF37-1B4D-4178-B4A8-46702493BCEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3733AF37-1B4D-4178-B4A8-46702493BCEC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3733AF37-1B4D-4178-B4A8-46702493BCEC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3733AF37-1B4D-4178-B4A8-46702493BCEC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/MicroserviceTemplate/src/nginx.conf b/MicroserviceTemplate/src/nginx.conf new file mode 100644 index 0000000..26a9b52 --- /dev/null +++ b/MicroserviceTemplate/src/nginx.conf @@ -0,0 +1,25 @@ +server { + listen 80; + + location /A { + proxy_pass http://localhost:51001/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection keep-alive; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /B { + proxy_pass http://appB/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection keep-alive; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/tye.yaml b/MicroserviceTemplate/src/tye.yaml new file mode 100644 index 0000000..4840147 --- /dev/null +++ b/MicroserviceTemplate/src/tye.yaml @@ -0,0 +1,79 @@ +# tye application configuration file +# read all about it at https://github.com/dotnet/tye +# +# when you've given us a try, we'd love to know what you think: +# https://aka.ms/AA7q20u +# + +extensions: +- name: seq + logPath: ./.logs + +name: servicetemplate +services: + + - name: nginx-ingress-controller + image: nginx + bindings: + - protocol: http + port: 51000 + volumes: + - source: nginx.conf + target: /etc/nginx/conf.d/default.conf + - name: servicename-api + project: ServiceName.API/ServiceName.API.csproj + bindings: + - port: 51001 + replicas: 2 + + #- name: SQL-Database + # image: mcr.microsoft.com/mssql/server:2019-latest + # bindings: + # - connectionString: Data Source=host.docker.internal,1433;Initial Catalog=ServiceDB;Persist Security Info=True;User ID=sa;Password=${env:SA_PASSWORD} + # port: 1433 + # env: + # - name: SA_PASSWORD + # value: M3rz0ug4!!!! + # - name: ACCEPT_EULA + # value: Y + + - name: redis-cache + image: redis + bindings: + - port: 6379 + connectionString: "${host}:${port}" + args: "redis-cli -h redis MONITOR" + +#https://github.com/dotnet/tye/issues/190 +#https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html + - name: dynamodb-local + image: "amazon/dynamodb-local:latest" + args: -jar DynamoDBLocal.jar -sharedDb -dbPath ./data + volumes: + - source: "/mnt/c/Temp" + target: "/mnt/c/Temp/DynamoDB" + bindings: + - port: 8000 + env: + - name: AWS_ACCESS_KEY_ID + value: test + - name: AWS_SECRET_ACCESS_KEY + value: test + - name: MYSQL_USER + value: steeltoe + - name: REGION + value: ap-southeast-2 + #volumes: + # - source: ./docker/dynamodb:/home/dynamodblocal/data + # target: /home/dynamodblocal + # bindings: + # - port: 8000 + # env: + # - name: AWS_ACCESS_KEY_ID + # value: test + # - name: AWS_SECRET_ACCESS_KEY + # value: test + # - name: MYSQL_USER + # value: steeltoe + # - name: REGION + # value: ap-southeast-2 \ No newline at end of file From c046b81bb919e23d0ab4549a8303d61c8ba8b803 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Fri, 3 Jun 2022 16:46:02 +1000 Subject: [PATCH 03/56] [MicroserviceTemplate] Fixed DynamoDB's Tye configuration --- .../DynamoDbSettingsRepository.cs | 6 ++-- MicroserviceTemplate/src/tye.yaml | 29 ++++--------------- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs index eca65d8..d5f1abd 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs @@ -10,7 +10,7 @@ namespace ServiceName.Infrastructure.Repositories /// //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.DotNet.html //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html - //aws --endpoint-url=http://localhost:4566 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST + //aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST //awslocal dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST /// public class DynamoDbSettingsRepository : ISettingsRepository @@ -24,10 +24,10 @@ public DynamoDbSettingsRepository() { RegionEndpoint = RegionEndpoint.GetBySystemName("ap-southeast-2"), UseHttp = true, - ServiceURL = "http://localhost:4566" + ServiceURL = "http://localhost:8000" }; - _amazonDynamoDBClient = new AmazonDynamoDBClient("123", "123", clientConfig); + _amazonDynamoDBClient = new AmazonDynamoDBClient("test", "test", clientConfig); } public async Task GetSettingsAsync(Guid tenantId) diff --git a/MicroserviceTemplate/src/tye.yaml b/MicroserviceTemplate/src/tye.yaml index 4840147..06be116 100644 --- a/MicroserviceTemplate/src/tye.yaml +++ b/MicroserviceTemplate/src/tye.yaml @@ -44,14 +44,13 @@ services: connectionString: "${host}:${port}" args: "redis-cli -h redis MONITOR" -#https://github.com/dotnet/tye/issues/190 -#https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html - - name: dynamodb-local + # https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html (Docker Tab) + - name: dynamodb-database image: "amazon/dynamodb-local:latest" - args: -jar DynamoDBLocal.jar -sharedDb -dbPath ./data + args: -jar DynamoDBLocal.jar -sharedDb -dbPath /mnt/c/Temp/DynamoDB volumes: - - source: "/mnt/c/Temp" - target: "/mnt/c/Temp/DynamoDB" + - source: "./" + target: "/mnt/c/Temp/DynamoDB" bindings: - port: 8000 env: @@ -59,21 +58,5 @@ services: value: test - name: AWS_SECRET_ACCESS_KEY value: test - - name: MYSQL_USER - value: steeltoe - name: REGION - value: ap-southeast-2 - #volumes: - # - source: ./docker/dynamodb:/home/dynamodblocal/data - # target: /home/dynamodblocal - # bindings: - # - port: 8000 - # env: - # - name: AWS_ACCESS_KEY_ID - # value: test - # - name: AWS_SECRET_ACCESS_KEY - # value: test - # - name: MYSQL_USER - # value: steeltoe - # - name: REGION - # value: ap-southeast-2 \ No newline at end of file + value: ap-southeast-2 \ No newline at end of file From 015fa499505d22ec41e1dc08417d785a65a529a0 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Tue, 7 Jun 2022 16:30:11 +1000 Subject: [PATCH 04/56] [MicroserviceTemplate] Tye file changes Added test project CloudWatch logging and InMemory caching implementation --- .../Extensions/EndpointExtensions.cs | 17 ++++ .../Extensions/SwaggerExtensions.cs | 49 +++++++++++ .../src/ServiceName.API/Program.cs | 48 ++-------- .../appsettings.Development.json | 2 +- .../src/ServiceName.API/aws-logger-errors.txt | 0 .../CQRS/Commands/SaveSettingsCommand.cs | 16 ++-- .../CQRS/Queries/GetSettingsQuery.cs | 15 +++- .../Common/Behaviours/LoggingBehaviour.cs | 12 ++- .../Interfaces/IAuthenticationService.cs | 2 +- .../Interfaces/IConfigurationService.cs | 2 +- .../Common/Interfaces/INotificationService.cs | 14 +++ .../ServiceName.Core/Model/ModuleOptions.cs | 7 ++ ...ice.cs => MockJwtAuthenticationService.cs} | 8 +- .../AwsParameterStoreConfigurationService.cs | 2 +- ...EnvironmentVariableConfigurationService.cs | 2 +- .../Configuration/FileConfigurationService.cs | 13 ++- .../JsonStringConfigurationService.cs | 9 +- .../ConfigureServices.cs | 39 +++++---- .../Logging/CloudWatchLoggingService.cs | 34 +++----- .../Logging/LocalCloudWatchLoggingService.cs | 47 ++++++++++ .../DynamoDbSettingsRepository.cs | 9 +- .../LocalDynamoDbSettingsRepository.cs | 87 +++++++++++++++++++ .../Repositories/MockSettingsRepository.cs | 3 +- .../ServiceName.Infrastructure.csproj | 2 + MicroserviceTemplate/src/ServiceTemplate.sln | 17 +++- MicroserviceTemplate/src/nginx.conf | 25 ------ MicroserviceTemplate/src/tye.yaml | 31 ++++--- .../ServiceName.Test/AuthenticationTests.cs | 21 +++++ .../ServiceName.Test/ServiceName.Test.csproj | 28 ++++++ .../tests/ServiceName.Test/Usings.cs | 1 + 30 files changed, 400 insertions(+), 162 deletions(-) create mode 100644 MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs create mode 100644 MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs create mode 100644 MicroserviceTemplate/src/ServiceName.API/aws-logger-errors.txt create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/INotificationService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Model/ModuleOptions.cs rename MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/{MockAuthenticationService.cs => MockJwtAuthenticationService.cs} (89%) create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/LocalCloudWatchLoggingService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDbSettingsRepository.cs delete mode 100644 MicroserviceTemplate/src/nginx.conf create mode 100644 MicroserviceTemplate/tests/ServiceName.Test/AuthenticationTests.cs create mode 100644 MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj create mode 100644 MicroserviceTemplate/tests/ServiceName.Test/Usings.cs diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs new file mode 100644 index 0000000..273a101 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs @@ -0,0 +1,17 @@ +using MediatR; +using Microsoft.AspNetCore.Mvc; +using ServiceName.Core.CQRS.Commands; +using ServiceName.Core.CQRS.Queries; +using ServiceName.Core.Model; + +namespace ServiceName.API.Extensions +{ + public static class EndpointExtensions + { + public static void MapEndpoints(this WebApplication app) + { + app.MapGet("/settings", (IMediator mediator) => mediator.Send(new GetSettingsQueryRequest() { TenantId = Guid.Parse("2e861859-1be6-4c0d-bfce-1e9d9d31a1c9") })); + app.MapPost("/settings", (IMediator mediator, [FromBody] Settings settings) => mediator.Send(new SaveSettingsCommandRequest() { TenantId = Guid.Parse("2e861859-1be6-4c0d-bfce-1e9d9d31a1c9"), Settings = settings })); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs new file mode 100644 index 0000000..96c6df0 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs @@ -0,0 +1,49 @@ +using Microsoft.OpenApi.Models; + +namespace ServiceName.API.Extensions +{ + public static class SwaggerExtensions + { + public static void ConfigureSwaggerServices(this IServiceCollection services) + { + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "Microservice API", Version = "v1" }); + + c.AddSecurityDefinition("basic", new OpenApiSecurityScheme + { + Name = "Authorization", + Type = SecuritySchemeType.Http, + Scheme = "basic", + In = ParameterLocation.Header, + Description = "Basic Authorization header using the Bearer scheme." + }); + + c.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "basic" + } + }, + new string[] {} + } + }); + }); + } + + public static void ConfigureSwaggerUI(this WebApplication app) + { + app.UseSwagger(); + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("./swagger/v1/swagger.json", "v1"); + options.RoutePrefix = string.Empty; + }); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.API/Program.cs b/MicroserviceTemplate/src/ServiceName.API/Program.cs index cec0899..052995a 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Program.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Program.cs @@ -1,65 +1,32 @@ -using MediatR; -using Microsoft.AspNetCore.Mvc; -using Microsoft.OpenApi.Models; +using ServiceName.API.Extensions; using ServiceName.Core; -using ServiceName.Core.CQRS.Commands; -using ServiceName.Core.CQRS.Queries; -using ServiceName.Core.Model; using ServiceName.Infrastructure; var builder = WebApplication.CreateBuilder(args); +builder.Logging.ClearProviders(); // Add services to the container. builder.Services.AddControllers(); // Add AWS Lambda support. When application is run in Lambda Kestrel is swapped out as the web server with Amazon.Lambda.AspNetCoreServer. This // package will act as the webserver translating request and responses between the Lambda event source and ASP.NET Core. +// https://aws.amazon.com/blogs/compute/introducing-the-net-6-runtime-for-aws-lambda/ builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi); -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); //Configure Swagger with Bearer Token authentication -builder.Services.AddSwaggerGen(c => -{ - c.SwaggerDoc("v1", new OpenApiInfo { Title = "Microservice API", Version = "v1" }); - - c.AddSecurityDefinition("basic", new OpenApiSecurityScheme - { - Name = "Authorization", - Type = SecuritySchemeType.Http, - Scheme = "basic", - In = ParameterLocation.Header, - Description = "Basic Authorization header using the Bearer scheme." - }); - - c.AddSecurityRequirement(new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = "basic" - } - }, - new string[] {} - } - }); -}); +builder.Services.ConfigureSwaggerServices(); //Clean Architecute: Service Injection builder.Services.AddApplicationServices(); -builder.Services.AddInfrastructureServices(); +builder.Services.AddInfrastructureServices(options => options.IsDevelopment = true); var app = builder.Build(); -// Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { - app.UseSwagger(); - app.UseSwaggerUI(); + app.ConfigureSwaggerUI(); } app.UseAuthentication(); @@ -67,8 +34,7 @@ app.UseHttpsRedirection(); app.MapControllers(); +app.MapEndpoints(); -app.MapGet("/settings", (IMediator mediator) => mediator.Send(new GetSettingsQueryRequest() { TenantId = Guid.Parse("2e861859-1be6-4c0d-bfce-1e9d9d31a1c9") })); -app.MapPost("/settings", (IMediator mediator, [FromBody] Settings settings) => mediator.Send(new SaveSettingsCommandRequest() { TenantId = Guid.Parse("2e861859-1be6-4c0d-bfce-1e9d9d31a1c9"), Settings=settings })); app.Run(); diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json index 1b2d3ba..a783479 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json @@ -1,7 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Information", + "Default": "Debug", "Microsoft.AspNetCore": "Warning" } } diff --git a/MicroserviceTemplate/src/ServiceName.API/aws-logger-errors.txt b/MicroserviceTemplate/src/ServiceName.API/aws-logger-errors.txt new file mode 100644 index 0000000..e69de29 diff --git a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs index 62703ce..70e9e88 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using MediatR; +using MediatR; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; @@ -17,16 +12,21 @@ public record SaveSettingsCommandRequest : IRequest public class CreateTodoListCommandHandler : IRequestHandler { - private readonly ISettingsRepository _settingsRepository; + ISettingsRepository _settingsRepository; + IConfigurationService _configurationService; + ICachingService _cachingService; - public CreateTodoListCommandHandler(ISettingsRepository settingsRepository) + public CreateTodoListCommandHandler(ISettingsRepository settingsRepository, IConfigurationService configurationService, ICachingService cachingService) { _settingsRepository = settingsRepository; + _configurationService = configurationService; + _cachingService = cachingService; } public async Task Handle(SaveSettingsCommandRequest request, CancellationToken cancellationToken) { var result = await _settingsRepository.SaveSettingsAsync(request.TenantId, request.Settings); + _cachingService.Set(request.TenantId.ToString(), request.Settings); return result; } } diff --git a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs index 3cfd6b9..dd6b0ad 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs @@ -13,17 +13,26 @@ public class GetSettingsQueryHandler : IRequestHandler Handle(GetSettingsQueryRequest request, CancellationToken cancellationToken) { - var settings = await _settingsRepository.GetSettingsAsync(request.TenantId); - return settings; + var cachedSettings = _cachingService.Get(request.TenantId.ToString()); + if (cachedSettings == null) + { + var settings = await _settingsRepository.GetSettingsAsync(request.TenantId); + _cachingService.Set(request.TenantId.ToString(), settings); + cachedSettings = settings; + } + + return cachedSettings; } } } diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs index f59a1e5..693f09b 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs @@ -18,27 +18,25 @@ public LoggingBehaviour(ILoggingService loggingService) public async Task Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) { - var requestId = Guid.NewGuid(); + var correlationId = Guid.NewGuid(); var requestName = typeof(TRequest).Name; - await _loggingService.LogInformationAsync(@"requestId {requestId} + await _loggingService.LogInformationAsync(@"correlationId {correlationId} requestName {requestName} type {requestType} - payload {requestPayload}", requestId, requestName, "Request", JsonSerializer.Serialize(request)); + payload {requestPayload}", correlationId, requestName, "Request", JsonSerializer.Serialize(request)); _timer.Start(); var response = await next(); _timer.Stop(); - await _loggingService.LogInformationAsync(@"requestId {requestId} + await _loggingService.LogInformationAsync(@"correlationId {correlationId} requestName {requestName} type {requestType} - payload {requestPayload} {elapsedMilliseconds}", requestId, requestName, "Response", JsonSerializer.Serialize(response), _timer.ElapsedMilliseconds); + payload {requestPayload} {elapsedMilliseconds}", correlationId, requestName, "Response", JsonSerializer.Serialize(response), _timer.ElapsedMilliseconds); return response; } - - } diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IAuthenticationService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IAuthenticationService.cs index d395c3e..923e6c2 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IAuthenticationService.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IAuthenticationService.cs @@ -3,6 +3,6 @@ public interface IAuthenticationService { string GenerateToken(int userId); - bool ValidateCurrentToken(string token); + bool ValidateToken(string token); } } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IConfigurationService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IConfigurationService.cs index 1e1bf1e..4103da8 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IConfigurationService.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IConfigurationService.cs @@ -7,7 +7,7 @@ namespace ServiceName.Core.Common.Interfaces /// public interface IConfigurationService { - void LoadFromSource(params string[] configurationSources); + void LoadFromSource(); public IConfiguration Configuration { get; } } } diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/INotificationService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/INotificationService.cs new file mode 100644 index 0000000..af159e1 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/INotificationService.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ServiceName.Core.Common.Interfaces +{ + public interface INotificationService + { + void SendEmail(); + + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Core/Model/ModuleOptions.cs b/MicroserviceTemplate/src/ServiceName.Core/Model/ModuleOptions.cs new file mode 100644 index 0000000..f34cbb8 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Model/ModuleOptions.cs @@ -0,0 +1,7 @@ +namespace ServiceName.Core.Model +{ + public class ModuleOptions + { + public bool IsDevelopment { get; set; } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/MockAuthenticationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/MockJwtAuthenticationService.cs similarity index 89% rename from MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/MockAuthenticationService.cs rename to MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/MockJwtAuthenticationService.cs index d289b8b..dbe4c06 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/MockAuthenticationService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/MockJwtAuthenticationService.cs @@ -9,11 +9,11 @@ namespace ServiceName.Infrastructure.Authentication /// /// https://dotnetcoretutorials.com/2020/01/15/creating-and-validating-jwt-tokens-in-asp-net-core/ /// - public class AuthenticationServiceMock : IAuthenticationService + public class MockJwtAuthenticationService : IAuthenticationService { public string GenerateToken(int userId) { - var mySecret = "asdv234234^&%&^%&^hjsdfb2%%%"; + var mySecret = "asdv234235^&%&^%&^hjsdfb2%%%"; var mySecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(mySecret)); var myIssuer = "http://mysite.com"; var myAudience = "http://myaudience.com"; @@ -22,7 +22,7 @@ public string GenerateToken(int userId) { Subject = new ClaimsIdentity(new Claim[] { - new Claim(ClaimTypes.NameIdentifier, userId.ToString()), + new Claim(ClaimTypes.NameIdentifier, userId.ToString()), }), Expires = DateTime.UtcNow.AddDays(7), Issuer = myIssuer, @@ -33,7 +33,7 @@ public string GenerateToken(int userId) return tokenHandler.WriteToken(token); } - public bool ValidateCurrentToken(string token) + public bool ValidateToken(string token) { var mySecret = "asdv234234^&%&^%&^hjsdfb2%%%"; var mySecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(mySecret)); diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/AwsParameterStoreConfigurationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/AwsParameterStoreConfigurationService.cs index fd1c4be..c358865 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/AwsParameterStoreConfigurationService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/AwsParameterStoreConfigurationService.cs @@ -11,7 +11,7 @@ public class AwsParameterStoreConfigurationService : IConfigurationService IConfiguration _configuration; public IConfiguration Configuration => _configuration; - public void LoadFromSource(params string[] configurationSources) + public void LoadFromSource() { throw new NotImplementedException(); } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/EnvironmentVariableConfigurationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/EnvironmentVariableConfigurationService.cs index 1c1a2de..733bf36 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/EnvironmentVariableConfigurationService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/EnvironmentVariableConfigurationService.cs @@ -8,7 +8,7 @@ internal class EnvironmentVariableConfigurationService : IConfigurationService IConfiguration _configuration; public IConfiguration Configuration => _configuration; - public void LoadFromSource(params string[] configurationSources) + public void LoadFromSource() { _configuration = new ConfigurationBuilder() .AddEnvironmentVariables() diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/FileConfigurationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/FileConfigurationService.cs index f538809..0c72a83 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/FileConfigurationService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/FileConfigurationService.cs @@ -1,20 +1,27 @@ using System.Reflection; -using System.Text; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Model; namespace ServiceName.Infrastructure.Configuration { public class FileConfigurationService : IConfigurationService { IConfiguration _configuration; + + IOptions _infrastructureOptions; + public IConfiguration Configuration => _configuration; - public void LoadFromSource(params string[] configurationSources) + public void LoadFromSource() { + + var configFileName = _infrastructureOptions.Value.IsDevelopment ? "appsettings.Development.json" : "appsettings.json"; + _configuration = new ConfigurationBuilder() .SetBasePath(Assembly.GetExecutingAssembly().Location) - .AddJsonFile(configurationSources[0], optional: false, reloadOnChange: true) + .AddJsonFile(configFileName, optional: false, reloadOnChange: true) .Build(); } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/JsonStringConfigurationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/JsonStringConfigurationService.cs index 2d6a03b..5129453 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/JsonStringConfigurationService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/JsonStringConfigurationService.cs @@ -9,11 +9,12 @@ public class JsonStringConfigurationService : IConfigurationService IConfiguration _configuration; public IConfiguration Configuration => _configuration; - public void LoadFromSource(params string[] configurationSources) + public void LoadFromSource() { - _configuration = new ConfigurationBuilder() - .AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(configurationSources[0]))) - .Build(); //https://github.com/dotnet/runtime/issues/36018 + throw new NotImplementedException(); + //_configuration = new ConfigurationBuilder() + // .AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(jsonString))) + // .Build(); //https://github.com/dotnet/runtime/issues/36018 } } } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs index 0a04140..ccef9c2 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs @@ -2,8 +2,11 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Model; using ServiceName.Infrastructure.Authentication; +using ServiceName.Infrastructure.Caching; using ServiceName.Infrastructure.Configuration; using ServiceName.Infrastructure.Logging; using ServiceName.Infrastructure.Repositories; @@ -12,24 +15,26 @@ namespace ServiceName.Infrastructure { public static class ConfigureServices { - public static IServiceCollection AddInfrastructureServices(this IServiceCollection services) + public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, Action? infrastructureOptions = null) { - - //For Development Environment - //services.AddScoped(); - services.AddScoped(); - services.AddSingleton(); - services.AddSingleton(); - //services.AddSingleton(); - - //For Cloud Environment - //services.AddScoped(); - //services.AddSingleton(); - - // Authentication - //services.AddAuthentication("BasicAuthentication") - // .AddScheme("BasicAuthentication", null); - //services.AddAuthorization(); + var options = new ModuleOptions(); + services.Configure(infrastructureOptions); + infrastructureOptions?.Invoke(options); + + if (options.IsDevelopment) + { + services.AddScoped(); + services.AddSingleton(); + //services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + } + else + { + services.AddScoped(); + services.AddSingleton(); + services.AddSingleton(); + } return services; } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/CloudWatchLoggingService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/CloudWatchLoggingService.cs index 575f9fc..ad24d9c 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/CloudWatchLoggingService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/CloudWatchLoggingService.cs @@ -1,36 +1,22 @@ -using Amazon.CloudWatchLogs; +using AWS.Logger; +using AWS.Logger.SeriLog; using Serilog; -using Serilog.Sinks.AwsCloudWatch; using ServiceName.Core.Common.Interfaces; namespace ServiceName.Infrastructure.Logging { internal class CloudWatchLoggingService : ILoggingService { - + /// + /// https://docs.aws.amazon.com/lambda/latest/dg/csharp-logging.html (LambdaLogger) + /// https://github.com/aws/aws-logging-dotnet + /// https://github.com/aws/aws-logging-dotnet/tree/master/samples/Serilog + /// public CloudWatchLoggingService() { - //// remove default logging providers - //builder.Logging.ClearProviders(); - - var awsConfig = new AmazonCloudWatchLogsConfig() { ServiceURL = "http://host.docker.internal:4566" }; - awsConfig.UseHttp = true; - awsConfig.RegionEndpoint = Amazon.RegionEndpoint.APSoutheast1; - var client = new AmazonCloudWatchLogsClient("aws", "aws", awsConfig); - - // Serilog configuration - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.AmazonCloudWatch( - logGroup: "/logs", - logStreamPrefix: DateTime.UtcNow.ToString("yyyyMMddHHmmssfff"), - cloudWatchClient: client - ).CreateLogger(); - - //Serilog.Debugging.SelfLog.Enable(Console.Error); - - //// Register Serilog - //builder.Logging.AddSerilog(logger); + Log.Logger = new LoggerConfiguration().WriteTo.AWSSeriLog() + .WriteTo.Console() + .CreateLogger(); } public async Task LogErrorAsync(string message, params object[] propertyValues) diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/LocalCloudWatchLoggingService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/LocalCloudWatchLoggingService.cs new file mode 100644 index 0000000..689e513 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/LocalCloudWatchLoggingService.cs @@ -0,0 +1,47 @@ +using AWS.Logger; +using AWS.Logger.SeriLog; +using Serilog; +using ServiceName.Core.Common.Interfaces; + +namespace ServiceName.Infrastructure.Logging +{ + internal class LocalCloudWatchLoggingService : ILoggingService + { + /// + /// https://awscli.amazonaws.com/v2/documentation/api/latest/reference/logs/tail.html + /// https://docs.aws.amazon.com/cli/latest/reference/logs/create-log-group.html + /// awslocal logs describe-log-groups + /// awslocal logs tail "/LocalStack/Microservice/Logs" + /// https://docs.aws.amazon.com/lambda/latest/dg/csharp-logging.html (LambdaLogger) + /// https://github.com/aws/aws-logging-dotnet + /// https://github.com/aws/aws-logging-dotnet/tree/master/samples/Serilog + /// + public LocalCloudWatchLoggingService() + { + AWSLoggerConfig configuration = new("/LocalStack/Microservice/Logs") + { + Region = "ap-southeast-2", + ServiceUrl = "http://localhost:4566" + }; + + Log.Logger = new LoggerConfiguration().WriteTo.AWSSeriLog(configuration) + .WriteTo.Console() + .CreateLogger(); + } + + public async Task LogErrorAsync(string message, params object[] propertyValues) + { + Log.Error(message, propertyValues); + } + + public async Task LogInformationAsync(string message, params object[] propertyValues) + { + Log.Information(message, propertyValues); + } + + public async Task LogWarningAsync(string message, params object[] propertyValues) + { + Log.Warning(message, propertyValues); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs index d5f1abd..64c25d9 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs @@ -20,14 +20,7 @@ public class DynamoDbSettingsRepository : ISettingsRepository public DynamoDbSettingsRepository() { - AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig - { - RegionEndpoint = RegionEndpoint.GetBySystemName("ap-southeast-2"), - UseHttp = true, - ServiceURL = "http://localhost:8000" - }; - - _amazonDynamoDBClient = new AmazonDynamoDBClient("test", "test", clientConfig); + _amazonDynamoDBClient = new AmazonDynamoDBClient("test", "test"); } public async Task GetSettingsAsync(Guid tenantId) diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDbSettingsRepository.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDbSettingsRepository.cs new file mode 100644 index 0000000..be2a48d --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDbSettingsRepository.cs @@ -0,0 +1,87 @@ +using System.Text.Json; +using Amazon; +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.Model; +using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Model; + +namespace ServiceName.Infrastructure.Repositories +{ + /// + //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.DotNet.html + //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html + //aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST + //awslocal dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST + /// + public class LocalDynamoDbSettingsRepository : ISettingsRepository + { + AmazonDynamoDBClient _amazonDynamoDBClient; + string _dynamoTableName = "ServiceName_Setting"; + + public LocalDynamoDbSettingsRepository() + { + AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig + { + RegionEndpoint = RegionEndpoint.GetBySystemName("ap-southeast-2"), + UseHttp = true, + ServiceURL = "http://localhost:8000" + }; + + _amazonDynamoDBClient = new AmazonDynamoDBClient("test", "test", clientConfig); + } + + public async Task GetSettingsAsync(Guid tenantId) + { + var getRequest = new GetItemRequest + { + TableName = _dynamoTableName, + Key = new Dictionary() { { "TenantId", new AttributeValue { S = tenantId.ToString().ToLower() } } }, + }; + + var response = await _amazonDynamoDBClient.GetItemAsync(getRequest); + + //If there is no settings configured for the tenant create a new one + if (response.Item.Count.Equals(0)) + { + var newSettings = new Settings() + { + Group1 = new SettingGroup() + { + Setting1 = "a", + Setting2 = "b" + }, + }; + + await SaveSettings(tenantId, newSettings); + + return newSettings; + } + + var existingSettings = JsonSerializer.Deserialize(response.Item["Settings"].S, new JsonSerializerOptions() { MaxDepth = 0, PropertyNameCaseInsensitive = true }); + + return existingSettings; + } + + public async Task SaveSettingsAsync(Guid tenantId, Settings settings) + { + await SaveSettings(tenantId, settings); + + return true; + } + + private async Task SaveSettings(Guid tenantId, Settings settings) + { + var putRequest = new PutItemRequest + { + TableName = _dynamoTableName, + Item = new Dictionary() + { + { "TenantId", new AttributeValue { S = tenantId.ToString() } }, + { "Settings", new AttributeValue { S = JsonSerializer.Serialize(settings).ToLower() } } + } + }; + + var response = await _amazonDynamoDBClient.PutItemAsync(putRequest); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/MockSettingsRepository.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/MockSettingsRepository.cs index 13b76cc..95670db 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/MockSettingsRepository.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/MockSettingsRepository.cs @@ -1,5 +1,4 @@ -using System.Text.Json; -using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; namespace ServiceName.Infrastructure.Repositories diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj b/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj index 22d2823..a562af4 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj @@ -7,6 +7,8 @@ + + diff --git a/MicroserviceTemplate/src/ServiceTemplate.sln b/MicroserviceTemplate/src/ServiceTemplate.sln index 1c05b97..bb62a15 100644 --- a/MicroserviceTemplate/src/ServiceTemplate.sln +++ b/MicroserviceTemplate/src/ServiceTemplate.sln @@ -9,12 +9,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.Infrastructure" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{99A702BB-D8C1-4F89-9B4E-EB891693FD7A}" ProjectSection(SolutionItems) = preProject - nginx.conf = nginx.conf tye.yaml = tye.yaml EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.Core", "ServiceName.Core\ServiceName.Core.csproj", "{3733AF37-1B4D-4178-B4A8-46702493BCEC}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0750781A-30FA-4A91-B764-A5E89BD9CCB0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3FD41DDF-2B43-4372-9FB1-62597803431B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceName.Test", "..\tests\ServiceName.Test\ServiceName.Test.csproj", "{5A8EB360-D518-4F2C-929C-3628C6DA9226}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,10 +38,20 @@ Global {3733AF37-1B4D-4178-B4A8-46702493BCEC}.Debug|Any CPU.Build.0 = Debug|Any CPU {3733AF37-1B4D-4178-B4A8-46702493BCEC}.Release|Any CPU.ActiveCfg = Release|Any CPU {3733AF37-1B4D-4178-B4A8-46702493BCEC}.Release|Any CPU.Build.0 = Release|Any CPU + {5A8EB360-D518-4F2C-929C-3628C6DA9226}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A8EB360-D518-4F2C-929C-3628C6DA9226}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A8EB360-D518-4F2C-929C-3628C6DA9226}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A8EB360-D518-4F2C-929C-3628C6DA9226}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {DD4C16FE-1C12-4B91-92D1-184329282E65} = {3FD41DDF-2B43-4372-9FB1-62597803431B} + {9B43FACF-50A2-4332-9642-3F723488DE40} = {3FD41DDF-2B43-4372-9FB1-62597803431B} + {3733AF37-1B4D-4178-B4A8-46702493BCEC} = {3FD41DDF-2B43-4372-9FB1-62597803431B} + {5A8EB360-D518-4F2C-929C-3628C6DA9226} = {0750781A-30FA-4A91-B764-A5E89BD9CCB0} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C9A3A709-C3C6-4654-AC2B-A92420A60DF5} EndGlobalSection diff --git a/MicroserviceTemplate/src/nginx.conf b/MicroserviceTemplate/src/nginx.conf deleted file mode 100644 index 26a9b52..0000000 --- a/MicroserviceTemplate/src/nginx.conf +++ /dev/null @@ -1,25 +0,0 @@ -server { - listen 80; - - location /A { - proxy_pass http://localhost:51001/; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection keep-alive; - proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - location /B { - proxy_pass http://appB/; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection keep-alive; - proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/tye.yaml b/MicroserviceTemplate/src/tye.yaml index 06be116..2a1d9aa 100644 --- a/MicroserviceTemplate/src/tye.yaml +++ b/MicroserviceTemplate/src/tye.yaml @@ -10,16 +10,16 @@ extensions: logPath: ./.logs name: servicetemplate -services: - - - name: nginx-ingress-controller - image: nginx +ingress: + - name: ingress bindings: - - protocol: http - port: 51000 - volumes: - - source: nginx.conf - target: /etc/nginx/conf.d/default.conf + - port: 51000 + rules: + - path: /A + service: servicename-api + - path: /B + service: servicename-api +services: - name: servicename-api project: ServiceName.API/ServiceName.API.csproj bindings: @@ -45,6 +45,7 @@ services: args: "redis-cli -h redis MONITOR" # https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html (Docker Tab) + #aws --endpoint-url=http://localhost:52001 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST - name: dynamodb-database image: "amazon/dynamodb-local:latest" args: -jar DynamoDBLocal.jar -sharedDb -dbPath /mnt/c/Temp/DynamoDB @@ -59,4 +60,14 @@ services: - name: AWS_SECRET_ACCESS_KEY value: test - name: REGION - value: ap-southeast-2 \ No newline at end of file + value: ap-southeast-2 + + #aws --endpoint-url=http://localhost:52002 kms --region ap-southeast-2 create-key --key-spec RSA_4096 --key-usage ENCRYPT_DECRYPT + #aws --endpoint-url=http://localhost:52002 kms --region ap-southeast-2 list-keys + - name: kms-local + image: nsmithuk/local-kms + volumes: + - source: "./" + target: "/mnt/c/Temp/Kms" + bindings: + - port: 52002 \ No newline at end of file diff --git a/MicroserviceTemplate/tests/ServiceName.Test/AuthenticationTests.cs b/MicroserviceTemplate/tests/ServiceName.Test/AuthenticationTests.cs new file mode 100644 index 0000000..4ba6e11 --- /dev/null +++ b/MicroserviceTemplate/tests/ServiceName.Test/AuthenticationTests.cs @@ -0,0 +1,21 @@ +using ServiceName.Infrastructure.Authentication; + +namespace ServiceName.Test +{ + public class AuthenticationTests + { + [Fact] + public void GenerateAndValidateJwtToken() + { + MockJwtAuthenticationService authService = new MockJwtAuthenticationService(); + var token = authService.GenerateToken(1); + var isValid = authService.ValidateToken(token); + } + + [Fact] + public void ValidateJWT() + { + + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj b/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj new file mode 100644 index 0000000..708a520 --- /dev/null +++ b/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/MicroserviceTemplate/tests/ServiceName.Test/Usings.cs b/MicroserviceTemplate/tests/ServiceName.Test/Usings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/MicroserviceTemplate/tests/ServiceName.Test/Usings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file From b4766bffbcec71b7d2d93710f258ae023914a476 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Wed, 8 Jun 2022 08:31:45 +1000 Subject: [PATCH 05/56] [MicroserviceTemplate] Made repository interface more generic IRepositoryService --- .../CQRS/Commands/SaveSettingsCommand.cs | 6 ++--- .../CQRS/Queries/GetSettingsQuery.cs | 6 ++--- .../Common/Interfaces/INotificationService.cs | 10 ++----- .../Common/Interfaces/IRepositoryService.cs | 8 ++++++ .../Common/Interfaces/ISettingsRepository.cs | 10 ------- .../Configuration/FileConfigurationService.cs | 5 ++-- .../ConfigureServices.cs | 10 +++---- .../DynamoDbSettingsRepository.cs | 27 ++++++++++--------- .../LocalDynamoDbSettingsRepository.cs | 25 ++++++++--------- .../Repositories/MockSettingsRepository.cs | 23 ---------------- 10 files changed, 49 insertions(+), 81 deletions(-) create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IRepositoryService.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ISettingsRepository.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/MockSettingsRepository.cs diff --git a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs index 70e9e88..aa97d6f 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs @@ -12,11 +12,11 @@ public record SaveSettingsCommandRequest : IRequest public class CreateTodoListCommandHandler : IRequestHandler { - ISettingsRepository _settingsRepository; + IRepositoryService _settingsRepository; IConfigurationService _configurationService; ICachingService _cachingService; - public CreateTodoListCommandHandler(ISettingsRepository settingsRepository, IConfigurationService configurationService, ICachingService cachingService) + public CreateTodoListCommandHandler(IRepositoryService settingsRepository, IConfigurationService configurationService, ICachingService cachingService) { _settingsRepository = settingsRepository; _configurationService = configurationService; @@ -25,7 +25,7 @@ public CreateTodoListCommandHandler(ISettingsRepository settingsRepository, ICon public async Task Handle(SaveSettingsCommandRequest request, CancellationToken cancellationToken) { - var result = await _settingsRepository.SaveSettingsAsync(request.TenantId, request.Settings); + var result = await _settingsRepository.SaveAsync(request.TenantId, request.Settings); _cachingService.Set(request.TenantId.ToString(), request.Settings); return result; } diff --git a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs index dd6b0ad..404d8e8 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs @@ -11,11 +11,11 @@ public record GetSettingsQueryRequest : IRequest } public class GetSettingsQueryHandler : IRequestHandler { - ISettingsRepository _settingsRepository; + IRepositoryService _settingsRepository; IConfigurationService _configurationService; ICachingService _cachingService; - public GetSettingsQueryHandler(ISettingsRepository settingsRepository, IConfigurationService configurationService, ICachingService cachingService) + public GetSettingsQueryHandler(IRepositoryService settingsRepository, IConfigurationService configurationService, ICachingService cachingService) { _settingsRepository = settingsRepository; _configurationService = configurationService; @@ -27,7 +27,7 @@ public async Task Handle(GetSettingsQueryRequest request, Cancellation var cachedSettings = _cachingService.Get(request.TenantId.ToString()); if (cachedSettings == null) { - var settings = await _settingsRepository.GetSettingsAsync(request.TenantId); + var settings = await _settingsRepository.GetAsync(request.TenantId); _cachingService.Set(request.TenantId.ToString(), settings); cachedSettings = settings; } diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/INotificationService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/INotificationService.cs index af159e1..e75bc5b 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/INotificationService.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/INotificationService.cs @@ -1,14 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ServiceName.Core.Common.Interfaces +namespace ServiceName.Core.Common.Interfaces { public interface INotificationService { void SendEmail(); - + void SendSMS(); } } diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IRepositoryService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IRepositoryService.cs new file mode 100644 index 0000000..9403310 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IRepositoryService.cs @@ -0,0 +1,8 @@ +namespace ServiceName.Core.Common.Interfaces +{ + public interface IRepositoryService + { + Task GetAsync(Guid id); + Task SaveAsync(Guid id, T obj); + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ISettingsRepository.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ISettingsRepository.cs deleted file mode 100644 index ae47f8f..0000000 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ISettingsRepository.cs +++ /dev/null @@ -1,10 +0,0 @@ -using ServiceName.Core.Model; - -namespace ServiceName.Core.Common.Interfaces -{ - public interface ISettingsRepository - { - Task GetSettingsAsync(Guid tenantId); - Task SaveSettingsAsync(Guid tenantId, Settings settings); - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/FileConfigurationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/FileConfigurationService.cs index 0c72a83..7aafaec 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/FileConfigurationService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/FileConfigurationService.cs @@ -17,11 +17,12 @@ public class FileConfigurationService : IConfigurationService public void LoadFromSource() { - var configFileName = _infrastructureOptions.Value.IsDevelopment ? "appsettings.Development.json" : "appsettings.json"; + var environmentName = _infrastructureOptions.Value.IsDevelopment ? "Development.json" : "appsettings.json"; _configuration = new ConfigurationBuilder() .SetBasePath(Assembly.GetExecutingAssembly().Location) - .AddJsonFile(configFileName, optional: false, reloadOnChange: true) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true) .Build(); } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs index ccef9c2..6cc6c23 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs @@ -1,11 +1,7 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; -using ServiceName.Infrastructure.Authentication; using ServiceName.Infrastructure.Caching; using ServiceName.Infrastructure.Configuration; using ServiceName.Infrastructure.Logging; @@ -23,7 +19,7 @@ public static IServiceCollection AddInfrastructureServices(this IServiceCollecti if (options.IsDevelopment) { - services.AddScoped(); + services.AddScoped, LocalDynamoDbSettingsRepository>(); services.AddSingleton(); //services.AddSingleton(); services.AddSingleton(); @@ -31,7 +27,7 @@ public static IServiceCollection AddInfrastructureServices(this IServiceCollecti } else { - services.AddScoped(); + services.AddScoped, DynamoDbSettingsRepository>(); services.AddSingleton(); services.AddSingleton(); } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs index 64c25d9..3226dd8 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs @@ -1,5 +1,5 @@ -using System.Text.Json; -using Amazon; +using System.Net; +using System.Text.Json; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; using ServiceName.Core.Common.Interfaces; @@ -13,7 +13,7 @@ namespace ServiceName.Infrastructure.Repositories //aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST //awslocal dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST /// - public class DynamoDbSettingsRepository : ISettingsRepository + public class DynamoDbSettingsRepository : IRepositoryService { AmazonDynamoDBClient _amazonDynamoDBClient; string _dynamoTableName = "ServiceName_Setting"; @@ -23,7 +23,7 @@ public DynamoDbSettingsRepository() _amazonDynamoDBClient = new AmazonDynamoDBClient("test", "test"); } - public async Task GetSettingsAsync(Guid tenantId) + public async Task GetAsync(Guid tenantId) { var getRequest = new GetItemRequest { @@ -45,7 +45,7 @@ public async Task GetSettingsAsync(Guid tenantId) }, }; - await SaveSettings(tenantId, newSettings); + await SaveAsync(tenantId, newSettings); return newSettings; } @@ -55,14 +55,7 @@ public async Task GetSettingsAsync(Guid tenantId) return existingSettings; } - public async Task SaveSettingsAsync(Guid tenantId, Settings settings) - { - await SaveSettings(tenantId, settings); - - return true; - } - - private async Task SaveSettings(Guid tenantId, Settings settings) + public async Task SaveAsync(Guid tenantId, Settings settings) { var putRequest = new PutItemRequest { @@ -75,6 +68,14 @@ private async Task SaveSettings(Guid tenantId, Settings settings) }; var response = await _amazonDynamoDBClient.PutItemAsync(putRequest); + + if (!response.HttpStatusCode.Equals(HttpStatusCode.OK)) + { + return false; + } + + return true; } + } } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDbSettingsRepository.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDbSettingsRepository.cs index be2a48d..9aa41c0 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDbSettingsRepository.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDbSettingsRepository.cs @@ -1,4 +1,5 @@ -using System.Text.Json; +using System.Net; +using System.Text.Json; using Amazon; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; @@ -13,7 +14,7 @@ namespace ServiceName.Infrastructure.Repositories //aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST //awslocal dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST /// - public class LocalDynamoDbSettingsRepository : ISettingsRepository + public class LocalDynamoDbSettingsRepository : IRepositoryService { AmazonDynamoDBClient _amazonDynamoDBClient; string _dynamoTableName = "ServiceName_Setting"; @@ -30,7 +31,7 @@ public LocalDynamoDbSettingsRepository() _amazonDynamoDBClient = new AmazonDynamoDBClient("test", "test", clientConfig); } - public async Task GetSettingsAsync(Guid tenantId) + public async Task GetAsync(Guid tenantId) { var getRequest = new GetItemRequest { @@ -52,7 +53,7 @@ public async Task GetSettingsAsync(Guid tenantId) }, }; - await SaveSettings(tenantId, newSettings); + await SaveAsync(tenantId, newSettings); return newSettings; } @@ -62,14 +63,7 @@ public async Task GetSettingsAsync(Guid tenantId) return existingSettings; } - public async Task SaveSettingsAsync(Guid tenantId, Settings settings) - { - await SaveSettings(tenantId, settings); - - return true; - } - - private async Task SaveSettings(Guid tenantId, Settings settings) + public async Task SaveAsync(Guid tenantId, Settings settings) { var putRequest = new PutItemRequest { @@ -82,6 +76,13 @@ private async Task SaveSettings(Guid tenantId, Settings settings) }; var response = await _amazonDynamoDBClient.PutItemAsync(putRequest); + + if (!response.HttpStatusCode.Equals(HttpStatusCode.OK)) + { + return false; + } + + return true; } } } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/MockSettingsRepository.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/MockSettingsRepository.cs deleted file mode 100644 index 95670db..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/MockSettingsRepository.cs +++ /dev/null @@ -1,23 +0,0 @@ -using ServiceName.Core.Common.Interfaces; -using ServiceName.Core.Model; - -namespace ServiceName.Infrastructure.Repositories -{ - public class MockSettingsRepository : ISettingsRepository - { - public async Task GetSettingsAsync(Guid tenantId) - { - var settings = new Settings() { - Group1 = new SettingGroup() { - Setting1 = "a", - Setting2 = "b" } - }; - return await Task.FromResult(settings); - } - - public async Task SaveSettingsAsync(Guid tenantId, Settings settings) - { - return await Task.FromResult(true); - } - } -} \ No newline at end of file From 2b65f03d8a949e1e40b2358009e7a2ba75096a04 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Wed, 8 Jun 2022 14:21:01 +1000 Subject: [PATCH 06/56] [MicroserviceTemplate] Separated RepositoryService (application) from DatabaseService (infrastructure) --- .../CQRS/Queries/GetSettingsQuery.cs | 5 +- .../Common/Interfaces/IDatabaseService.cs | 10 +++ .../Common/Interfaces/IRepositoryService.cs | 6 +- .../src/ServiceName.Core/ConfigureServices.cs | 4 + .../src/ServiceName.Core/Model/Query.cs | 8 ++ .../src/ServiceName.Core/Model/QueryField.cs | 8 ++ .../src/ServiceName.Core/Model/QueryRead.cs | 8 ++ .../src/ServiceName.Core/Model/QueryWrite.cs | 7 ++ .../Repository/SettingsRepository.cs | 80 ++++++++++++++++++ .../ConfigureServices.cs | 6 +- .../Databases/DynamoDatabaseService.cs | 75 +++++++++++++++++ .../LocalDynamoDatabaseService.cs} | 46 +++++------ .../DynamoDbSettingsRepository.cs | 81 ------------------- MicroserviceTemplate/src/tye.yaml | 28 +++---- 14 files changed, 245 insertions(+), 127 deletions(-) create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IDatabaseService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Model/Query.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Model/QueryField.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Model/QueryRead.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Model/QueryWrite.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Repository/SettingsRepository.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Databases/DynamoDatabaseService.cs rename MicroserviceTemplate/src/ServiceName.Infrastructure/{Repositories/LocalDynamoDbSettingsRepository.cs => Databases/LocalDynamoDatabaseService.cs} (60%) delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs diff --git a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs index 404d8e8..8c90896 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs @@ -1,5 +1,4 @@ -using System.Text.Json; -using MediatR; +using MediatR; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; @@ -27,7 +26,7 @@ public async Task Handle(GetSettingsQueryRequest request, Cancellation var cachedSettings = _cachingService.Get(request.TenantId.ToString()); if (cachedSettings == null) { - var settings = await _settingsRepository.GetAsync(request.TenantId); + var settings = await _settingsRepository.GetByIdAsync(request.TenantId); _cachingService.Set(request.TenantId.ToString(), settings); cachedSettings = settings; } diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IDatabaseService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IDatabaseService.cs new file mode 100644 index 0000000..29dd1fa --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IDatabaseService.cs @@ -0,0 +1,10 @@ +using ServiceName.Core.Model; + +namespace ServiceName.Core.Common.Interfaces +{ + public interface IDatabaseService + { + Task ExecuteQuery(QueryRead query); + Task ExecuteQuery(QueryWrite query); + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IRepositoryService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IRepositoryService.cs index 9403310..740cf90 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IRepositoryService.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IRepositoryService.cs @@ -1,8 +1,10 @@ namespace ServiceName.Core.Common.Interfaces { - public interface IRepositoryService + public interface IRepositoryService where T : class { - Task GetAsync(Guid id); + Task GetByIdAsync(Guid id); Task SaveAsync(Guid id, T obj); + Task> GetAllAsync(); + Task DeleteAsync(int id); } } diff --git a/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs index 02d58b3..bd6697a 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs @@ -2,6 +2,9 @@ using MediatR; using Microsoft.Extensions.DependencyInjection; using ServiceName.Core.Common.Behaviours; +using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Model; +using ServiceName.Core.Repository; namespace ServiceName.Core { @@ -11,6 +14,7 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection { services.AddMediatR(Assembly.GetExecutingAssembly()); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehaviour<,>)); + services.AddScoped, SettingsRepository>(); return services; } } diff --git a/MicroserviceTemplate/src/ServiceName.Core/Model/Query.cs b/MicroserviceTemplate/src/ServiceName.Core/Model/Query.cs new file mode 100644 index 0000000..7094fa0 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Model/Query.cs @@ -0,0 +1,8 @@ +namespace ServiceName.Core.Model +{ + public class Query + { + public List FilterFields { get; set; } + public string TableName { get; set; } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Model/QueryField.cs b/MicroserviceTemplate/src/ServiceName.Core/Model/QueryField.cs new file mode 100644 index 0000000..02fce58 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Model/QueryField.cs @@ -0,0 +1,8 @@ +namespace ServiceName.Core.Model +{ + public class QueryField + { + public string Name { get; set; } + public object Value { get; set; } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Model/QueryRead.cs b/MicroserviceTemplate/src/ServiceName.Core/Model/QueryRead.cs new file mode 100644 index 0000000..5fa9dd7 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Model/QueryRead.cs @@ -0,0 +1,8 @@ +namespace ServiceName.Core.Model +{ + public class QueryRead: Query + { + public object DefaultResult { get; set; } + public List SelectFields { get; set; } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Core/Model/QueryWrite.cs b/MicroserviceTemplate/src/ServiceName.Core/Model/QueryWrite.cs new file mode 100644 index 0000000..f852c7f --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Model/QueryWrite.cs @@ -0,0 +1,7 @@ +namespace ServiceName.Core.Model +{ + public class QueryWrite : Query + { + public List UpdateFields { get; set; } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Core/Repository/SettingsRepository.cs b/MicroserviceTemplate/src/ServiceName.Core/Repository/SettingsRepository.cs new file mode 100644 index 0000000..05031f1 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Repository/SettingsRepository.cs @@ -0,0 +1,80 @@ +using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Model; + +namespace ServiceName.Core.Repository +{ + /// + //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.DotNet.html + //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html + //aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST + //awslocal dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST + /// + public class SettingsRepository : IRepositoryService + { + IDatabaseService _databaseService; + + public SettingsRepository(IDatabaseService databaseService) + { + _databaseService = databaseService; + } + + public async Task GetByIdAsync(Guid tenantId) + { + QueryRead query = new QueryRead() { + + TableName = "ServiceName_Setting", + SelectFields = new List() { "Settings" }, + FilterFields = new List() { new QueryField(){ + Name = "TenantId", + Value = tenantId + } + } + }; + + query.DefaultResult = new Settings() + { + Group1 = new SettingGroup() + { + Setting1 = "a", + Setting2 = "b" + }, + }; + + + var settings = await _databaseService.ExecuteQuery(query); + return (Settings)settings; + } + + public async Task SaveAsync(Guid tenantId, Settings settings) + { + QueryWrite query = new QueryWrite() + { + TableName = "ServiceName_Setting", + FilterFields = new List() { new QueryField(){ + Name = "TenantId", + Value = tenantId + } + }, + UpdateFields = new List() { + new QueryField(){ + Name = "Settings", + Value = settings + } + } + }; + + var result = await _databaseService.ExecuteQuery(query); + return (bool)result; + } + + public Task DeleteAsync(int id) + { + throw new NotImplementedException(); + } + + public Task> GetAllAsync() + { + throw new NotImplementedException(); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs index 6cc6c23..a2b2edf 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs @@ -4,8 +4,8 @@ using ServiceName.Core.Model; using ServiceName.Infrastructure.Caching; using ServiceName.Infrastructure.Configuration; +using ServiceName.Infrastructure.Databases; using ServiceName.Infrastructure.Logging; -using ServiceName.Infrastructure.Repositories; namespace ServiceName.Infrastructure { @@ -19,7 +19,7 @@ public static IServiceCollection AddInfrastructureServices(this IServiceCollecti if (options.IsDevelopment) { - services.AddScoped, LocalDynamoDbSettingsRepository>(); + services.AddScoped(); services.AddSingleton(); //services.AddSingleton(); services.AddSingleton(); @@ -27,7 +27,7 @@ public static IServiceCollection AddInfrastructureServices(this IServiceCollecti } else { - services.AddScoped, DynamoDbSettingsRepository>(); + services.AddScoped(); services.AddSingleton(); services.AddSingleton(); } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Databases/DynamoDatabaseService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Databases/DynamoDatabaseService.cs new file mode 100644 index 0000000..c6878a2 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Databases/DynamoDatabaseService.cs @@ -0,0 +1,75 @@ +//using System.Net; +//using System.Text.Json; +//using Amazon.DynamoDBv2; +//using Amazon.DynamoDBv2.Model; +//using ServiceName.Core.Common.Interfaces; +//using ServiceName.Core.Model; + +//namespace ServiceName.Infrastructure.Databases +//{ +// /// +// //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.DotNet.html +// //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html +// //aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST +// //awslocal dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST +// /// +// public class DynamoDbDatabaseService : IDatabaseService +// { +// AmazonDynamoDBClient _amazonDynamoDBClient; +// //string _dynamoTableName = "ServiceName_Setting"; + +// public DynamoDbDatabaseService() +// { +// _amazonDynamoDBClient = new AmazonDynamoDBClient("test", "test"); +// } + +// public async Task ExecuteQuery(QueryRead queryRead) +// { +// var getRequest = new GetItemRequest +// { +// TableName = queryRead.TableName, +// Key = new Dictionary() { { queryRead.FilterFields[0].Name, new AttributeValue { S = queryRead.FilterFields[0].Value?.ToString().ToLower() } } }, +// }; + +// var response = await _amazonDynamoDBClient.GetItemAsync(getRequest); + +// //If there is no settings configured for the tenant create a new one +// if (response.Item.Count.Equals(0)) +// { +// //Save +// var queryWrite = new QueryWrite(); +// queryWrite.FilterFields = queryRead.FilterFields; +// queryWrite.UpdateFields = new List() { new QueryField() { Name = queryRead.SelectFields[0].ToString(), Value = queryRead.DefaultResult } }; +// await ExecuteQuery(queryWrite); +// return queryRead.DefaultResult; +// } + +// var existingSettings = JsonSerializer.Deserialize(response.Item[queryRead.SelectFields[0].ToString()].S, new JsonSerializerOptions() { MaxDepth = 0, PropertyNameCaseInsensitive = true }); + +// return existingSettings; +// } + +// public async Task ExecuteQuery(QueryWrite query) +// { +// var putRequest = new PutItemRequest +// { +// TableName = query.TableName, +// Item = new Dictionary() +// { +// { query.FilterFields[0].Name, new AttributeValue { S = query.FilterFields[0].Value.ToString() } }, +// { query.UpdateFields[0].Name, new AttributeValue { S = JsonSerializer.Serialize(query.UpdateFields[0].Value).ToLower() } } +// } +// }; + +// var response = await _amazonDynamoDBClient.PutItemAsync(putRequest); + +// if (!response.HttpStatusCode.Equals(HttpStatusCode.OK)) +// { +// return false; +// } + +// return true; +// } + +// } +//} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDbSettingsRepository.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Databases/LocalDynamoDatabaseService.cs similarity index 60% rename from MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDbSettingsRepository.cs rename to MicroserviceTemplate/src/ServiceName.Infrastructure/Databases/LocalDynamoDatabaseService.cs index 9aa41c0..0900bf6 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDbSettingsRepository.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Databases/LocalDynamoDatabaseService.cs @@ -6,7 +6,7 @@ using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; -namespace ServiceName.Infrastructure.Repositories +namespace ServiceName.Infrastructure.Databases { /// //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.DotNet.html @@ -14,12 +14,11 @@ namespace ServiceName.Infrastructure.Repositories //aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST //awslocal dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST /// - public class LocalDynamoDbSettingsRepository : IRepositoryService + public class LocalDynamoDatabaseService : IDatabaseService { AmazonDynamoDBClient _amazonDynamoDBClient; - string _dynamoTableName = "ServiceName_Setting"; - public LocalDynamoDbSettingsRepository() + public LocalDynamoDatabaseService() { AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig { @@ -31,47 +30,46 @@ public LocalDynamoDbSettingsRepository() _amazonDynamoDBClient = new AmazonDynamoDBClient("test", "test", clientConfig); } - public async Task GetAsync(Guid tenantId) + public async Task ExecuteQuery(QueryRead queryRead) { var getRequest = new GetItemRequest { - TableName = _dynamoTableName, - Key = new Dictionary() { { "TenantId", new AttributeValue { S = tenantId.ToString().ToLower() } } }, + TableName = queryRead.TableName, + Key = new Dictionary(), }; + foreach (var filter in queryRead.FilterFields) + { + getRequest.Key.Add(filter.Name, new AttributeValue { S = filter.Value?.ToString().ToLower() }); + } + var response = await _amazonDynamoDBClient.GetItemAsync(getRequest); //If there is no settings configured for the tenant create a new one if (response.Item.Count.Equals(0)) { - var newSettings = new Settings() - { - Group1 = new SettingGroup() - { - Setting1 = "a", - Setting2 = "b" - }, - }; - - await SaveAsync(tenantId, newSettings); - - return newSettings; + //Save + var queryWrite = new QueryWrite(); + queryWrite.FilterFields = queryRead.FilterFields; + queryWrite.UpdateFields = new List() { new QueryField() { Name= queryRead.SelectFields[0].ToString(), Value = queryRead.DefaultResult } }; + await ExecuteQuery(queryWrite); + return queryRead.DefaultResult; } - var existingSettings = JsonSerializer.Deserialize(response.Item["Settings"].S, new JsonSerializerOptions() { MaxDepth = 0, PropertyNameCaseInsensitive = true }); + var existingSettings = JsonSerializer.Deserialize(response.Item[queryRead.SelectFields[0].ToString()].S, new JsonSerializerOptions() { MaxDepth = 0, PropertyNameCaseInsensitive = true }); return existingSettings; } - public async Task SaveAsync(Guid tenantId, Settings settings) + public async Task ExecuteQuery(QueryWrite query) { var putRequest = new PutItemRequest { - TableName = _dynamoTableName, + TableName = query.TableName, Item = new Dictionary() { - { "TenantId", new AttributeValue { S = tenantId.ToString() } }, - { "Settings", new AttributeValue { S = JsonSerializer.Serialize(settings).ToLower() } } + { query.FilterFields[0].Name, new AttributeValue { S = query.FilterFields[0].Value.ToString() } }, + { query.UpdateFields[0].Name, new AttributeValue { S = JsonSerializer.Serialize(query.UpdateFields[0].Value).ToLower() } } } }; diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs deleted file mode 100644 index 3226dd8..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDbSettingsRepository.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Net; -using System.Text.Json; -using Amazon.DynamoDBv2; -using Amazon.DynamoDBv2.Model; -using ServiceName.Core.Common.Interfaces; -using ServiceName.Core.Model; - -namespace ServiceName.Infrastructure.Repositories -{ - /// - //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.DotNet.html - //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html - //aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST - //awslocal dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST - /// - public class DynamoDbSettingsRepository : IRepositoryService - { - AmazonDynamoDBClient _amazonDynamoDBClient; - string _dynamoTableName = "ServiceName_Setting"; - - public DynamoDbSettingsRepository() - { - _amazonDynamoDBClient = new AmazonDynamoDBClient("test", "test"); - } - - public async Task GetAsync(Guid tenantId) - { - var getRequest = new GetItemRequest - { - TableName = _dynamoTableName, - Key = new Dictionary() { { "TenantId", new AttributeValue { S = tenantId.ToString().ToLower() } } }, - }; - - var response = await _amazonDynamoDBClient.GetItemAsync(getRequest); - - //If there is no settings configured for the tenant create a new one - if (response.Item.Count.Equals(0)) - { - var newSettings = new Settings() - { - Group1 = new SettingGroup() - { - Setting1 = "a", - Setting2 = "b" - }, - }; - - await SaveAsync(tenantId, newSettings); - - return newSettings; - } - - var existingSettings = JsonSerializer.Deserialize(response.Item["Settings"].S, new JsonSerializerOptions() { MaxDepth = 0, PropertyNameCaseInsensitive = true }); - - return existingSettings; - } - - public async Task SaveAsync(Guid tenantId, Settings settings) - { - var putRequest = new PutItemRequest - { - TableName = _dynamoTableName, - Item = new Dictionary() - { - { "TenantId", new AttributeValue { S = tenantId.ToString() } }, - { "Settings", new AttributeValue { S = JsonSerializer.Serialize(settings).ToLower() } } - } - }; - - var response = await _amazonDynamoDBClient.PutItemAsync(putRequest); - - if (!response.HttpStatusCode.Equals(HttpStatusCode.OK)) - { - return false; - } - - return true; - } - - } -} diff --git a/MicroserviceTemplate/src/tye.yaml b/MicroserviceTemplate/src/tye.yaml index 2a1d9aa..82b5750 100644 --- a/MicroserviceTemplate/src/tye.yaml +++ b/MicroserviceTemplate/src/tye.yaml @@ -11,7 +11,7 @@ extensions: name: servicetemplate ingress: - - name: ingress + - name: Ingress bindings: - port: 51000 rules: @@ -26,18 +26,18 @@ services: - port: 51001 replicas: 2 - #- name: SQL-Database - # image: mcr.microsoft.com/mssql/server:2019-latest - # bindings: - # - connectionString: Data Source=host.docker.internal,1433;Initial Catalog=ServiceDB;Persist Security Info=True;User ID=sa;Password=${env:SA_PASSWORD} - # port: 1433 - # env: - # - name: SA_PASSWORD - # value: M3rz0ug4!!!! - # - name: ACCEPT_EULA - # value: Y + - name: SqlServer + image: mcr.microsoft.com/mssql/server:2019-latest + bindings: + - connectionString: Data Source=host.docker.internal,1433;Initial Catalog=ServiceDB;Persist Security Info=True;User ID=sa;Password=${env:SA_PASSWORD} + port: 1433 + env: + - name: SA_PASSWORD + value: M3rz0ug4!!!! + - name: ACCEPT_EULA + value: Y - - name: redis-cache + - name: Redis image: redis bindings: - port: 6379 @@ -46,7 +46,7 @@ services: # https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html (Docker Tab) #aws --endpoint-url=http://localhost:52001 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST - - name: dynamodb-database + - name: DynamoDB image: "amazon/dynamodb-local:latest" args: -jar DynamoDBLocal.jar -sharedDb -dbPath /mnt/c/Temp/DynamoDB volumes: @@ -64,7 +64,7 @@ services: #aws --endpoint-url=http://localhost:52002 kms --region ap-southeast-2 create-key --key-spec RSA_4096 --key-usage ENCRYPT_DECRYPT #aws --endpoint-url=http://localhost:52002 kms --region ap-southeast-2 list-keys - - name: kms-local + - name: KMS image: nsmithuk/local-kms volumes: - source: "./" From 40173bc22edcdbeab879036fce0f0a35e2df9154 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Thu, 9 Jun 2022 11:45:47 +1000 Subject: [PATCH 07/56] [MicroserviceTemplate] Added TenantId Parameter for GET and POST endpoints Removed IDatabaseService Replaced DynamoDB queries with .NET Object Persistence Model Resolved DynamoDB performance issues in Tye.yaml Added LocalStack to Tye.yaml --- .../Extensions/EndpointExtensions.cs | 4 +- .../Common/Interfaces/IDatabaseService.cs | 10 --- .../src/ServiceName.Core/ConfigureServices.cs | 2 - .../src/ServiceName.Core/Model/Query.cs | 8 -- .../src/ServiceName.Core/Model/QueryField.cs | 8 -- .../src/ServiceName.Core/Model/QueryRead.cs | 8 -- .../src/ServiceName.Core/Model/QueryWrite.cs | 7 -- .../ServiceName.Core/Model/SettingGroup.cs | 8 +- .../src/ServiceName.Core/Model/Settings.cs | 4 +- .../Repository/SettingsRepository.cs | 80 ----------------- .../ConfigureServices.cs | 4 +- .../Databases/DynamoDatabaseService.cs | 75 ---------------- .../Databases/LocalDynamoDatabaseService.cs | 86 ------------------- .../DynamoDBModel/SettingDbRecord.cs | 18 ++++ .../LocalDynamoDatabaseService.cs | 79 +++++++++++++++++ .../ServiceName.Infrastructure.csproj | 2 + MicroserviceTemplate/src/tye.yaml | 30 +++++-- .../ServiceName.Test/AuthenticationTests.cs | 28 +++++- .../ServiceName.Test/ServiceName.Test.csproj | 1 + 19 files changed, 161 insertions(+), 301 deletions(-) delete mode 100644 MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IDatabaseService.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Core/Model/Query.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Core/Model/QueryField.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Core/Model/QueryRead.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Core/Model/QueryWrite.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Core/Repository/SettingsRepository.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Databases/DynamoDatabaseService.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Databases/LocalDynamoDatabaseService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDBModel/SettingDbRecord.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDatabaseService.cs diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs index 273a101..c812435 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs @@ -10,8 +10,8 @@ public static class EndpointExtensions { public static void MapEndpoints(this WebApplication app) { - app.MapGet("/settings", (IMediator mediator) => mediator.Send(new GetSettingsQueryRequest() { TenantId = Guid.Parse("2e861859-1be6-4c0d-bfce-1e9d9d31a1c9") })); - app.MapPost("/settings", (IMediator mediator, [FromBody] Settings settings) => mediator.Send(new SaveSettingsCommandRequest() { TenantId = Guid.Parse("2e861859-1be6-4c0d-bfce-1e9d9d31a1c9"), Settings = settings })); + app.MapGet("/settings/{tenantId}", async (IMediator mediator, string tenantId) => await mediator.Send(new GetSettingsQueryRequest() { TenantId = Guid.Parse(tenantId) })); + app.MapPost("/settings/{tenantId}", async (IMediator mediator, [FromBody] Settings settings, string tenantId) => await mediator.Send(new SaveSettingsCommandRequest() { TenantId = Guid.Parse(tenantId), Settings = settings })); } } } diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IDatabaseService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IDatabaseService.cs deleted file mode 100644 index 29dd1fa..0000000 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IDatabaseService.cs +++ /dev/null @@ -1,10 +0,0 @@ -using ServiceName.Core.Model; - -namespace ServiceName.Core.Common.Interfaces -{ - public interface IDatabaseService - { - Task ExecuteQuery(QueryRead query); - Task ExecuteQuery(QueryWrite query); - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs index bd6697a..c00909e 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs @@ -4,7 +4,6 @@ using ServiceName.Core.Common.Behaviours; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; -using ServiceName.Core.Repository; namespace ServiceName.Core { @@ -14,7 +13,6 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection { services.AddMediatR(Assembly.GetExecutingAssembly()); services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehaviour<,>)); - services.AddScoped, SettingsRepository>(); return services; } } diff --git a/MicroserviceTemplate/src/ServiceName.Core/Model/Query.cs b/MicroserviceTemplate/src/ServiceName.Core/Model/Query.cs deleted file mode 100644 index 7094fa0..0000000 --- a/MicroserviceTemplate/src/ServiceName.Core/Model/Query.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ServiceName.Core.Model -{ - public class Query - { - public List FilterFields { get; set; } - public string TableName { get; set; } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Model/QueryField.cs b/MicroserviceTemplate/src/ServiceName.Core/Model/QueryField.cs deleted file mode 100644 index 02fce58..0000000 --- a/MicroserviceTemplate/src/ServiceName.Core/Model/QueryField.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ServiceName.Core.Model -{ - public class QueryField - { - public string Name { get; set; } - public object Value { get; set; } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Model/QueryRead.cs b/MicroserviceTemplate/src/ServiceName.Core/Model/QueryRead.cs deleted file mode 100644 index 5fa9dd7..0000000 --- a/MicroserviceTemplate/src/ServiceName.Core/Model/QueryRead.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ServiceName.Core.Model -{ - public class QueryRead: Query - { - public object DefaultResult { get; set; } - public List SelectFields { get; set; } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Core/Model/QueryWrite.cs b/MicroserviceTemplate/src/ServiceName.Core/Model/QueryWrite.cs deleted file mode 100644 index f852c7f..0000000 --- a/MicroserviceTemplate/src/ServiceName.Core/Model/QueryWrite.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ServiceName.Core.Model -{ - public class QueryWrite : Query - { - public List UpdateFields { get; set; } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Core/Model/SettingGroup.cs b/MicroserviceTemplate/src/ServiceName.Core/Model/SettingGroup.cs index f9c45b7..7b39db6 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/Model/SettingGroup.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Model/SettingGroup.cs @@ -4,9 +4,9 @@ namespace ServiceName.Core.Model { public class SettingGroup { - [JsonPropertyName("Setting1")] - public string Setting1 { get; set; } - [JsonPropertyName("Setting2")] - public string Setting2 { get; set; } + [JsonPropertyName("IsSettingAEnabled")] + public bool IsSettingAEnabled { get; set; } + [JsonPropertyName("IsSettingBEnabled")] + public bool IsSettingBEnabled { get; set; } } } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Model/Settings.cs b/MicroserviceTemplate/src/ServiceName.Core/Model/Settings.cs index 099deef..8997fb0 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/Model/Settings.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Model/Settings.cs @@ -4,7 +4,7 @@ namespace ServiceName.Core.Model { public class Settings { - [JsonPropertyName("Group1")] - public SettingGroup Group1 { get; set; } + [JsonPropertyName("CategoryA")] + public SettingGroup CategoryA { get; set; } } } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Repository/SettingsRepository.cs b/MicroserviceTemplate/src/ServiceName.Core/Repository/SettingsRepository.cs deleted file mode 100644 index 05031f1..0000000 --- a/MicroserviceTemplate/src/ServiceName.Core/Repository/SettingsRepository.cs +++ /dev/null @@ -1,80 +0,0 @@ -using ServiceName.Core.Common.Interfaces; -using ServiceName.Core.Model; - -namespace ServiceName.Core.Repository -{ - /// - //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.DotNet.html - //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html - //aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST - //awslocal dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST - /// - public class SettingsRepository : IRepositoryService - { - IDatabaseService _databaseService; - - public SettingsRepository(IDatabaseService databaseService) - { - _databaseService = databaseService; - } - - public async Task GetByIdAsync(Guid tenantId) - { - QueryRead query = new QueryRead() { - - TableName = "ServiceName_Setting", - SelectFields = new List() { "Settings" }, - FilterFields = new List() { new QueryField(){ - Name = "TenantId", - Value = tenantId - } - } - }; - - query.DefaultResult = new Settings() - { - Group1 = new SettingGroup() - { - Setting1 = "a", - Setting2 = "b" - }, - }; - - - var settings = await _databaseService.ExecuteQuery(query); - return (Settings)settings; - } - - public async Task SaveAsync(Guid tenantId, Settings settings) - { - QueryWrite query = new QueryWrite() - { - TableName = "ServiceName_Setting", - FilterFields = new List() { new QueryField(){ - Name = "TenantId", - Value = tenantId - } - }, - UpdateFields = new List() { - new QueryField(){ - Name = "Settings", - Value = settings - } - } - }; - - var result = await _databaseService.ExecuteQuery(query); - return (bool)result; - } - - public Task DeleteAsync(int id) - { - throw new NotImplementedException(); - } - - public Task> GetAllAsync() - { - throw new NotImplementedException(); - } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs index a2b2edf..6544ac3 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs @@ -19,7 +19,7 @@ public static IServiceCollection AddInfrastructureServices(this IServiceCollecti if (options.IsDevelopment) { - services.AddScoped(); + services.AddScoped, LocalDynamoDatabaseService>(); services.AddSingleton(); //services.AddSingleton(); services.AddSingleton(); @@ -27,7 +27,7 @@ public static IServiceCollection AddInfrastructureServices(this IServiceCollecti } else { - services.AddScoped(); + services.AddScoped, LocalDynamoDatabaseService>(); services.AddSingleton(); services.AddSingleton(); } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Databases/DynamoDatabaseService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Databases/DynamoDatabaseService.cs deleted file mode 100644 index c6878a2..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Databases/DynamoDatabaseService.cs +++ /dev/null @@ -1,75 +0,0 @@ -//using System.Net; -//using System.Text.Json; -//using Amazon.DynamoDBv2; -//using Amazon.DynamoDBv2.Model; -//using ServiceName.Core.Common.Interfaces; -//using ServiceName.Core.Model; - -//namespace ServiceName.Infrastructure.Databases -//{ -// /// -// //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.DotNet.html -// //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html -// //aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST -// //awslocal dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST -// /// -// public class DynamoDbDatabaseService : IDatabaseService -// { -// AmazonDynamoDBClient _amazonDynamoDBClient; -// //string _dynamoTableName = "ServiceName_Setting"; - -// public DynamoDbDatabaseService() -// { -// _amazonDynamoDBClient = new AmazonDynamoDBClient("test", "test"); -// } - -// public async Task ExecuteQuery(QueryRead queryRead) -// { -// var getRequest = new GetItemRequest -// { -// TableName = queryRead.TableName, -// Key = new Dictionary() { { queryRead.FilterFields[0].Name, new AttributeValue { S = queryRead.FilterFields[0].Value?.ToString().ToLower() } } }, -// }; - -// var response = await _amazonDynamoDBClient.GetItemAsync(getRequest); - -// //If there is no settings configured for the tenant create a new one -// if (response.Item.Count.Equals(0)) -// { -// //Save -// var queryWrite = new QueryWrite(); -// queryWrite.FilterFields = queryRead.FilterFields; -// queryWrite.UpdateFields = new List() { new QueryField() { Name = queryRead.SelectFields[0].ToString(), Value = queryRead.DefaultResult } }; -// await ExecuteQuery(queryWrite); -// return queryRead.DefaultResult; -// } - -// var existingSettings = JsonSerializer.Deserialize(response.Item[queryRead.SelectFields[0].ToString()].S, new JsonSerializerOptions() { MaxDepth = 0, PropertyNameCaseInsensitive = true }); - -// return existingSettings; -// } - -// public async Task ExecuteQuery(QueryWrite query) -// { -// var putRequest = new PutItemRequest -// { -// TableName = query.TableName, -// Item = new Dictionary() -// { -// { query.FilterFields[0].Name, new AttributeValue { S = query.FilterFields[0].Value.ToString() } }, -// { query.UpdateFields[0].Name, new AttributeValue { S = JsonSerializer.Serialize(query.UpdateFields[0].Value).ToLower() } } -// } -// }; - -// var response = await _amazonDynamoDBClient.PutItemAsync(putRequest); - -// if (!response.HttpStatusCode.Equals(HttpStatusCode.OK)) -// { -// return false; -// } - -// return true; -// } - -// } -//} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Databases/LocalDynamoDatabaseService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Databases/LocalDynamoDatabaseService.cs deleted file mode 100644 index 0900bf6..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Databases/LocalDynamoDatabaseService.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System.Net; -using System.Text.Json; -using Amazon; -using Amazon.DynamoDBv2; -using Amazon.DynamoDBv2.Model; -using ServiceName.Core.Common.Interfaces; -using ServiceName.Core.Model; - -namespace ServiceName.Infrastructure.Databases -{ - /// - //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.DotNet.html - //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html - //aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST - //awslocal dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST - /// - public class LocalDynamoDatabaseService : IDatabaseService - { - AmazonDynamoDBClient _amazonDynamoDBClient; - - public LocalDynamoDatabaseService() - { - AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig - { - RegionEndpoint = RegionEndpoint.GetBySystemName("ap-southeast-2"), - UseHttp = true, - ServiceURL = "http://localhost:8000" - }; - - _amazonDynamoDBClient = new AmazonDynamoDBClient("test", "test", clientConfig); - } - - public async Task ExecuteQuery(QueryRead queryRead) - { - var getRequest = new GetItemRequest - { - TableName = queryRead.TableName, - Key = new Dictionary(), - }; - - foreach (var filter in queryRead.FilterFields) - { - getRequest.Key.Add(filter.Name, new AttributeValue { S = filter.Value?.ToString().ToLower() }); - } - - var response = await _amazonDynamoDBClient.GetItemAsync(getRequest); - - //If there is no settings configured for the tenant create a new one - if (response.Item.Count.Equals(0)) - { - //Save - var queryWrite = new QueryWrite(); - queryWrite.FilterFields = queryRead.FilterFields; - queryWrite.UpdateFields = new List() { new QueryField() { Name= queryRead.SelectFields[0].ToString(), Value = queryRead.DefaultResult } }; - await ExecuteQuery(queryWrite); - return queryRead.DefaultResult; - } - - var existingSettings = JsonSerializer.Deserialize(response.Item[queryRead.SelectFields[0].ToString()].S, new JsonSerializerOptions() { MaxDepth = 0, PropertyNameCaseInsensitive = true }); - - return existingSettings; - } - - public async Task ExecuteQuery(QueryWrite query) - { - var putRequest = new PutItemRequest - { - TableName = query.TableName, - Item = new Dictionary() - { - { query.FilterFields[0].Name, new AttributeValue { S = query.FilterFields[0].Value.ToString() } }, - { query.UpdateFields[0].Name, new AttributeValue { S = JsonSerializer.Serialize(query.UpdateFields[0].Value).ToLower() } } - } - }; - - var response = await _amazonDynamoDBClient.PutItemAsync(putRequest); - - if (!response.HttpStatusCode.Equals(HttpStatusCode.OK)) - { - return false; - } - - return true; - } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDBModel/SettingDbRecord.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDBModel/SettingDbRecord.cs new file mode 100644 index 0000000..80903a9 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDBModel/SettingDbRecord.cs @@ -0,0 +1,18 @@ +using Amazon.DynamoDBv2.DataModel; + +namespace ServiceName.Infrastructure.Repositories.DynamoDBModel +{ + [DynamoDBTable("ServiceName_Setting")] + public class SettingDbRecord + { + [DynamoDBHashKey] //Partition key + public string TenantId + { + get; set; + } + public string Settings + { + get; set; + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDatabaseService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDatabaseService.cs new file mode 100644 index 0000000..5b14cb8 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDatabaseService.cs @@ -0,0 +1,79 @@ +using System.Net; +using System.Text.Json; +using Amazon; +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.DataModel; +using Amazon.DynamoDBv2.Model; +using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Model; +using ServiceName.Infrastructure.Repositories.DynamoDBModel; + +namespace ServiceName.Infrastructure.Databases +{ + /// + //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.DotNet.html + //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html + //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DotNetSDKHighLevel.html + //aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST + //aws dynamodb list-tables --endpoint-url http://localhost:8000 + /// + public class LocalDynamoDatabaseService : IRepositoryService + { + DynamoDBContext _dynamoDBContext; + + public LocalDynamoDatabaseService() + { + AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig + { + RegionEndpoint = RegionEndpoint.GetBySystemName("ap-southeast-2"), + UseHttp = true, + ServiceURL = "http://localhost:8000" + }; + + var amazonDynamoDBClient = new AmazonDynamoDBClient("test", "test", clientConfig); + var dynamoDBContextConfig = new DynamoDBContextConfig() { ConsistentRead = true }; + _dynamoDBContext = new DynamoDBContext(amazonDynamoDBClient, dynamoDBContextConfig); + } + + public Task DeleteAsync(int id) + { + throw new NotImplementedException(); + } + + public Task> GetAllAsync() + { + throw new NotImplementedException(); + } + + public async Task GetByIdAsync(Guid id) + { + var settingDbRecord = await _dynamoDBContext.LoadAsync(id.ToString()); + if (settingDbRecord == null) + { + settingDbRecord = new SettingDbRecord() + { + TenantId = id.ToString(), + Settings = JsonSerializer.Serialize(new Settings() { CategoryA = new SettingGroup() }) + }; + + await _dynamoDBContext.SaveAsync(settingDbRecord); + } + + var settings = JsonSerializer.Deserialize(settingDbRecord.Settings, new JsonSerializerOptions() { MaxDepth = 0, PropertyNameCaseInsensitive = true }); + return settings; + } + + public async Task SaveAsync(Guid id, Settings obj) + { + var settingDbRecord = new SettingDbRecord() + { + TenantId = id.ToString(), + Settings = JsonSerializer.Serialize(obj) + }; + + await _dynamoDBContext.SaveAsync(settingDbRecord); + + return true; + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj b/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj index a562af4..5130397 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj @@ -10,6 +10,8 @@ + + diff --git a/MicroserviceTemplate/src/tye.yaml b/MicroserviceTemplate/src/tye.yaml index 82b5750..3052d44 100644 --- a/MicroserviceTemplate/src/tye.yaml +++ b/MicroserviceTemplate/src/tye.yaml @@ -10,6 +10,9 @@ extensions: logPath: ./.logs name: servicetemplate +#registry: exampleuser (required for Kubernetes) +#namespace: examplenamespace +network: tye-network ingress: - name: Ingress bindings: @@ -45,13 +48,18 @@ services: args: "redis-cli -h redis MONITOR" # https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html (Docker Tab) - #aws --endpoint-url=http://localhost:52001 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST + # https://docs.amazonaws.cn/en_us/amazondynamodb/latest/developerguide/DynamoDBLocal.UsageNotes.html + # aws --endpoint-url=http://localhost:8000 dynamodb list-tables + # aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST + # -inMemory and -dbPath cannot be set at the same time + # Local DynamoDB has serious performance issues when not using -inMemory parameter - name: DynamoDB image: "amazon/dynamodb-local:latest" - args: -jar DynamoDBLocal.jar -sharedDb -dbPath /mnt/c/Temp/DynamoDB - volumes: - - source: "./" - target: "/mnt/c/Temp/DynamoDB" + args: -jar DynamoDBLocal.jar -inMemory -sharedDb + #args: -jar DynamoDBLocal.jar -sharedDb -dbPath /mnt/c/Temp/DynamoDB + #volumes: + # - source: "./" + # target: "/mnt/c/Temp/DynamoDB" bindings: - port: 8000 env: @@ -70,4 +78,14 @@ services: - source: "./" target: "/mnt/c/Temp/Kms" bindings: - - port: 52002 \ No newline at end of file + - port: 52002 + + - name: LocalStack + image: "localstack/localstack:latest" + bindings: + - port: 4566 + env: + - name: DEBUG + value: "1" + - name: SERVICES + value: "logs" \ No newline at end of file diff --git a/MicroserviceTemplate/tests/ServiceName.Test/AuthenticationTests.cs b/MicroserviceTemplate/tests/ServiceName.Test/AuthenticationTests.cs index 4ba6e11..c118ec1 100644 --- a/MicroserviceTemplate/tests/ServiceName.Test/AuthenticationTests.cs +++ b/MicroserviceTemplate/tests/ServiceName.Test/AuthenticationTests.cs @@ -1,4 +1,9 @@ +using Antlr4.Runtime.Misc; +using Microsoft.SqlServer.Management.SqlParser.Parser; +using Microsoft.SqlServer.TransactSql.ScriptDom; using ServiceName.Infrastructure.Authentication; +using SQLParser.Enums; +using SQLParser.Parsers.TSql; namespace ServiceName.Test { @@ -13,9 +18,30 @@ public void GenerateAndValidateJwtToken() } [Fact] - public void ValidateJWT() + public void TestSQLParser() { + //https://devblogs.microsoft.com/azure-sql/programmatically-parsing-transact-sql-t-sql-with-the-scriptdom-parser/ + using (var rdr = new StringReader("SELECT Settings FROM ServiceName_Setting WHERE TenantId=1")) + { + IList errors = null; + var parser = new TSql150Parser(true, SqlEngineType.All); + var tree = parser.Parse(rdr, out errors); + MyVisitor checker = new MyVisitor(); + tree.Accept(checker); + } + } + } + + class MyVisitor : TSqlFragmentVisitor + { + internal bool containsOnlySelects = true; + public readonly List SelectStatements = new List(); + + public override void Visit(SelectStatement node) + { + SelectStatements.Add(node); + base.Visit(node); } } } \ No newline at end of file diff --git a/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj b/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj index 708a520..7811104 100644 --- a/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj +++ b/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj @@ -10,6 +10,7 @@ + runtime; build; native; contentfiles; analyzers; buildtransitive From 58cdd206fbc806b595092958364315ee54e3d812 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Thu, 9 Jun 2022 16:06:20 +1000 Subject: [PATCH 08/56] [MicroserviceTemplate] IDynamoDBContext is now being injected in the constructor Added SettingsRepository unit test --- .../src/ServiceName.Core/ConfigureServices.cs | 2 - .../ConfigureServices.cs | 25 ++++- .../LocalDynamoDatabaseService.cs | 79 ---------------- .../Repositories/SettingsRepositoryService.cs | 91 +++++++++++++++++++ MicroserviceTemplate/src/tye.yaml | 2 +- .../ServiceName.Test/AuthenticationTests.cs | 47 ---------- .../tests/ServiceName.Test/RepositoryTests.cs | 34 +++++++ .../ServiceName.Test/ServiceName.Test.csproj | 2 +- 8 files changed, 149 insertions(+), 133 deletions(-) delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDatabaseService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs delete mode 100644 MicroserviceTemplate/tests/ServiceName.Test/AuthenticationTests.cs create mode 100644 MicroserviceTemplate/tests/ServiceName.Test/RepositoryTests.cs diff --git a/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs index c00909e..02d58b3 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs @@ -2,8 +2,6 @@ using MediatR; using Microsoft.Extensions.DependencyInjection; using ServiceName.Core.Common.Behaviours; -using ServiceName.Core.Common.Interfaces; -using ServiceName.Core.Model; namespace ServiceName.Core { diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs index 6544ac3..21a0202 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs @@ -1,4 +1,7 @@ -using Microsoft.Extensions.DependencyInjection; +using Amazon; +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.DataModel; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; @@ -19,20 +22,36 @@ public static IServiceCollection AddInfrastructureServices(this IServiceCollecti if (options.IsDevelopment) { - services.AddScoped, LocalDynamoDatabaseService>(); + services.AddScoped, SettingsRepositoryService>(); services.AddSingleton(); //services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(GetLocalDynamoDB()); } else { - services.AddScoped, LocalDynamoDatabaseService>(); + services.AddScoped, SettingsRepositoryService>(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(GetLocalDynamoDB()); } return services; } + + private static DynamoDBContext GetLocalDynamoDB() + { + AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig + { + RegionEndpoint = RegionEndpoint.GetBySystemName("ap-southeast-2"), + UseHttp = true, + ServiceURL = "http://localhost:8000" + }; + + var amazonDynamoDBClient = new AmazonDynamoDBClient("test", "test", clientConfig); + var dynamoDBContextConfig = new DynamoDBContextConfig() { ConsistentRead = true }; + return new DynamoDBContext(amazonDynamoDBClient, dynamoDBContextConfig); + } } } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDatabaseService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDatabaseService.cs deleted file mode 100644 index 5b14cb8..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/LocalDynamoDatabaseService.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.Net; -using System.Text.Json; -using Amazon; -using Amazon.DynamoDBv2; -using Amazon.DynamoDBv2.DataModel; -using Amazon.DynamoDBv2.Model; -using ServiceName.Core.Common.Interfaces; -using ServiceName.Core.Model; -using ServiceName.Infrastructure.Repositories.DynamoDBModel; - -namespace ServiceName.Infrastructure.Databases -{ - /// - //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.DotNet.html - //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html - //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DotNetSDKHighLevel.html - //aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST - //aws dynamodb list-tables --endpoint-url http://localhost:8000 - /// - public class LocalDynamoDatabaseService : IRepositoryService - { - DynamoDBContext _dynamoDBContext; - - public LocalDynamoDatabaseService() - { - AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig - { - RegionEndpoint = RegionEndpoint.GetBySystemName("ap-southeast-2"), - UseHttp = true, - ServiceURL = "http://localhost:8000" - }; - - var amazonDynamoDBClient = new AmazonDynamoDBClient("test", "test", clientConfig); - var dynamoDBContextConfig = new DynamoDBContextConfig() { ConsistentRead = true }; - _dynamoDBContext = new DynamoDBContext(amazonDynamoDBClient, dynamoDBContextConfig); - } - - public Task DeleteAsync(int id) - { - throw new NotImplementedException(); - } - - public Task> GetAllAsync() - { - throw new NotImplementedException(); - } - - public async Task GetByIdAsync(Guid id) - { - var settingDbRecord = await _dynamoDBContext.LoadAsync(id.ToString()); - if (settingDbRecord == null) - { - settingDbRecord = new SettingDbRecord() - { - TenantId = id.ToString(), - Settings = JsonSerializer.Serialize(new Settings() { CategoryA = new SettingGroup() }) - }; - - await _dynamoDBContext.SaveAsync(settingDbRecord); - } - - var settings = JsonSerializer.Deserialize(settingDbRecord.Settings, new JsonSerializerOptions() { MaxDepth = 0, PropertyNameCaseInsensitive = true }); - return settings; - } - - public async Task SaveAsync(Guid id, Settings obj) - { - var settingDbRecord = new SettingDbRecord() - { - TenantId = id.ToString(), - Settings = JsonSerializer.Serialize(obj) - }; - - await _dynamoDBContext.SaveAsync(settingDbRecord); - - return true; - } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs new file mode 100644 index 0000000..2229fb2 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs @@ -0,0 +1,91 @@ +using System.Text.Json; +using Amazon.DynamoDBv2.DataModel; +using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Model; +using ServiceName.Infrastructure.Repositories.DynamoDBModel; + +namespace ServiceName.Infrastructure.Databases +{ + /// + //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.DotNet.html + //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html + //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DotNetSDKHighLevel.html + //aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST + //aws dynamodb list-tables --endpoint-url http://localhost:8000 + /// + public class SettingsRepositoryService : IRepositoryService + { + readonly IDynamoDBContext _dynamoDBContext; + readonly IConfigurationService _configurationService; + readonly ILoggingService _loggingService; + + public SettingsRepositoryService(IConfigurationService configurationService, ILoggingService loggingService, IDynamoDBContext dynamoContext) + { + _configurationService = configurationService; + _loggingService = loggingService; + _dynamoDBContext = dynamoContext; + } + + public Task DeleteAsync(int id) + { + throw new NotImplementedException(); + } + + public Task> GetAllAsync() + { + throw new NotImplementedException(); + } + + public async Task GetByIdAsync(Guid id) + { + try + { + await _loggingService.LogInformationAsync($"LocalDynamoDatabaseService GetByIdAsync {id} started"); + var settingDbRecord = await _dynamoDBContext.LoadAsync(id.ToString()); + + if (settingDbRecord == null) + { + settingDbRecord= await SaveNewSettings(id, new Settings() { CategoryA = new SettingGroup() }); + } + + var settings = JsonSerializer.Deserialize(settingDbRecord.Settings, new JsonSerializerOptions() { MaxDepth = 0, PropertyNameCaseInsensitive = true }); + + await _loggingService.LogInformationAsync($"LocalDynamoDatabaseService GetByIdAsync {id} finished"); + + return settings; + } + catch (Exception ex) + { + await _loggingService.LogErrorAsync(ex.Message, ex.StackTrace); + throw; + } + } + + public async Task SaveAsync(Guid id, Settings obj) + { + try + { + await SaveNewSettings(id, obj); + return true; + } + catch (Exception ex) + { + await _loggingService.LogErrorAsync(ex.Message, ex.StackTrace); + throw; + } + } + + private async Task SaveNewSettings(Guid id, Settings obj) + { + var settingDbRecord = new SettingDbRecord() + { + TenantId = id.ToString(), + Settings = JsonSerializer.Serialize(obj) + }; + + await _dynamoDBContext.SaveAsync(settingDbRecord); + + return settingDbRecord; + } + } +} diff --git a/MicroserviceTemplate/src/tye.yaml b/MicroserviceTemplate/src/tye.yaml index 3052d44..fb262d3 100644 --- a/MicroserviceTemplate/src/tye.yaml +++ b/MicroserviceTemplate/src/tye.yaml @@ -27,7 +27,7 @@ services: project: ServiceName.API/ServiceName.API.csproj bindings: - port: 51001 - replicas: 2 + #replicas: 2 - name: SqlServer image: mcr.microsoft.com/mssql/server:2019-latest diff --git a/MicroserviceTemplate/tests/ServiceName.Test/AuthenticationTests.cs b/MicroserviceTemplate/tests/ServiceName.Test/AuthenticationTests.cs deleted file mode 100644 index c118ec1..0000000 --- a/MicroserviceTemplate/tests/ServiceName.Test/AuthenticationTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Antlr4.Runtime.Misc; -using Microsoft.SqlServer.Management.SqlParser.Parser; -using Microsoft.SqlServer.TransactSql.ScriptDom; -using ServiceName.Infrastructure.Authentication; -using SQLParser.Enums; -using SQLParser.Parsers.TSql; - -namespace ServiceName.Test -{ - public class AuthenticationTests - { - [Fact] - public void GenerateAndValidateJwtToken() - { - MockJwtAuthenticationService authService = new MockJwtAuthenticationService(); - var token = authService.GenerateToken(1); - var isValid = authService.ValidateToken(token); - } - - [Fact] - public void TestSQLParser() - { - //https://devblogs.microsoft.com/azure-sql/programmatically-parsing-transact-sql-t-sql-with-the-scriptdom-parser/ - using (var rdr = new StringReader("SELECT Settings FROM ServiceName_Setting WHERE TenantId=1")) - { - IList errors = null; - var parser = new TSql150Parser(true, SqlEngineType.All); - var tree = parser.Parse(rdr, out errors); - MyVisitor checker = new MyVisitor(); - tree.Accept(checker); - } - } - } - - class MyVisitor : TSqlFragmentVisitor - { - internal bool containsOnlySelects = true; - public readonly List SelectStatements = new List(); - - public override void Visit(SelectStatement node) - { - SelectStatements.Add(node); - - base.Visit(node); - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/tests/ServiceName.Test/RepositoryTests.cs b/MicroserviceTemplate/tests/ServiceName.Test/RepositoryTests.cs new file mode 100644 index 0000000..3406d16 --- /dev/null +++ b/MicroserviceTemplate/tests/ServiceName.Test/RepositoryTests.cs @@ -0,0 +1,34 @@ +using Moq; +using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Model; + +namespace ServiceName.Test +{ + public class RepositoryTests + { + [Fact] + public async Task SettingRepositoryTest() + { + var tenantId = Guid.Parse("58628ac6-eba8-4e9f-87fe-a2d1b69d0d34"); + var mock = new Mock>(); + mock.Setup(x => x.GetByIdAsync(tenantId)).Returns(Task.FromResult(GetSettingsObject())); + + var result = await mock.Object.GetByIdAsync(tenantId); + + Assert.NotNull(result.CategoryA); + Assert.True(result.CategoryA.IsSettingAEnabled); + Assert.False(result.CategoryA.IsSettingBEnabled); + } + + + private Settings GetSettingsObject() + { + return new Settings() + { + CategoryA = new SettingGroup() { IsSettingAEnabled = true, + IsSettingBEnabled = false } + + }; + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj b/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj index 7811104..640f5d1 100644 --- a/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj +++ b/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj @@ -10,7 +10,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive From 7b3252211290974a39542babd2dd99ad1d9e3bfb Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Thu, 9 Jun 2022 16:21:51 +1000 Subject: [PATCH 09/56] [MicroserviceTemplate] Removed and ignored files that don't need to be tracked by Git --- .gitignore | 2 ++ MicroserviceTemplate/src/ServiceName.API/aws-logger-errors.txt | 0 2 files changed, 2 insertions(+) delete mode 100644 MicroserviceTemplate/src/ServiceName.API/aws-logger-errors.txt diff --git a/.gitignore b/.gitignore index 1000b0d..509aa93 100644 --- a/.gitignore +++ b/.gitignore @@ -347,3 +347,5 @@ MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ +/MicroserviceTemplate/src/.logs +/MicroserviceTemplate/src/shared-local-instance.db diff --git a/MicroserviceTemplate/src/ServiceName.API/aws-logger-errors.txt b/MicroserviceTemplate/src/ServiceName.API/aws-logger-errors.txt deleted file mode 100644 index e69de29..0000000 From 4ceb8a9921be86f6158d107cd49c288d5c26d55c Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Fri, 10 Jun 2022 09:19:37 +1000 Subject: [PATCH 10/56] [MicroserviceTemplate] Improved unit tests --- .../ConfigureServices.cs | 2 +- .../Repositories/SettingsRepositoryService.cs | 8 ++-- .../ServiceName.Test/Helpers/TestHelper.cs | 16 +++++++ .../tests/ServiceName.Test/RepositoryTests.cs | 43 ++++++++++++------- .../ServiceName.Test/ServiceName.Test.csproj | 1 + 5 files changed, 49 insertions(+), 21 deletions(-) create mode 100644 MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs index 21a0202..b73a24b 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs @@ -7,8 +7,8 @@ using ServiceName.Core.Model; using ServiceName.Infrastructure.Caching; using ServiceName.Infrastructure.Configuration; -using ServiceName.Infrastructure.Databases; using ServiceName.Infrastructure.Logging; +using ServiceName.Infrastructure.Repositories; namespace ServiceName.Infrastructure { diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs index 2229fb2..856ebd9 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs @@ -4,7 +4,7 @@ using ServiceName.Core.Model; using ServiceName.Infrastructure.Repositories.DynamoDBModel; -namespace ServiceName.Infrastructure.Databases +namespace ServiceName.Infrastructure.Repositories { /// //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.DotNet.html @@ -23,7 +23,7 @@ public SettingsRepositoryService(IConfigurationService configurationService, ILo { _configurationService = configurationService; _loggingService = loggingService; - _dynamoDBContext = dynamoContext; + _dynamoDBContext = dynamoContext; } public Task DeleteAsync(int id) @@ -45,7 +45,7 @@ public async Task GetByIdAsync(Guid id) if (settingDbRecord == null) { - settingDbRecord= await SaveNewSettings(id, new Settings() { CategoryA = new SettingGroup() }); + settingDbRecord = await SaveNewSettings(id, new Settings() { CategoryA = new SettingGroup() }); } var settings = JsonSerializer.Deserialize(settingDbRecord.Settings, new JsonSerializerOptions() { MaxDepth = 0, PropertyNameCaseInsensitive = true }); @@ -83,7 +83,7 @@ private async Task SaveNewSettings(Guid id, Settings obj) Settings = JsonSerializer.Serialize(obj) }; - await _dynamoDBContext.SaveAsync(settingDbRecord); + await _dynamoDBContext.SaveAsync(settingDbRecord); return settingDbRecord; } diff --git a/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs b/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs new file mode 100644 index 0000000..389437c --- /dev/null +++ b/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs @@ -0,0 +1,16 @@ +using ServiceName.Infrastructure.Repositories.DynamoDBModel; + +namespace ServiceName.Test.Helpers +{ + internal class TestHelper + { + public static SettingDbRecord GetSettingsRecordObject() + { + return new SettingDbRecord() + { + TenantId = "53a13ec4-fde8-4087-8e2a-5fb6b1fbc062", + Settings = @"{""CategoryA"":{""IsSettingAEnabled"":true,""IsSettingBEnabled"":true}}" + }; + } + } +} diff --git a/MicroserviceTemplate/tests/ServiceName.Test/RepositoryTests.cs b/MicroserviceTemplate/tests/ServiceName.Test/RepositoryTests.cs index 3406d16..3bb875f 100644 --- a/MicroserviceTemplate/tests/ServiceName.Test/RepositoryTests.cs +++ b/MicroserviceTemplate/tests/ServiceName.Test/RepositoryTests.cs @@ -1,34 +1,45 @@ +using Amazon.DynamoDBv2.DataModel; using Moq; -using ServiceName.Core.Common.Interfaces; -using ServiceName.Core.Model; +using Moq.AutoMock; +using ServiceName.Infrastructure.Repositories; +using ServiceName.Infrastructure.Repositories.DynamoDBModel; +using ServiceName.Test.Helpers; namespace ServiceName.Test { public class RepositoryTests { + readonly AutoMocker _autoMocker; + readonly SettingsRepositoryService _mockSettingsRepository; + + public RepositoryTests() + { + _autoMocker = new AutoMocker(); + _mockSettingsRepository = _autoMocker.CreateInstance(); + } + [Fact] - public async Task SettingRepositoryTest() + public async Task SettingRepositoryTestWhenTenantIdExists() { - var tenantId = Guid.Parse("58628ac6-eba8-4e9f-87fe-a2d1b69d0d34"); - var mock = new Mock>(); - mock.Setup(x => x.GetByIdAsync(tenantId)).Returns(Task.FromResult(GetSettingsObject())); - - var result = await mock.Object.GetByIdAsync(tenantId); + _autoMocker.GetMock().Setup(x => x.LoadAsync("53a13ec4-fde8-4087-8e2a-5fb6b1fbc062", default)).ReturnsAsync(TestHelper.GetSettingsRecordObject()); + + var result = await _mockSettingsRepository.GetByIdAsync(Guid.Parse("53a13ec4-fde8-4087-8e2a-5fb6b1fbc062")); Assert.NotNull(result.CategoryA); Assert.True(result.CategoryA.IsSettingAEnabled); - Assert.False(result.CategoryA.IsSettingBEnabled); + Assert.True(result.CategoryA.IsSettingBEnabled); } - - private Settings GetSettingsObject() + [Fact] + public async Task SettingRepositoryTestWhenTenantIdDoesNotExist() { - return new Settings() - { - CategoryA = new SettingGroup() { IsSettingAEnabled = true, - IsSettingBEnabled = false } + _autoMocker.GetMock().Setup(x => x.LoadAsync("296b73d9-692a-42bf-9ccb-ff41ca256722", default)).ReturnsAsync((SettingDbRecord)null); + + var result = await _mockSettingsRepository.GetByIdAsync(Guid.Parse("296b73d9-692a-42bf-9ccb-ff41ca256722")); - }; + Assert.NotNull(result.CategoryA); + Assert.False(result.CategoryA.IsSettingAEnabled); + Assert.False(result.CategoryA.IsSettingBEnabled); } } } \ No newline at end of file diff --git a/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj b/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj index 640f5d1..db13d4d 100644 --- a/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj +++ b/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj @@ -11,6 +11,7 @@ + runtime; build; native; contentfiles; analyzers; buildtransitive From e812606f96fdac30b422ef16d14ca482966e3c47 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Tue, 14 Jun 2022 11:35:48 +1000 Subject: [PATCH 11/56] [MicroserviceTemplate] Simplified Configuration Injection --- .../src/ServiceName.API/Program.cs | 2 +- .../appsettings.Development.json | 13 +++--- .../src/ServiceName.API/appsettings.json | 3 +- .../CQRS/Commands/SaveSettingsCommand.cs | 7 +-- .../CQRS/Queries/GetSettingsQuery.cs | 7 +-- .../Interfaces/IConfigurationService.cs | 13 ------ .../AwsParameterStoreConfigurationService.cs | 19 -------- ...EnvironmentVariableConfigurationService.cs | 19 -------- .../Configuration/FileConfigurationService.cs | 30 ------------- .../JsonStringConfigurationService.cs | 20 --------- .../ConfigureServices.cs | 44 +++++++++---------- .../Repositories/SettingsRepositoryService.cs | 7 +-- MicroserviceTemplate/src/tye.yaml | 2 +- 13 files changed, 44 insertions(+), 142 deletions(-) delete mode 100644 MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IConfigurationService.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/AwsParameterStoreConfigurationService.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/EnvironmentVariableConfigurationService.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/FileConfigurationService.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/JsonStringConfigurationService.cs diff --git a/MicroserviceTemplate/src/ServiceName.API/Program.cs b/MicroserviceTemplate/src/ServiceName.API/Program.cs index 052995a..a3a7eee 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Program.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Program.cs @@ -19,8 +19,8 @@ builder.Services.ConfigureSwaggerServices(); //Clean Architecute: Service Injection +builder.Services.AddInfrastructureServices(builder.Configuration); builder.Services.AddApplicationServices(); -builder.Services.AddInfrastructureServices(options => options.IsDevelopment = true); var app = builder.Build(); diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json index a783479..5220dde 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json @@ -1,8 +1,9 @@ { - "Logging": { - "LogLevel": { - "Default": "Debug", - "Microsoft.AspNetCore": "Warning" - } - } + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.json index 28faa2f..b2a9133 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.json @@ -5,5 +5,6 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "MyConfig": "someconfig" } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs index aa97d6f..7cf8851 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs @@ -1,4 +1,5 @@ using MediatR; +using Microsoft.Extensions.Configuration; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; @@ -13,13 +14,13 @@ public record SaveSettingsCommandRequest : IRequest public class CreateTodoListCommandHandler : IRequestHandler { IRepositoryService _settingsRepository; - IConfigurationService _configurationService; + IConfiguration _configuration; ICachingService _cachingService; - public CreateTodoListCommandHandler(IRepositoryService settingsRepository, IConfigurationService configurationService, ICachingService cachingService) + public CreateTodoListCommandHandler(IRepositoryService settingsRepository, IConfiguration configuration, ICachingService cachingService) { _settingsRepository = settingsRepository; - _configurationService = configurationService; + _configuration = configuration; _cachingService = cachingService; } diff --git a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs index 8c90896..ed6601f 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs @@ -1,4 +1,5 @@ using MediatR; +using Microsoft.Extensions.Configuration; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; @@ -11,13 +12,13 @@ public record GetSettingsQueryRequest : IRequest public class GetSettingsQueryHandler : IRequestHandler { IRepositoryService _settingsRepository; - IConfigurationService _configurationService; + IConfiguration _configuration; ICachingService _cachingService; - public GetSettingsQueryHandler(IRepositoryService settingsRepository, IConfigurationService configurationService, ICachingService cachingService) + public GetSettingsQueryHandler(IRepositoryService settingsRepository, IConfiguration configuration, ICachingService cachingService) { _settingsRepository = settingsRepository; - _configurationService = configurationService; + _configuration = configuration; _cachingService = cachingService; } diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IConfigurationService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IConfigurationService.cs deleted file mode 100644 index 4103da8..0000000 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IConfigurationService.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.Extensions.Configuration; - -namespace ServiceName.Core.Common.Interfaces -{ - /// - /// https://docs.microsoft.com/en-us/dotnet/core/extensions/configuration - /// - public interface IConfigurationService - { - void LoadFromSource(); - public IConfiguration Configuration { get; } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/AwsParameterStoreConfigurationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/AwsParameterStoreConfigurationService.cs deleted file mode 100644 index c358865..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/AwsParameterStoreConfigurationService.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.Extensions.Configuration; -using ServiceName.Core.Common.Interfaces; - -namespace ServiceName.Infrastructure.Configuration -{ - /// - /// https://github.com/aws/aws-dotnet-extensions-configuration - /// - public class AwsParameterStoreConfigurationService : IConfigurationService - { - IConfiguration _configuration; - public IConfiguration Configuration => _configuration; - - public void LoadFromSource() - { - throw new NotImplementedException(); - } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/EnvironmentVariableConfigurationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/EnvironmentVariableConfigurationService.cs deleted file mode 100644 index 733bf36..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/EnvironmentVariableConfigurationService.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.Extensions.Configuration; -using ServiceName.Core.Common.Interfaces; - -namespace ServiceName.Infrastructure.Configuration -{ - internal class EnvironmentVariableConfigurationService : IConfigurationService - { - IConfiguration _configuration; - public IConfiguration Configuration => _configuration; - - public void LoadFromSource() - { - _configuration = new ConfigurationBuilder() - .AddEnvironmentVariables() - .Build(); - } - - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/FileConfigurationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/FileConfigurationService.cs deleted file mode 100644 index 7aafaec..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/FileConfigurationService.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Reflection; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Options; -using ServiceName.Core.Common.Interfaces; -using ServiceName.Core.Model; - -namespace ServiceName.Infrastructure.Configuration -{ - public class FileConfigurationService : IConfigurationService - { - IConfiguration _configuration; - - IOptions _infrastructureOptions; - - public IConfiguration Configuration => _configuration; - - public void LoadFromSource() - { - - var environmentName = _infrastructureOptions.Value.IsDevelopment ? "Development.json" : "appsettings.json"; - - _configuration = new ConfigurationBuilder() - .SetBasePath(Assembly.GetExecutingAssembly().Location) - .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true) - .Build(); - } - - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/JsonStringConfigurationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/JsonStringConfigurationService.cs deleted file mode 100644 index 5129453..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Configuration/JsonStringConfigurationService.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Text; -using Microsoft.Extensions.Configuration; -using ServiceName.Core.Common.Interfaces; - -namespace ServiceName.Infrastructure.Configuration -{ - public class JsonStringConfigurationService : IConfigurationService - { - IConfiguration _configuration; - public IConfiguration Configuration => _configuration; - - public void LoadFromSource() - { - throw new NotImplementedException(); - //_configuration = new ConfigurationBuilder() - // .AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(jsonString))) - // .Build(); //https://github.com/dotnet/runtime/issues/36018 - } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs index b73a24b..5547bef 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs @@ -1,12 +1,11 @@ using Amazon; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.DataModel; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; using ServiceName.Infrastructure.Caching; -using ServiceName.Infrastructure.Configuration; using ServiceName.Infrastructure.Logging; using ServiceName.Infrastructure.Repositories; @@ -14,32 +13,31 @@ namespace ServiceName.Infrastructure { public static class ConfigureServices { - public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, Action? infrastructureOptions = null) + public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, ConfigurationManager configurationManager) { - var options = new ModuleOptions(); - services.Configure(infrastructureOptions); - infrastructureOptions?.Invoke(options); - - if (options.IsDevelopment) - { - services.AddScoped, SettingsRepositoryService>(); - services.AddSingleton(); - //services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(GetLocalDynamoDB()); - } - else - { - services.AddScoped, SettingsRepositoryService>(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(GetLocalDynamoDB()); - } + services.AddSingleton(GetConfiguration(configurationManager)); + services.AddScoped, SettingsRepositoryService>(); + services.AddSingleton(); + //services.AddSingleton(); + + services.AddSingleton(); + services.AddSingleton(GetLocalDynamoDB()); return services; } + private static IConfiguration GetConfiguration(ConfigurationManager configurationManager) + { + configurationManager + .SetBasePath(Environment.CurrentDirectory) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile($"appsettings.Development.json", optional: true, reloadOnChange: true) + .AddEnvironmentVariables() + .Build(); + + return configurationManager; + } + private static DynamoDBContext GetLocalDynamoDB() { AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs index 856ebd9..acd2746 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs @@ -1,5 +1,6 @@ using System.Text.Json; using Amazon.DynamoDBv2.DataModel; +using Microsoft.Extensions.Configuration; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; using ServiceName.Infrastructure.Repositories.DynamoDBModel; @@ -16,12 +17,12 @@ namespace ServiceName.Infrastructure.Repositories public class SettingsRepositoryService : IRepositoryService { readonly IDynamoDBContext _dynamoDBContext; - readonly IConfigurationService _configurationService; + readonly IConfiguration _configuration; readonly ILoggingService _loggingService; - public SettingsRepositoryService(IConfigurationService configurationService, ILoggingService loggingService, IDynamoDBContext dynamoContext) + public SettingsRepositoryService(IConfiguration configuration, ILoggingService loggingService, IDynamoDBContext dynamoContext) { - _configurationService = configurationService; + _configuration = configuration; _loggingService = loggingService; _dynamoDBContext = dynamoContext; } diff --git a/MicroserviceTemplate/src/tye.yaml b/MicroserviceTemplate/src/tye.yaml index fb262d3..455c06e 100644 --- a/MicroserviceTemplate/src/tye.yaml +++ b/MicroserviceTemplate/src/tye.yaml @@ -45,7 +45,7 @@ services: bindings: - port: 6379 connectionString: "${host}:${port}" - args: "redis-cli -h redis MONITOR" + #args: "redis-cli -h redis MONITOR" # https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html (Docker Tab) # https://docs.amazonaws.cn/en_us/amazondynamodb/latest/developerguide/DynamoDBLocal.UsageNotes.html From 19385272427b39ae9180f8b13d3b787bfff02711 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Tue, 14 Jun 2022 13:07:27 +1000 Subject: [PATCH 12/56] [MicroserviceTemplate] Simplified Logging injection --- .../Common/Behaviours/LoggingBehaviour.cs | 12 ++--- .../Common/Interfaces/ILoggingService.cs | 18 ------- .../ServiceName.Core/ServiceName.Core.csproj | 2 +- .../ConfigureServices.cs | 33 +++++++++++-- .../Logging/CloudWatchLoggingService.cs | 37 --------------- .../Logging/LocalCloudWatchLoggingService.cs | 47 ------------------- .../Logging/SeqLoggingService.cs | 35 -------------- .../Repositories/SettingsRepositoryService.cs | 15 +++--- MicroserviceTemplate/src/tye.yaml | 2 +- .../ServiceName.Test/ServiceName.Test.csproj | 4 +- 10 files changed, 48 insertions(+), 157 deletions(-) delete mode 100644 MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ILoggingService.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/CloudWatchLoggingService.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/LocalCloudWatchLoggingService.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/SeqLoggingService.cs diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs index 693f09b..dc1b1e9 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs @@ -1,18 +1,18 @@ using System.Diagnostics; using System.Text.Json; using MediatR; -using ServiceName.Core.Common.Interfaces; +using Serilog; namespace ServiceName.Core.Common.Behaviours { public class LoggingBehaviour : IPipelineBehavior where TRequest : IRequest { - ILoggingService _loggingService; + ILogger _logger; private readonly Stopwatch _timer; - public LoggingBehaviour(ILoggingService loggingService) + public LoggingBehaviour(ILogger logger) { - _loggingService = loggingService; + _logger = logger; _timer = new Stopwatch(); } @@ -21,7 +21,7 @@ public async Task Handle(TRequest request, CancellationToken cancella var correlationId = Guid.NewGuid(); var requestName = typeof(TRequest).Name; - await _loggingService.LogInformationAsync(@"correlationId {correlationId} + _logger.Information(@"correlationId {correlationId} requestName {requestName} type {requestType} payload {requestPayload}", correlationId, requestName, "Request", JsonSerializer.Serialize(request)); @@ -30,7 +30,7 @@ await _loggingService.LogInformationAsync(@"correlationId {correlationId} var response = await next(); _timer.Stop(); - await _loggingService.LogInformationAsync(@"correlationId {correlationId} + _logger.Information(@"correlationId {correlationId} requestName {requestName} type {requestType} payload {requestPayload} {elapsedMilliseconds}", correlationId, requestName, "Response", JsonSerializer.Serialize(response), _timer.ElapsedMilliseconds); diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ILoggingService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ILoggingService.cs deleted file mode 100644 index 2b33ae1..0000000 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ILoggingService.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace ServiceName.Core.Common.Interfaces -{ - /// - /// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-6.0 - /// - public interface ILoggingService - { - /// - /// - /// - /// Log Message - /// Allows Structure Logging: https://softwareengineering.stackexchange.com/questions/312197/benefits-of-structured-logging-vs-basic-logging - /// - Task LogInformationAsync(string message, params object[] propertyValues); - Task LogWarningAsync(string message, params object[] propertyValues); - Task LogErrorAsync(string message, params object[] propertyValues); - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Core/ServiceName.Core.csproj b/MicroserviceTemplate/src/ServiceName.Core/ServiceName.Core.csproj index 8f7dd8b..ffa344c 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/ServiceName.Core.csproj +++ b/MicroserviceTemplate/src/ServiceName.Core/ServiceName.Core.csproj @@ -8,8 +8,8 @@ - + diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs index 5547bef..04eea6e 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs @@ -1,12 +1,14 @@ using Amazon; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.DataModel; +using AWS.Logger; +using AWS.Logger.SeriLog; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Serilog; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; using ServiceName.Infrastructure.Caching; -using ServiceName.Infrastructure.Logging; using ServiceName.Infrastructure.Repositories; namespace ServiceName.Infrastructure @@ -17,8 +19,8 @@ public static IServiceCollection AddInfrastructureServices(this IServiceCollecti { services.AddSingleton(GetConfiguration(configurationManager)); services.AddScoped, SettingsRepositoryService>(); - services.AddSingleton(); - //services.AddSingleton(); + services.AddSingleton(GetCloudWatchLogger()); + //services.AddSingleton(GetCloudSeqLogger()); services.AddSingleton(); services.AddSingleton(GetLocalDynamoDB()); @@ -26,6 +28,31 @@ public static IServiceCollection AddInfrastructureServices(this IServiceCollecti return services; } + private static ILogger GetCloudSeqLogger() + { + Log.Logger = new LoggerConfiguration() + .WriteTo.Console() + .WriteTo.Seq("http://localhost:5341") + .CreateLogger(); + + return Log.Logger; + } + + private static ILogger GetCloudWatchLogger() + { + AWSLoggerConfig configuration = new("/LocalStack/Microservice/Logs") + { + Region = "ap-southeast-2", + ServiceUrl = "http://localhost:4566" + }; + + Log.Logger = new LoggerConfiguration().WriteTo.AWSSeriLog(configuration) + .WriteTo.Console() + .CreateLogger(); + + return Log.Logger; + } + private static IConfiguration GetConfiguration(ConfigurationManager configurationManager) { configurationManager diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/CloudWatchLoggingService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/CloudWatchLoggingService.cs deleted file mode 100644 index ad24d9c..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/CloudWatchLoggingService.cs +++ /dev/null @@ -1,37 +0,0 @@ -using AWS.Logger; -using AWS.Logger.SeriLog; -using Serilog; -using ServiceName.Core.Common.Interfaces; - -namespace ServiceName.Infrastructure.Logging -{ - internal class CloudWatchLoggingService : ILoggingService - { - /// - /// https://docs.aws.amazon.com/lambda/latest/dg/csharp-logging.html (LambdaLogger) - /// https://github.com/aws/aws-logging-dotnet - /// https://github.com/aws/aws-logging-dotnet/tree/master/samples/Serilog - /// - public CloudWatchLoggingService() - { - Log.Logger = new LoggerConfiguration().WriteTo.AWSSeriLog() - .WriteTo.Console() - .CreateLogger(); - } - - public async Task LogErrorAsync(string message, params object[] propertyValues) - { - Log.Error(message, propertyValues); - } - - public async Task LogInformationAsync(string message, params object[] propertyValues) - { - Log.Information(message, propertyValues); - } - - public async Task LogWarningAsync(string message, params object[] propertyValues) - { - Log.Warning(message, propertyValues); - } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/LocalCloudWatchLoggingService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/LocalCloudWatchLoggingService.cs deleted file mode 100644 index 689e513..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/LocalCloudWatchLoggingService.cs +++ /dev/null @@ -1,47 +0,0 @@ -using AWS.Logger; -using AWS.Logger.SeriLog; -using Serilog; -using ServiceName.Core.Common.Interfaces; - -namespace ServiceName.Infrastructure.Logging -{ - internal class LocalCloudWatchLoggingService : ILoggingService - { - /// - /// https://awscli.amazonaws.com/v2/documentation/api/latest/reference/logs/tail.html - /// https://docs.aws.amazon.com/cli/latest/reference/logs/create-log-group.html - /// awslocal logs describe-log-groups - /// awslocal logs tail "/LocalStack/Microservice/Logs" - /// https://docs.aws.amazon.com/lambda/latest/dg/csharp-logging.html (LambdaLogger) - /// https://github.com/aws/aws-logging-dotnet - /// https://github.com/aws/aws-logging-dotnet/tree/master/samples/Serilog - /// - public LocalCloudWatchLoggingService() - { - AWSLoggerConfig configuration = new("/LocalStack/Microservice/Logs") - { - Region = "ap-southeast-2", - ServiceUrl = "http://localhost:4566" - }; - - Log.Logger = new LoggerConfiguration().WriteTo.AWSSeriLog(configuration) - .WriteTo.Console() - .CreateLogger(); - } - - public async Task LogErrorAsync(string message, params object[] propertyValues) - { - Log.Error(message, propertyValues); - } - - public async Task LogInformationAsync(string message, params object[] propertyValues) - { - Log.Information(message, propertyValues); - } - - public async Task LogWarningAsync(string message, params object[] propertyValues) - { - Log.Warning(message, propertyValues); - } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/SeqLoggingService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/SeqLoggingService.cs deleted file mode 100644 index 6f0654f..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Logging/SeqLoggingService.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Serilog; -using ServiceName.Core.Common.Interfaces; - -namespace ServiceName.Infrastructure.Logging -{ - public class SeqLoggingService : ILoggingService - { - /// - /// Note: Seq already performs asynchronous batching natively. - /// https://github.com/serilog/serilog-sinks-async - /// - public SeqLoggingService() - { - Log.Logger = new LoggerConfiguration() - .WriteTo.Console() - .WriteTo.Seq("http://localhost:5341") - .CreateLogger(); - } - - public async Task LogErrorAsync(string message, params object[] propertyValues) - { - Log.Error(message, propertyValues); - } - - public async Task LogInformationAsync(string message, params object[] propertyValues) - { - Log.Information(message, propertyValues); - } - - public async Task LogWarningAsync(string message, params object[] propertyValues) - { - Log.Warning(message, propertyValues); - } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs index acd2746..2c864a2 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs @@ -1,6 +1,7 @@ using System.Text.Json; using Amazon.DynamoDBv2.DataModel; using Microsoft.Extensions.Configuration; +using Serilog; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; using ServiceName.Infrastructure.Repositories.DynamoDBModel; @@ -18,12 +19,12 @@ public class SettingsRepositoryService : IRepositoryService { readonly IDynamoDBContext _dynamoDBContext; readonly IConfiguration _configuration; - readonly ILoggingService _loggingService; + readonly ILogger _logger; - public SettingsRepositoryService(IConfiguration configuration, ILoggingService loggingService, IDynamoDBContext dynamoContext) + public SettingsRepositoryService(IConfiguration configuration, ILogger logger, IDynamoDBContext dynamoContext) { _configuration = configuration; - _loggingService = loggingService; + _logger = logger; _dynamoDBContext = dynamoContext; } @@ -41,7 +42,7 @@ public async Task GetByIdAsync(Guid id) { try { - await _loggingService.LogInformationAsync($"LocalDynamoDatabaseService GetByIdAsync {id} started"); + _logger.Information($"LocalDynamoDatabaseService GetByIdAsync {id} started"); var settingDbRecord = await _dynamoDBContext.LoadAsync(id.ToString()); if (settingDbRecord == null) @@ -51,13 +52,13 @@ public async Task GetByIdAsync(Guid id) var settings = JsonSerializer.Deserialize(settingDbRecord.Settings, new JsonSerializerOptions() { MaxDepth = 0, PropertyNameCaseInsensitive = true }); - await _loggingService.LogInformationAsync($"LocalDynamoDatabaseService GetByIdAsync {id} finished"); + _logger.Information($"LocalDynamoDatabaseService GetByIdAsync {id} finished"); return settings; } catch (Exception ex) { - await _loggingService.LogErrorAsync(ex.Message, ex.StackTrace); + _logger.Error(ex.Message, ex.StackTrace); throw; } } @@ -71,7 +72,7 @@ public async Task SaveAsync(Guid id, Settings obj) } catch (Exception ex) { - await _loggingService.LogErrorAsync(ex.Message, ex.StackTrace); + _logger.Error(ex.Message, ex.StackTrace); throw; } } diff --git a/MicroserviceTemplate/src/tye.yaml b/MicroserviceTemplate/src/tye.yaml index 455c06e..18e1c8c 100644 --- a/MicroserviceTemplate/src/tye.yaml +++ b/MicroserviceTemplate/src/tye.yaml @@ -38,7 +38,7 @@ services: - name: SA_PASSWORD value: M3rz0ug4!!!! - name: ACCEPT_EULA - value: Y + value: "Y" - name: Redis image: redis diff --git a/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj b/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj index db13d4d..8a9c069 100644 --- a/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj +++ b/MicroserviceTemplate/tests/ServiceName.Test/ServiceName.Test.csproj @@ -9,11 +9,11 @@ - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 6991fe6a690641ceae45766e551211adfce0c528 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Tue, 14 Jun 2022 13:49:39 +1000 Subject: [PATCH 13/56] [MicroserviceTemplate] Added SettingRepositoryTestWhenSettingsAreEmpty unit test Simplified Caching injection --- .../CQRS/Commands/SaveSettingsCommand.cs | 12 ++++---- .../CQRS/Queries/GetSettingsQuery.cs | 23 ++++++++++----- .../Common/Interfaces/ICachingService.cs | 8 ------ .../ServiceName.Core/ServiceName.Core.csproj | 1 + .../Caching/InMemoryCachingService.cs | 28 ------------------- .../Caching/RedisCachingService.cs | 20 ------------- .../ConfigureServices.cs | 15 ++++++++-- .../Repositories/SettingsRepositoryService.cs | 2 +- .../ServiceName.Infrastructure.csproj | 1 + .../ServiceName.Test/Helpers/TestHelper.cs | 22 +++++++++++---- .../tests/ServiceName.Test/RepositoryTests.cs | 27 +++++++++++++++--- 11 files changed, 79 insertions(+), 80 deletions(-) delete mode 100644 MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ICachingService.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Caching/InMemoryCachingService.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Caching/RedisCachingService.cs diff --git a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs index 7cf8851..59dd138 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs @@ -1,4 +1,6 @@ -using MediatR; +using System.Text.Json; +using MediatR; +using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Configuration; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; @@ -15,19 +17,19 @@ public class CreateTodoListCommandHandler : IRequestHandler _settingsRepository; IConfiguration _configuration; - ICachingService _cachingService; + IDistributedCache _cache; - public CreateTodoListCommandHandler(IRepositoryService settingsRepository, IConfiguration configuration, ICachingService cachingService) + public CreateTodoListCommandHandler(IRepositoryService settingsRepository, IConfiguration configuration, IDistributedCache cache) { _settingsRepository = settingsRepository; _configuration = configuration; - _cachingService = cachingService; + _cache = cache; } public async Task Handle(SaveSettingsCommandRequest request, CancellationToken cancellationToken) { var result = await _settingsRepository.SaveAsync(request.TenantId, request.Settings); - _cachingService.Set(request.TenantId.ToString(), request.Settings); + await _cache.SetStringAsync(request.TenantId.ToString(), JsonSerializer.Serialize(request.Settings)); return result; } } diff --git a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs index ed6601f..e1b5cda 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs @@ -1,4 +1,6 @@ -using MediatR; +using System.Text.Json; +using MediatR; +using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Configuration; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; @@ -13,24 +15,31 @@ public class GetSettingsQueryHandler : IRequestHandler _settingsRepository; IConfiguration _configuration; - ICachingService _cachingService; + IDistributedCache _cache; - public GetSettingsQueryHandler(IRepositoryService settingsRepository, IConfiguration configuration, ICachingService cachingService) + public GetSettingsQueryHandler(IRepositoryService settingsRepository, IConfiguration configuration, IDistributedCache cache) { _settingsRepository = settingsRepository; _configuration = configuration; - _cachingService = cachingService; + _cache = cache; } public async Task Handle(GetSettingsQueryRequest request, CancellationToken cancellationToken) { - var cachedSettings = _cachingService.Get(request.TenantId.ToString()); - if (cachedSettings == null) + Settings cachedSettings; + + var cachedSettingsJson = await _cache.GetStringAsync(request.TenantId.ToString()); + + if (string.IsNullOrEmpty(cachedSettingsJson)) { var settings = await _settingsRepository.GetByIdAsync(request.TenantId); - _cachingService.Set(request.TenantId.ToString(), settings); + await _cache.SetStringAsync(request.TenantId.ToString(), JsonSerializer.Serialize(settings)); cachedSettings = settings; } + else + { + cachedSettings = JsonSerializer.Deserialize(cachedSettingsJson); + } return cachedSettings; } diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ICachingService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ICachingService.cs deleted file mode 100644 index e9b59c0..0000000 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/ICachingService.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ServiceName.Core.Common.Interfaces -{ - public interface ICachingService - { - T Get(string key); - void Set(string key, object value); - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/ServiceName.Core.csproj b/MicroserviceTemplate/src/ServiceName.Core/ServiceName.Core.csproj index ffa344c..4f68052 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/ServiceName.Core.csproj +++ b/MicroserviceTemplate/src/ServiceName.Core/ServiceName.Core.csproj @@ -9,6 +9,7 @@ + diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Caching/InMemoryCachingService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Caching/InMemoryCachingService.cs deleted file mode 100644 index a12d14b..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Caching/InMemoryCachingService.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Microsoft.Extensions.Caching.Memory; -using ServiceName.Core.Common.Interfaces; - -namespace ServiceName.Infrastructure.Caching -{ - /// - ///https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-6.0 - /// - public class InMemoryCachingService : ICachingService - { - private readonly IMemoryCache _memoryCache; - - public InMemoryCachingService() - { - _memoryCache = new MemoryCache(new MemoryCacheOptions()); - } - - public T Get(string key) - { - return (T)_memoryCache.Get(key); - } - - public void Set(string key, object value) - { - _memoryCache.Set(key, value); - } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Caching/RedisCachingService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Caching/RedisCachingService.cs deleted file mode 100644 index 47188e7..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Caching/RedisCachingService.cs +++ /dev/null @@ -1,20 +0,0 @@ -using ServiceName.Core.Common.Interfaces; - -namespace ServiceName.Infrastructure.Caching -{ - /// - /// https://github.com/redis/redis-om-dotnet - /// - public class RedisCachingService : ICachingService - { - public T Get(string key) - { - throw new NotImplementedException(); - } - - public void Set(string key, object value) - { - throw new NotImplementedException(); - } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs index 04eea6e..273fab4 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs @@ -3,12 +3,13 @@ using Amazon.DynamoDBv2.DataModel; using AWS.Logger; using AWS.Logger.SeriLog; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Caching.StackExchangeRedis; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Serilog; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; -using ServiceName.Infrastructure.Caching; using ServiceName.Infrastructure.Repositories; namespace ServiceName.Infrastructure @@ -22,12 +23,22 @@ public static IServiceCollection AddInfrastructureServices(this IServiceCollecti services.AddSingleton(GetCloudWatchLogger()); //services.AddSingleton(GetCloudSeqLogger()); - services.AddSingleton(); + services.AddSingleton(GetRedisCache()); services.AddSingleton(GetLocalDynamoDB()); return services; } + private static IDistributedCache GetRedisCache() + { + var cache = new RedisCache(new RedisCacheOptions + { + Configuration = "localhost:6379" + }); + + return cache; + } + private static ILogger GetCloudSeqLogger() { Log.Logger = new LoggerConfiguration() diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs index 2c864a2..cbee3dd 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs @@ -45,7 +45,7 @@ public async Task GetByIdAsync(Guid id) _logger.Information($"LocalDynamoDatabaseService GetByIdAsync {id} started"); var settingDbRecord = await _dynamoDBContext.LoadAsync(id.ToString()); - if (settingDbRecord == null) + if (settingDbRecord == null || string.IsNullOrEmpty(settingDbRecord.Settings)) { settingDbRecord = await SaveNewSettings(id, new Settings() { CategoryA = new SettingGroup() }); } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj b/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj index 5130397..a611a6b 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj @@ -10,6 +10,7 @@ + diff --git a/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs b/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs index 389437c..51bf6c1 100644 --- a/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs +++ b/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs @@ -4,13 +4,25 @@ namespace ServiceName.Test.Helpers { internal class TestHelper { - public static SettingDbRecord GetSettingsRecordObject() + public static SettingDbRecord GetDynamoDBRecord(string tenantId) { - return new SettingDbRecord() + switch (tenantId) { - TenantId = "53a13ec4-fde8-4087-8e2a-5fb6b1fbc062", - Settings = @"{""CategoryA"":{""IsSettingAEnabled"":true,""IsSettingBEnabled"":true}}" - }; + case "53a13ec4-fde8-4087-8e2a-5fb6b1fbc062": + return new SettingDbRecord() + { + TenantId = "53a13ec4-fde8-4087-8e2a-5fb6b1fbc062", + Settings = @"{""CategoryA"":{""IsSettingAEnabled"":true,""IsSettingBEnabled"":true}}" + }; + case "e2c92679-5309-438b-8efc-64054a7babc2": + return new SettingDbRecord() + { + TenantId = "e2c92679-5309-438b-8efc-64054a7babc2", + Settings = String.Empty + }; + default: + return null; + } } } } diff --git a/MicroserviceTemplate/tests/ServiceName.Test/RepositoryTests.cs b/MicroserviceTemplate/tests/ServiceName.Test/RepositoryTests.cs index 3bb875f..ccb9d2d 100644 --- a/MicroserviceTemplate/tests/ServiceName.Test/RepositoryTests.cs +++ b/MicroserviceTemplate/tests/ServiceName.Test/RepositoryTests.cs @@ -21,9 +21,11 @@ public RepositoryTests() [Fact] public async Task SettingRepositoryTestWhenTenantIdExists() { - _autoMocker.GetMock().Setup(x => x.LoadAsync("53a13ec4-fde8-4087-8e2a-5fb6b1fbc062", default)).ReturnsAsync(TestHelper.GetSettingsRecordObject()); + var tenantId = "53a13ec4-fde8-4087-8e2a-5fb6b1fbc062"; - var result = await _mockSettingsRepository.GetByIdAsync(Guid.Parse("53a13ec4-fde8-4087-8e2a-5fb6b1fbc062")); + _autoMocker.GetMock().Setup(x => x.LoadAsync(tenantId, default)).ReturnsAsync(TestHelper.GetDynamoDBRecord(tenantId)); + + var result = await _mockSettingsRepository.GetByIdAsync(Guid.Parse(tenantId)); Assert.NotNull(result.CategoryA); Assert.True(result.CategoryA.IsSettingAEnabled); @@ -33,13 +35,30 @@ public async Task SettingRepositoryTestWhenTenantIdExists() [Fact] public async Task SettingRepositoryTestWhenTenantIdDoesNotExist() { - _autoMocker.GetMock().Setup(x => x.LoadAsync("296b73d9-692a-42bf-9ccb-ff41ca256722", default)).ReturnsAsync((SettingDbRecord)null); + var tenantId = "296b73d9-692a-42bf-9ccb-ff41ca256722"; + + _autoMocker.GetMock().Setup(x => x.LoadAsync(tenantId, default)).ReturnsAsync(TestHelper.GetDynamoDBRecord(tenantId)); - var result = await _mockSettingsRepository.GetByIdAsync(Guid.Parse("296b73d9-692a-42bf-9ccb-ff41ca256722")); + var result = await _mockSettingsRepository.GetByIdAsync(Guid.Parse(tenantId)); Assert.NotNull(result.CategoryA); Assert.False(result.CategoryA.IsSettingAEnabled); Assert.False(result.CategoryA.IsSettingBEnabled); } + + + [Fact] + public async Task SettingRepositoryTestWhenSettingsAreEmpty() + { + var tenantId = "e2c92679-5309-438b-8efc-64054a7babc2"; + + _autoMocker.GetMock().Setup(x => x.LoadAsync(tenantId, default)).ReturnsAsync(TestHelper.GetDynamoDBRecord(tenantId)); + + var result = await _mockSettingsRepository.GetByIdAsync(Guid.Parse(tenantId)); + + Assert.NotNull(result.CategoryA); + Assert.False(result.CategoryA.IsSettingAEnabled); + Assert.False(result.CategoryA.IsSettingBEnabled); + } } } \ No newline at end of file From 8c1a2fdb254de8ae33e450cf6e598a2fd361bf0a Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Wed, 15 Jun 2022 10:18:42 +1000 Subject: [PATCH 14/56] [MicroserviceTemplate] Added API Versioning Support --- .../Extensions/EndpointExtensions.cs | 24 +++++++- .../Swagger/ConfigureSwaggerOptions.cs | 55 +++++++++++++++++++ .../Swagger/SwaggerDefaultValues.cs | 48 ++++++++++++++++ .../Extensions/SwaggerExtensions.cs | 30 +++++++--- .../Extensions/VersionExtension.cs | 21 +++++++ .../src/ServiceName.API/Program.cs | 13 +++-- .../ServiceName.API/ServiceName.API.csproj | 2 + 7 files changed, 177 insertions(+), 16 deletions(-) create mode 100644 MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs create mode 100644 MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/SwaggerDefaultValues.cs create mode 100644 MicroserviceTemplate/src/ServiceName.API/Extensions/VersionExtension.cs diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs index c812435..7cdce68 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs @@ -1,4 +1,5 @@ -using MediatR; +using Asp.Versioning.Conventions; +using MediatR; using Microsoft.AspNetCore.Mvc; using ServiceName.Core.CQRS.Commands; using ServiceName.Core.CQRS.Queries; @@ -10,8 +11,25 @@ public static class EndpointExtensions { public static void MapEndpoints(this WebApplication app) { - app.MapGet("/settings/{tenantId}", async (IMediator mediator, string tenantId) => await mediator.Send(new GetSettingsQueryRequest() { TenantId = Guid.Parse(tenantId) })); - app.MapPost("/settings/{tenantId}", async (IMediator mediator, [FromBody] Settings settings, string tenantId) => await mediator.Send(new SaveSettingsCommandRequest() { TenantId = Guid.Parse(tenantId), Settings = settings })); + // define a 'version set' that applies to an API group + var versionSet = app.NewApiVersionSet() + .HasApiVersion(1.0) + .HasApiVersion(2.0) + .ReportApiVersions() + .Build(); + + + app.MapGet("/settings/{tenantId}", async (IMediator mediator, string tenantId) => await mediator.Send(new GetSettingsQueryRequest() { TenantId = Guid.Parse(tenantId) })) + .WithApiVersionSet(versionSet) + .MapToApiVersion(1.0).HasApiVersion(1.0); + + app.MapGet("/settings/{tenantId}", async (string tenantId) => await Task.FromResult(tenantId)) + .WithApiVersionSet(versionSet) + .MapToApiVersion(2.0).HasApiVersion(2.0); + + app.MapPost("/settings/{tenantId}", async (IMediator mediator, [FromBody] Settings settings, string tenantId) => await mediator.Send(new SaveSettingsCommandRequest() { TenantId = Guid.Parse(tenantId), Settings = settings })) + .WithApiVersionSet(versionSet) + .MapToApiVersion(1.0).HasApiVersion(1.0); } } } diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs new file mode 100644 index 0000000..803f75d --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs @@ -0,0 +1,55 @@ +namespace ServiceName.API.Extensions.Swagger +{ + using Asp.Versioning.ApiExplorer; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Options; + using Microsoft.OpenApi.Models; + using Swashbuckle.AspNetCore.SwaggerGen; + using System; + + /// + /// Configures the Swagger generation options. + /// + /// This allows API versioning to define a Swagger document per API version after the + /// service has been resolved from the service container. + public class ConfigureSwaggerOptions : IConfigureOptions + { + readonly IApiVersionDescriptionProvider provider; + + /// + /// Initializes a new instance of the class. + /// + /// The provider used to generate Swagger documents. + public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => this.provider = provider; + + /// + public void Configure(SwaggerGenOptions options) + { + // add a swagger document for each discovered API version + // note: you might choose to skip or document deprecated API versions differently + foreach (var description in provider.ApiVersionDescriptions) + { + options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); + } + } + + static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) + { + var info = new OpenApiInfo() + { + Title = "Sample API", + Version = description.ApiVersion.ToString(), + Description = "A sample application with Swagger, Swashbuckle, and API versioning.", + Contact = new OpenApiContact() { Name = "Leandro", Email = "leandro@somewhere.com" }, + License = new OpenApiLicense() { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") } + }; + + if (description.IsDeprecated) + { + info.Description += " This API version has been deprecated."; + } + + return info; + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/SwaggerDefaultValues.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/SwaggerDefaultValues.cs new file mode 100644 index 0000000..af07330 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/SwaggerDefaultValues.cs @@ -0,0 +1,48 @@ +namespace ServiceName.API.Extensions.Swagger +{ + using Microsoft.AspNetCore.Mvc.ApiExplorer; + using Microsoft.OpenApi.Any; + using Microsoft.OpenApi.Models; + using Swashbuckle.AspNetCore.SwaggerGen; + using System.Linq; + + /// + /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter. + /// + /// This is only required due to bugs in the . + /// Once they are fixed and published, this class can be removed. + public class SwaggerDefaultValues : IOperationFilter + { + /// + /// Applies the filter to the specified operation using the given context. + /// + /// The operation to apply the filter to. + /// The current operation filter context. + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + var apiDescription = context.ApiDescription; + operation.Deprecated |= apiDescription.IsDeprecated(); + + if (operation.Parameters == null) + return; + + // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412 + // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413 + foreach (var parameter in operation.Parameters) + { + var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); + if (parameter.Description == null) + { + parameter.Description = description.ModelMetadata?.Description; + } + + if (parameter.Schema.Default == null && description.DefaultValue != null) + { + parameter.Schema.Default = new OpenApiString(description.DefaultValue.ToString()); + } + + parameter.Required |= description.IsRequired; + } + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs index 96c6df0..74693be 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs @@ -1,16 +1,21 @@ -using Microsoft.OpenApi.Models; +using Microsoft.Extensions.Options; +using Microsoft.OpenApi.Models; +using ServiceName.API.Extensions.Swagger; +using Swashbuckle.AspNetCore.SwaggerGen; namespace ServiceName.API.Extensions { public static class SwaggerExtensions { - public static void ConfigureSwaggerServices(this IServiceCollection services) - { - services.AddSwaggerGen(c => + public static void AddSwaggerSupport(this IServiceCollection services) + { + services.AddTransient, ConfigureSwaggerOptions>(); + + services.AddSwaggerGen(options => { - c.SwaggerDoc("v1", new OpenApiInfo { Title = "Microservice API", Version = "v1" }); + options.OperationFilter(); - c.AddSecurityDefinition("basic", new OpenApiSecurityScheme + options.AddSecurityDefinition("basic", new OpenApiSecurityScheme { Name = "Authorization", Type = SecuritySchemeType.Http, @@ -19,7 +24,7 @@ public static void ConfigureSwaggerServices(this IServiceCollection services) Description = "Basic Authorization header using the Bearer scheme." }); - c.AddSecurityRequirement(new OpenApiSecurityRequirement + options.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme @@ -41,8 +46,17 @@ public static void ConfigureSwaggerUI(this WebApplication app) app.UseSwagger(); app.UseSwaggerUI(options => { - options.SwaggerEndpoint("./swagger/v1/swagger.json", "v1"); options.RoutePrefix = string.Empty; + + var descriptions = app.DescribeApiVersions(); + + // build a swagger endpoint for each discovered API version + foreach (var description in descriptions) + { + var url = $"./swagger/{description.GroupName}/swagger.json"; + var name = description.GroupName.ToUpperInvariant(); + options.SwaggerEndpoint(url, name); + } }); } } diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/VersionExtension.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/VersionExtension.cs new file mode 100644 index 0000000..3ceea13 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/VersionExtension.cs @@ -0,0 +1,21 @@ +namespace ServiceName.API.Extensions +{ + public static class VersionExtension + { + public static void AddApiVersioningSupport(this IServiceCollection services) + { + services.AddApiVersioning() + .AddApiExplorer( + options => + { + // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service + // note: the specified format code will format the version as "'v'major[.minor][-status]" + options.GroupNameFormat = "'v'VVV"; + + // note: this option is only necessary when versioning by url segment. the SubstitutionFormat + // can also be used to control the format of the API version in route templates + options.SubstituteApiVersionInUrl = true; + }); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.API/Program.cs b/MicroserviceTemplate/src/ServiceName.API/Program.cs index a3a7eee..abbcfb8 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Program.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Program.cs @@ -15,15 +15,20 @@ builder.Services.AddEndpointsApiExplorer(); -//Configure Swagger with Bearer Token authentication -builder.Services.ConfigureSwaggerServices(); +builder.Services.AddApiVersioningSupport(); + +builder.Services.AddSwaggerSupport(); -//Clean Architecute: Service Injection builder.Services.AddInfrastructureServices(builder.Configuration); + builder.Services.AddApplicationServices(); var app = builder.Build(); +app.MapControllers(); + +app.MapEndpoints(); + if (app.Environment.IsDevelopment()) { app.ConfigureSwaggerUI(); @@ -33,8 +38,6 @@ app.UseAuthorization(); app.UseHttpsRedirection(); -app.MapControllers(); -app.MapEndpoints(); app.Run(); diff --git a/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj b/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj index d11d15f..56d6817 100644 --- a/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj +++ b/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj @@ -12,6 +12,8 @@ + + From c0b1d9baf94f78d64c3fd789075f0746de78973a Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Wed, 15 Jun 2022 13:02:56 +1000 Subject: [PATCH 15/56] [MicroserviceTemplate] removed unnecessary MapToApiVersion method --- .../src/ServiceName.API/Extensions/EndpointExtensions.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs index 7cdce68..9ae63a4 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs @@ -21,15 +21,15 @@ public static void MapEndpoints(this WebApplication app) app.MapGet("/settings/{tenantId}", async (IMediator mediator, string tenantId) => await mediator.Send(new GetSettingsQueryRequest() { TenantId = Guid.Parse(tenantId) })) .WithApiVersionSet(versionSet) - .MapToApiVersion(1.0).HasApiVersion(1.0); + .MapToApiVersion(1.0); app.MapGet("/settings/{tenantId}", async (string tenantId) => await Task.FromResult(tenantId)) .WithApiVersionSet(versionSet) - .MapToApiVersion(2.0).HasApiVersion(2.0); + .MapToApiVersion(2.0); app.MapPost("/settings/{tenantId}", async (IMediator mediator, [FromBody] Settings settings, string tenantId) => await mediator.Send(new SaveSettingsCommandRequest() { TenantId = Guid.Parse(tenantId), Settings = settings })) .WithApiVersionSet(versionSet) - .MapToApiVersion(1.0).HasApiVersion(1.0); + .MapToApiVersion(1.0); } } } From a1a2771ef89167fab65fdbf99510c298cc329c4a Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Wed, 15 Jun 2022 14:37:59 +1000 Subject: [PATCH 16/56] [MicroserviceTemplate] Added HealthCheck Support --- .../HealthCheck/ServiceHealthCheck.cs | 22 ++++++++ .../Extensions/HealthCheckExtensions.cs | 50 +++++++++++++++++++ .../src/ServiceName.API/Program.cs | 4 ++ .../ServiceName.API/ServiceName.API.csproj | 9 +++- 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/ServiceHealthCheck.cs create mode 100644 MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/ServiceHealthCheck.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/ServiceHealthCheck.cs new file mode 100644 index 0000000..05f15d7 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/ServiceHealthCheck.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace ServiceName.API.Extensions.HealthCheck +{ + public class CustomServiceHealthCheck : IHealthCheck + { + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + var isHealthy = true; + + if (isHealthy) + { + return Task.FromResult( + HealthCheckResult.Healthy("A healthy result.")); + } + + return Task.FromResult( + new HealthCheckResult( + context.Registration.FailureStatus, "An unhealthy result.")); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs new file mode 100644 index 0000000..8fff9a2 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs @@ -0,0 +1,50 @@ +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using ServiceName.API.Extensions.HealthCheck; + +namespace ServiceName.API.Extensions +{ + /// + /// https://docs.steeltoe.io/api/v3/management/health.html + /// https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-6.0 + /// https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/monitor-app-health + /// https://blog.zhaytam.com/2020/04/30/health-checks-aspnetcore/ + + //AspNetCore.HealthChecks.UI which adds the UI. + //AspNetCore.HealthChecks.UI.Client which turns our old response(e.g.Healthy) into a more detailed response. + //AspNetCore.HealthChecks.UI.InMemory.Storage which saves the results in memory for the UI to use. + + /// + public static class HealthCheckExtensions + { + public static void AddHealthCheckSupport(this IServiceCollection services) + { + services.AddHealthChecks() + .AddCheck("AlwaysHealthy", () => HealthCheckResult.Healthy()) + .AddCheck("ServiceHealthCheck") + .AddSqlServer("") + .AddRedis(""); + //.AddDynamoDb(""); + + services.AddHealthChecksUI(s => + { + s.AddHealthCheckEndpoint("ServiceName", "/health"); + }).AddInMemoryStorage(); + } + + public static void ConfigureHealthCheck(this WebApplication app) + { + app.UseHealthChecksUI(config => config.UIPath = "/health-ui"); + + app.MapHealthChecksUI(); + + app.MapHealthChecks("/health", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse, + AllowCachingResponses = false + }); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.API/Program.cs b/MicroserviceTemplate/src/ServiceName.API/Program.cs index abbcfb8..1062b3a 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Program.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Program.cs @@ -23,12 +23,16 @@ builder.Services.AddApplicationServices(); +builder.Services.AddHealthCheckSupport(); + var app = builder.Build(); app.MapControllers(); app.MapEndpoints(); +app.ConfigureHealthCheck(); + if (app.Environment.IsDevelopment()) { app.ConfigureSwaggerUI(); diff --git a/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj b/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj index 56d6817..59215f3 100644 --- a/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj +++ b/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj @@ -11,9 +11,16 @@ true - + + + + + + + + From de00cdf78c6de631d3573026f0ccc94bb61a7554 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Thu, 16 Jun 2022 13:39:21 +1000 Subject: [PATCH 17/56] [MicroserviceTemplate] Fixed HealthChecks for SQL Server, Redis and DynamoDB Moved hardcoded configurations to appsettings.json --- .../HealthCheck/DynamoDbHealthCheck.cs | 52 ++++++++++++ .../HealthCheck/ServiceHealthCheck.cs | 22 ----- .../Extensions/HealthCheckExtensions.cs | 12 ++- .../src/ServiceName.API/Program.cs | 14 ++-- .../ServiceName.API/ServiceName.API.csproj | 1 - .../appsettings.Development.json | 30 ++++++- .../src/ServiceName.API/appsettings.json | 29 ++++++- .../ConfigureServices.cs | 80 +++++++++++++++---- 8 files changed, 185 insertions(+), 55 deletions(-) create mode 100644 MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/ServiceHealthCheck.cs diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs new file mode 100644 index 0000000..75e562e --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs @@ -0,0 +1,52 @@ +using Amazon; +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.DocumentModel; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace ServiceName.API.Extensions.HealthCheck +{ + public class DynamoDbHealthCheck : IHealthCheck + { + readonly IConfiguration _configuration; + + public DynamoDbHealthCheck(IConfiguration configuration) + { + _configuration = configuration; + } + + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + try + { + var accessKey = _configuration["ModuleConfiguration:ConnectionStrings:DynamoDb:AccessKey"]; + var secretKey = _configuration["ModuleConfiguration:ConnectionStrings:DynamoDb:SecretKey"]; + var regionEndpoint = RegionEndpoint.GetBySystemName(_configuration["ModuleConfiguration:ConnectionStrings:DynamoDb:RegionEndpoint"]); + var tableName = _configuration["ModuleConfiguration:ConnectionStrings:DynamoDb:TableName"]; + var localTestEndpoint = _configuration["ModuleConfiguration:ConnectionStrings:DynamoDb:LocalTestEndpoint"]; + + AmazonDynamoDBConfig clientConfig = new() + { + RegionEndpoint = regionEndpoint, + }; + + if (!string.IsNullOrEmpty(localTestEndpoint)) + { + clientConfig.UseHttp = true; + clientConfig.ServiceURL = localTestEndpoint; + } + + var amazonDynamoDBClient = new AmazonDynamoDBClient(accessKey, secretKey, clientConfig); + + Table.LoadTable(amazonDynamoDBClient, tableName); + + return Task.FromResult(HealthCheckResult.Healthy($"Table {tableName} exists.")); + } + catch (Exception ex) + { + return Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus, ex.Message)); + } + + + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/ServiceHealthCheck.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/ServiceHealthCheck.cs deleted file mode 100644 index 05f15d7..0000000 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/ServiceHealthCheck.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.Extensions.Diagnostics.HealthChecks; - -namespace ServiceName.API.Extensions.HealthCheck -{ - public class CustomServiceHealthCheck : IHealthCheck - { - public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) - { - var isHealthy = true; - - if (isHealthy) - { - return Task.FromResult( - HealthCheckResult.Healthy("A healthy result.")); - } - - return Task.FromResult( - new HealthCheckResult( - context.Registration.FailureStatus, "An unhealthy result.")); - } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs index 8fff9a2..06e26de 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs @@ -1,6 +1,5 @@ using HealthChecks.UI.Client; using Microsoft.AspNetCore.Diagnostics.HealthChecks; -using Microsoft.Extensions.Diagnostics.HealthChecks; using ServiceName.API.Extensions.HealthCheck; namespace ServiceName.API.Extensions @@ -18,14 +17,13 @@ namespace ServiceName.API.Extensions /// public static class HealthCheckExtensions { - public static void AddHealthCheckSupport(this IServiceCollection services) + + public static void AddHealthCheckSupport(this IServiceCollection services, ConfigurationManager configurationManager) { services.AddHealthChecks() - .AddCheck("AlwaysHealthy", () => HealthCheckResult.Healthy()) - .AddCheck("ServiceHealthCheck") - .AddSqlServer("") - .AddRedis(""); - //.AddDynamoDb(""); + .AddCheck("dynamodb") + .AddSqlServer(configurationManager["ModuleConfiguration:ConnectionStrings:SqlServer"]) + .AddRedis(configurationManager["ModuleConfiguration:ConnectionStrings:Redis"]); services.AddHealthChecksUI(s => { diff --git a/MicroserviceTemplate/src/ServiceName.API/Program.cs b/MicroserviceTemplate/src/ServiceName.API/Program.cs index 1062b3a..61a9035 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Program.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Program.cs @@ -3,7 +3,6 @@ using ServiceName.Infrastructure; var builder = WebApplication.CreateBuilder(args); -builder.Logging.ClearProviders(); // Add services to the container. builder.Services.AddControllers(); @@ -11,19 +10,20 @@ // Add AWS Lambda support. When application is run in Lambda Kestrel is swapped out as the web server with Amazon.Lambda.AspNetCoreServer. This // package will act as the webserver translating request and responses between the Lambda event source and ASP.NET Core. // https://aws.amazon.com/blogs/compute/introducing-the-net-6-runtime-for-aws-lambda/ + builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi); builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddApiVersioningSupport(); - -builder.Services.AddSwaggerSupport(); - builder.Services.AddInfrastructureServices(builder.Configuration); builder.Services.AddApplicationServices(); -builder.Services.AddHealthCheckSupport(); +builder.Services.AddApiVersioningSupport(); + +builder.Services.AddSwaggerSupport(); + +builder.Services.AddHealthCheckSupport(builder.Configuration); var app = builder.Build(); @@ -33,7 +33,7 @@ app.ConfigureHealthCheck(); -if (app.Environment.IsDevelopment()) +if (bool.Parse(builder.Configuration["ModuleConfiguration:IsSwaggerUIEnabled"])) { app.ConfigureSwaggerUI(); } diff --git a/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj b/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj index 59215f3..37fd036 100644 --- a/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj +++ b/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj @@ -14,7 +14,6 @@ - diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json index 5220dde..88e51a2 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json @@ -5,5 +5,33 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "ModuleConfiguration": { + "IsSwaggerUIEnabled": true, + "ConnectionStrings": { + "SqlServer": "Data Source=host.docker.internal,1433;Initial Catalog=DBName;User ID=sa;Password=M3rz0ug4!!!!", + "Redis": "host.docker.internal:6379", + "DynamoDb": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "TableName": "ServiceName_Setting", + "LocalTestEndpoint": "http://host.docker.internal:8000" + } + }, + "Logging": { + "Sink": "Seq", + "Seq": { + "ServerUrl": "http://localhost:5341", + "ApiKey": "" + }, + "AwsCloudWatch": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "LogGroupName": "/LocalStack/Microservice/Logs", + "LocalTestEndpoint": "http://host.docker.internal:4566" + } + } + } } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.json index b2a9133..98f62c8 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.json @@ -6,5 +6,32 @@ } }, "AllowedHosts": "*", - "MyConfig": "someconfig" + "ModuleConfiguration": { + "IsSwaggerUIEnabled": true, + "ConnectionStrings": { + "SqlServer": "Data Source=host.docker.internal,1433;Initial Catalog=DBName;User ID=sa;Password=M3rz0ug4!!!!", + "Redis": "host.docker.internal:6379", + "DynamoDb": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "TableName": "ServiceName_Setting", + "LocalTestEndpoint": "http://host.docker.internal:8000" + } + }, + "Logging": { + "Sink": "AwsCloudWatch", + "Seq": { + "ServerUrl": "http://localhost:5341", + "ApiKey": "" + }, + "AwsCloudWatch": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "LogGroupName": "/LocalStack/Microservice/Logs", + "LocalTestEndpoint": "http://host.docker.internal:4566" + } + } + } } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs index 273fab4..924b0ed 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs @@ -1,6 +1,7 @@ using Amazon; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.DataModel; +using Amazon.Runtime; using AWS.Logger; using AWS.Logger.SeriLog; using Microsoft.Extensions.Caching.Distributed; @@ -16,15 +17,29 @@ namespace ServiceName.Infrastructure { public static class ConfigureServices { + static ConfigurationManager _configurationManager; + public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, ConfigurationManager configurationManager) { + _configurationManager = configurationManager; + services.AddSingleton(GetConfiguration(configurationManager)); services.AddScoped, SettingsRepositoryService>(); - services.AddSingleton(GetCloudWatchLogger()); - //services.AddSingleton(GetCloudSeqLogger()); + + var loggingSink = _configurationManager["ModuleConfiguration:Logging:Sink"]; + + switch (loggingSink) + { + case "Seq": + services.AddSingleton(GetCloudSeqLogger()); + break; + default: + services.AddSingleton(GetCloudWatchLogger()); + break; + } services.AddSingleton(GetRedisCache()); - services.AddSingleton(GetLocalDynamoDB()); + services.AddSingleton(GetDynamoDBContext()); return services; } @@ -33,7 +48,7 @@ private static IDistributedCache GetRedisCache() { var cache = new RedisCache(new RedisCacheOptions { - Configuration = "localhost:6379" + Configuration = _configurationManager["ModuleConfiguration:ConnectionStrings:Redis"] }); return cache; @@ -41,9 +56,13 @@ private static IDistributedCache GetRedisCache() private static ILogger GetCloudSeqLogger() { + var serverUrl = _configurationManager["ModuleConfiguration:Logging:Seq:ServerUrl"]; + var apiKey = _configurationManager["ModuleConfiguration:Logging:Seq:ApiKey"]; + Log.Logger = new LoggerConfiguration() .WriteTo.Console() - .WriteTo.Seq("http://localhost:5341") + .WriteTo.Seq(serverUrl: serverUrl, + apiKey: apiKey) .CreateLogger(); return Log.Logger; @@ -51,17 +70,37 @@ private static ILogger GetCloudSeqLogger() private static ILogger GetCloudWatchLogger() { - AWSLoggerConfig configuration = new("/LocalStack/Microservice/Logs") + var accessKey = _configurationManager["ModuleConfiguration:Logging:AwsCloudWatch:AccessKey"]; + var secretKey = _configurationManager["ModuleConfiguration:Logging:AwsCloudWatch:SecretKey"]; + var regionEndpoint = _configurationManager["ModuleConfiguration:Logging:AwsCloudWatch:RegionEndpoint"]; + var localTestEndpoint = _configurationManager["ModuleConfiguration:Logging:AwsCloudWatch:LocalTestEndpoint"]; + var logGroupName = _configurationManager["ModuleConfiguration:Logging:AwsCloudWatch:LogGroupName"]; + + AWSLoggerConfig configuration; + + //If logGroupName is empty it uses the default AWS Lambad Log Group + if (!string.IsNullOrEmpty(logGroupName)) { - Region = "ap-southeast-2", - ServiceUrl = "http://localhost:4566" - }; + configuration = new(logGroupName) + { + Region = regionEndpoint, + Credentials = new BasicAWSCredentials(accessKey, secretKey) + }; + + //used for local testing only + if (!string.IsNullOrEmpty(localTestEndpoint)) + { + configuration.ServiceUrl = localTestEndpoint; + } - Log.Logger = new LoggerConfiguration().WriteTo.AWSSeriLog(configuration) + return new LoggerConfiguration().WriteTo.AWSSeriLog(configuration) .WriteTo.Console() .CreateLogger(); + } - return Log.Logger; + return new LoggerConfiguration().WriteTo.AWSSeriLog() + .WriteTo.Console() + .CreateLogger(); } private static IConfiguration GetConfiguration(ConfigurationManager configurationManager) @@ -76,16 +115,25 @@ private static IConfiguration GetConfiguration(ConfigurationManager configuratio return configurationManager; } - private static DynamoDBContext GetLocalDynamoDB() + private static DynamoDBContext GetDynamoDBContext() { + var accessKey = _configurationManager["ModuleConfiguration:ConnectionStrings:DynamoDb:AccessKey"]; + var secretKey = _configurationManager["ModuleConfiguration:ConnectionStrings:DynamoDb:SecretKey"]; + var regionEndpoint = RegionEndpoint.GetBySystemName(_configurationManager["ModuleConfiguration:ConnectionStrings:DynamoDb:RegionEndpoint"]); + var localTestEndpoint = _configurationManager["ModuleConfiguration:ConnectionStrings:DynamoDb:LocalTestEndpoint"]; + AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig { - RegionEndpoint = RegionEndpoint.GetBySystemName("ap-southeast-2"), - UseHttp = true, - ServiceURL = "http://localhost:8000" + RegionEndpoint = regionEndpoint }; - var amazonDynamoDBClient = new AmazonDynamoDBClient("test", "test", clientConfig); + if (!string.IsNullOrEmpty(localTestEndpoint)) + { + clientConfig.UseHttp = true; + clientConfig.ServiceURL = localTestEndpoint; + } + + var amazonDynamoDBClient = new AmazonDynamoDBClient(accessKey, secretKey, clientConfig); var dynamoDBContextConfig = new DynamoDBContextConfig() { ConsistentRead = true }; return new DynamoDBContext(amazonDynamoDBClient, dynamoDBContextConfig); } From bf693ee831ac2fcfb8b0b89c9e658989720680c8 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Fri, 17 Jun 2022 14:28:52 +1000 Subject: [PATCH 18/56] [MicroserviceTemplate] Added JWT with assymetric encryption using Amazon Key Management Service --- .../ServiceName.API/ServiceName.API.csproj | 1 - .../appsettings.Development.json | 29 ++++- .../Common/Behaviours/LoggingBehaviour.cs | 2 +- .../Interfaces/IAuthenticationService.cs | 8 -- .../Interfaces/IJwtAuthenticationService.cs | 10 ++ .../ServiceName.Core/Model/ModuleIdentity.cs | 9 ++ .../Authentication/AssymetricKmsJwtService.cs | 118 ++++++++++++++++++ .../Authentication/JWT/CustomJwtHeader.cs | 12 ++ .../Authentication/JWT/CustomJwtPayload.cs | 27 ++++ ...s => SymmetricJwtAuthenticationService.cs} | 17 +-- .../ConfigureServices.cs | 47 ++++++- .../Repositories/SettingsRepositoryService.cs | 4 +- .../ServiceName.Infrastructure.csproj | 12 +- .../tests/ServiceName.Test/JwtTests.cs | 64 ++++++++++ 14 files changed, 319 insertions(+), 41 deletions(-) delete mode 100644 MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IAuthenticationService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IJwtAuthenticationService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Model/ModuleIdentity.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/JWT/CustomJwtHeader.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/JWT/CustomJwtPayload.cs rename MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/{MockJwtAuthenticationService.cs => SymmetricJwtAuthenticationService.cs} (78%) create mode 100644 MicroserviceTemplate/tests/ServiceName.Test/JwtTests.cs diff --git a/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj b/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj index 37fd036..ea2cf05 100644 --- a/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj +++ b/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj @@ -19,7 +19,6 @@ - diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json index 88e51a2..6dcb120 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json @@ -6,7 +6,8 @@ } }, "AllowedHosts": "*", - "ModuleConfiguration": { + "ModuleConfiguration": + { "IsSwaggerUIEnabled": true, "ConnectionStrings": { "SqlServer": "Data Source=host.docker.internal,1433;Initial Catalog=DBName;User ID=sa;Password=M3rz0ug4!!!!", @@ -19,19 +20,39 @@ "LocalTestEndpoint": "http://host.docker.internal:8000" } }, - "Logging": { + "Logging": + { "Sink": "Seq", - "Seq": { + "Seq": + { "ServerUrl": "http://localhost:5341", "ApiKey": "" }, - "AwsCloudWatch": { + "AwsCloudWatch": + { "AccessKey": "test", "SecretKey": "test", "RegionEndpoint": "ap-southeast-2", "LogGroupName": "/LocalStack/Microservice/Logs", "LocalTestEndpoint": "http://host.docker.internal:4566" } + }, + "AwsServices": + { + "Kms": + { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "eu-west-2", + "LocalTestEndpoint": "http://localhost:52002" + } + }, + "Jwt": + { + "SigningKeyId": "6732c7ca-6ec9-4b96-9711-fd1c7d637c8e", + "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA56tfR6w3YJpbH5XZ6Ze2kB2evnUpbyZJiTLPKSc/VeA46m09lVB7bJRp0pKX2LusT2pccrVe5AYtbnikKqhOQWUdjLJnSONPNpd4yjEseqPblsXicA+xdP+Fk2W0yDxOc79LUAywgjV8JqNbbtVbhzqVPOLalJYnPEAVa3NQV138dnU7NzxbAjPjXINi7BBZ2OLRuocJRMfe16AUiQtH8MaWfRnnRRwdCBLJCXnZy+0hVc701SrVoTS+CA8RfGTCnzutx9MXW7t4SCEjZH0MSfhSZbKggPfi36HeUdClacgD6L0+FhSBKzd8kOC06CDf5WM9oV/XtWVXEWWGPDHv8wIDAQAB", + "Issuer": "Issuer", + "Audience": "Audience" } } } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs index dc1b1e9..74b4715 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs @@ -7,7 +7,7 @@ namespace ServiceName.Core.Common.Behaviours { public class LoggingBehaviour : IPipelineBehavior where TRequest : IRequest { - ILogger _logger; + readonly ILogger _logger; private readonly Stopwatch _timer; public LoggingBehaviour(ILogger logger) diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IAuthenticationService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IAuthenticationService.cs deleted file mode 100644 index 923e6c2..0000000 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IAuthenticationService.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ServiceName.Core.Common.Interfaces -{ - public interface IAuthenticationService - { - string GenerateToken(int userId); - bool ValidateToken(string token); - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IJwtAuthenticationService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IJwtAuthenticationService.cs new file mode 100644 index 0000000..7879e7f --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IJwtAuthenticationService.cs @@ -0,0 +1,10 @@ +using ServiceName.Core.Model; + +namespace ServiceName.Core.Common.Interfaces +{ + public interface IJwtAuthenticationService + { + Task GenerateTokenAsync(ModuleIdentity identity, int lifetimeSeconds, string issuer, string audience); + Task ValidateTokenAsync(string token); + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Model/ModuleIdentity.cs b/MicroserviceTemplate/src/ServiceName.Core/Model/ModuleIdentity.cs new file mode 100644 index 0000000..170fbd3 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Model/ModuleIdentity.cs @@ -0,0 +1,9 @@ +namespace ServiceName.Core.Model +{ + public class ModuleIdentity + { + public string UserGuid { get; set; } + public string InstanceGuid { get; set; } + public string UserName { get; set; } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs new file mode 100644 index 0000000..8a09046 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs @@ -0,0 +1,118 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using Amazon; +using Amazon.KeyManagementService; +using Amazon.KeyManagementService.Model; +using Microsoft.Extensions.Configuration; +using Microsoft.IdentityModel.Tokens; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Serilog; +using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Model; +using ServiceName.Infrastructure.Authentication.JWT; + +namespace ServiceName.Infrastructure.Authentication +{ + /// + /// + /// aws --endpoint-url=http://localhost:52002 kms --region ap-southeast-2 create-key --key-spec RSA_2048 --key-usage SIGN_VERIFY + /// aws --endpoint-url=http://localhost:52002 kms --region ap-southeast-2 list-keys + /// aws --endpoint-url=http://localhost:52002 kms --region ap-southeast-2 get-public-key --key-id 6732c7ca-6ec9-4b96-9711-fd1c7d637c8e + /// + public class AssymetricKmsJwtService : IJwtAuthenticationService + { + readonly IConfiguration _configuration; + readonly IAmazonKeyManagementService _amazonKms; + readonly ILogger _logger; + + public AssymetricKmsJwtService(IConfiguration configuration, IAmazonKeyManagementService amazonKms, ILogger logger) + { + _configuration = configuration; + _amazonKms = amazonKms; + _logger = logger; + } + + public async Task GenerateTokenAsync(ModuleIdentity identity, int lifetimeSeconds, string issuer, string audience) + { + var signingKeyId = _configuration["ModuleConfiguration:Jwt:SigningKeyId"]; + + if (string.IsNullOrWhiteSpace(signingKeyId)) + { + throw new InvalidOperationException("The amazonKmsSigningKeyId is not defined."); + } + + var header = Base64UrlEncoder.Encode(JsonSerializer.Serialize(new CustomJwtHeader() + { + Algorithm = "RS256", + KeyId = signingKeyId + })); + + var time = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + var payload = Base64UrlEncoder.Encode(JsonSerializer.Serialize(new CustomJwtPayload() + { + IssuedAt = time, + ExpirationTime = time + lifetimeSeconds, + Issuer = issuer, + Audience = audience, + Subject = identity.UserGuid, + TenantId = identity.InstanceGuid, + Username = identity.UserName + })); + + string signature; + + using (var ms = new MemoryStream(Encoding.UTF8.GetBytes($"{header}.{payload}"))) + { + var request = new SignRequest + { + KeyId = signingKeyId, + SigningAlgorithm = SigningAlgorithmSpec.RSASSA_PKCS1_V1_5_SHA_256, + MessageType = MessageType.RAW, + Message = ms + }; + + var signResult = await _amazonKms.SignAsync(request); + signature = Base64UrlEncoder.Encode(signResult.Signature.ToArray()); + } + + return $"{header}.{payload}.{signature}"; + } + + public async Task ValidateTokenAsync(string token) + { + var publicKey = Convert.FromBase64String(_configuration["ModuleConfiguration:Jwt:PublicKey"]); + var asymmetricKeyParameter = PublicKeyFactory.CreateKey(publicKey); + var rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter; + var rsaParameters = new RSAParameters + { + Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned(), + Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned() + }; + var rsaSecurityKey = new RsaSecurityKey(rsaParameters); + + var issuer = _configuration["ModuleConfiguration:Jwt:Issuer"]; + var audience = _configuration["ModuleConfiguration:Jwt:Audience"]; + var tokenHandler = new JwtSecurityTokenHandler(); + try + { + tokenHandler.ValidateToken(token, new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + ValidateIssuer = true, + ValidateAudience = true, + ValidIssuer = issuer, + ValidAudience = audience, + IssuerSigningKey = rsaSecurityKey + }, out SecurityToken validatedToken); + } + catch + { + return await Task.FromResult(false); + } + return await Task.FromResult(true); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/JWT/CustomJwtHeader.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/JWT/CustomJwtHeader.cs new file mode 100644 index 0000000..dbc4c89 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/JWT/CustomJwtHeader.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace ServiceName.Infrastructure.Authentication.JWT +{ + internal class CustomJwtHeader + { + [JsonPropertyName("kid")] + public string KeyId { get; set; } + [JsonPropertyName("alg")] + public string Algorithm { get; set; } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/JWT/CustomJwtPayload.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/JWT/CustomJwtPayload.cs new file mode 100644 index 0000000..8d8cc7b --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/JWT/CustomJwtPayload.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace ServiceName.Infrastructure.Authentication.JWT +{ + internal class CustomJwtPayload + { + [JsonPropertyName("exp")] + public long ExpirationTime { get; set; } + [JsonPropertyName("iat")] + public long IssuedAt { get; set; } + [JsonPropertyName("iss")] + public string Issuer { get; set; } + [JsonPropertyName("aud")] + public string Audience { get; set; } + [JsonPropertyName("sub")] + public string Subject { get; set; } + [JsonPropertyName("custom:tenantId")] + public string TenantId { get; set; } + [JsonPropertyName("custom:username")] + public string Username { get; set; } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/MockJwtAuthenticationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/SymmetricJwtAuthenticationService.cs similarity index 78% rename from MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/MockJwtAuthenticationService.cs rename to MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/SymmetricJwtAuthenticationService.cs index dbe4c06..68cd259 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/MockJwtAuthenticationService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/SymmetricJwtAuthenticationService.cs @@ -3,15 +3,16 @@ using System.Text; using Microsoft.IdentityModel.Tokens; using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Model; namespace ServiceName.Infrastructure.Authentication { /// /// https://dotnetcoretutorials.com/2020/01/15/creating-and-validating-jwt-tokens-in-asp-net-core/ /// - public class MockJwtAuthenticationService : IAuthenticationService + public class SymmetricJwtAuthenticationService : IJwtAuthenticationService { - public string GenerateToken(int userId) + public Task GenerateTokenAsync(ModuleIdentity identity, int lifetimeSeconds, string issuer, string audience) { var mySecret = "asdv234235^&%&^%&^hjsdfb2%%%"; var mySecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(mySecret)); @@ -22,7 +23,7 @@ public string GenerateToken(int userId) { Subject = new ClaimsIdentity(new Claim[] { - new Claim(ClaimTypes.NameIdentifier, userId.ToString()), + new Claim(ClaimTypes.NameIdentifier, identity.UserGuid), }), Expires = DateTime.UtcNow.AddDays(7), Issuer = myIssuer, @@ -30,10 +31,10 @@ public string GenerateToken(int userId) SigningCredentials = new SigningCredentials(mySecurityKey, SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); - return tokenHandler.WriteToken(token); + return Task.FromResult(tokenHandler.WriteToken(token)); } - - public bool ValidateToken(string token) + + public async Task ValidateTokenAsync(string token) { var mySecret = "asdv234234^&%&^%&^hjsdfb2%%%"; var mySecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(mySecret)); @@ -54,9 +55,9 @@ public bool ValidateToken(string token) } catch { - return false; + return await Task.FromResult(false); } - return true; + return await Task.FromResult(true); } } } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs index 924b0ed..dbcf3e0 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs @@ -1,6 +1,7 @@ using Amazon; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.DataModel; +using Amazon.KeyManagementService; using Amazon.Runtime; using AWS.Logger; using AWS.Logger.SeriLog; @@ -40,10 +41,37 @@ public static IServiceCollection AddInfrastructureServices(this IServiceCollecti services.AddSingleton(GetRedisCache()); services.AddSingleton(GetDynamoDBContext()); + services.AddSingleton(GetAmazonKms()); return services; } + private static IAmazonKeyManagementService GetAmazonKms() + { + var accessKey = _configurationManager["ModuleConfiguration:AwsServices:Kms:AccessKey"]; + var secretKey = _configurationManager["ModuleConfiguration:AwsServices:Kms:SecretKey"]; + var regionEndpoint = RegionEndpoint.GetBySystemName(_configurationManager["ModuleConfiguration:AwsServices:Kms:RegionEndpoint"]); + var localTestEndpoint = _configurationManager["ModuleConfiguration:AwsServices:Kms:LocalTestEndpoint"]; + + AmazonKeyManagementServiceConfig amazonKeyManagementServiceConfig = new() + { + RegionEndpoint = regionEndpoint, + }; + + if (!string.IsNullOrEmpty(localTestEndpoint)) + { + amazonKeyManagementServiceConfig.UseHttp = true; + amazonKeyManagementServiceConfig.ServiceURL = localTestEndpoint; + } + + if (!string.IsNullOrEmpty(accessKey) && !string.IsNullOrEmpty(secretKey)) + { + return new AmazonKeyManagementServiceClient(accessKey, secretKey, amazonKeyManagementServiceConfig); + } + + return new AmazonKeyManagementServiceClient(amazonKeyManagementServiceConfig); + } + private static IDistributedCache GetRedisCache() { var cache = new RedisCache(new RedisCacheOptions @@ -121,21 +149,28 @@ private static DynamoDBContext GetDynamoDBContext() var secretKey = _configurationManager["ModuleConfiguration:ConnectionStrings:DynamoDb:SecretKey"]; var regionEndpoint = RegionEndpoint.GetBySystemName(_configurationManager["ModuleConfiguration:ConnectionStrings:DynamoDb:RegionEndpoint"]); var localTestEndpoint = _configurationManager["ModuleConfiguration:ConnectionStrings:DynamoDb:LocalTestEndpoint"]; + + var dynamoDBContextConfig = new DynamoDBContextConfig() { ConsistentRead = true }; - AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig + AmazonDynamoDBConfig amazonDynamoDBConfig = new() { RegionEndpoint = regionEndpoint }; if (!string.IsNullOrEmpty(localTestEndpoint)) { - clientConfig.UseHttp = true; - clientConfig.ServiceURL = localTestEndpoint; + amazonDynamoDBConfig.UseHttp = true; + amazonDynamoDBConfig.ServiceURL = localTestEndpoint; } - var amazonDynamoDBClient = new AmazonDynamoDBClient(accessKey, secretKey, clientConfig); - var dynamoDBContextConfig = new DynamoDBContextConfig() { ConsistentRead = true }; - return new DynamoDBContext(amazonDynamoDBClient, dynamoDBContextConfig); + if (!string.IsNullOrEmpty(accessKey) && !string.IsNullOrEmpty(secretKey)) + { + var amazonDynamoDBClientWithCredentials = new AmazonDynamoDBClient(accessKey, secretKey, amazonDynamoDBConfig); + return new DynamoDBContext(amazonDynamoDBClientWithCredentials, dynamoDBContextConfig); + } + + var amazonDynamoDBClientWithoutCredentials = new AmazonDynamoDBClient(amazonDynamoDBConfig); + return new DynamoDBContext(amazonDynamoDBClientWithoutCredentials, dynamoDBContextConfig); } } } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs index cbee3dd..3a3d4ff 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs @@ -18,12 +18,10 @@ namespace ServiceName.Infrastructure.Repositories public class SettingsRepositoryService : IRepositoryService { readonly IDynamoDBContext _dynamoDBContext; - readonly IConfiguration _configuration; readonly ILogger _logger; - public SettingsRepositoryService(IConfiguration configuration, ILogger logger, IDynamoDBContext dynamoContext) + public SettingsRepositoryService(IDynamoDBContext dynamoContext, ILogger logger) { - _configuration = configuration; _logger = logger; _dynamoDBContext = dynamoContext; } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj b/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj index a611a6b..f5d7906 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ServiceName.Infrastructure.csproj @@ -7,20 +7,12 @@ - + - - - - + - - - - - diff --git a/MicroserviceTemplate/tests/ServiceName.Test/JwtTests.cs b/MicroserviceTemplate/tests/ServiceName.Test/JwtTests.cs new file mode 100644 index 0000000..3c8684a --- /dev/null +++ b/MicroserviceTemplate/tests/ServiceName.Test/JwtTests.cs @@ -0,0 +1,64 @@ +using Microsoft.Extensions.Configuration; +using ServiceName.Core.Model; +using ServiceName.Infrastructure.Authentication; + +namespace ServiceName.Test +{ + public class JwtTests + { + AssymetricKmsJwtService _assymetricKmsJwtService; + ModuleIdentity _moduleIdentity; + + public JwtTests() + { + var configMock = new Dictionary + { + {"ModuleConfiguration:AwsServices:Kms:AccessKey", "test"}, + {"ModuleConfiguration:AwsServices:Kms:SecretKey", "test"}, + {"ModuleConfiguration:AwsServices:Kms:RegionEndpoint", "eu-west-2"}, + {"ModuleConfiguration:AwsServices:Kms:LocalTestEndpoint", "http://localhost:52002"}, + {"ModuleConfiguration:Jwt:SigningKeyId", "6732c7ca-6ec9-4b96-9711-fd1c7d637c8e"}, + {"ModuleConfiguration:Jwt:PublicKey", "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA56tfR6w3YJpbH5XZ6Ze2kB2evnUpbyZJiTLPKSc/VeA46m09lVB7bJRp0pKX2LusT2pccrVe5AYtbnikKqhOQWUdjLJnSONPNpd4yjEseqPblsXicA+xdP+Fk2W0yDxOc79LUAywgjV8JqNbbtVbhzqVPOLalJYnPEAVa3NQV138dnU7NzxbAjPjXINi7BBZ2OLRuocJRMfe16AUiQtH8MaWfRnnRRwdCBLJCXnZy+0hVc701SrVoTS+CA8RfGTCnzutx9MXW7t4SCEjZH0MSfhSZbKggPfi36HeUdClacgD6L0+FhSBKzd8kOC06CDf5WM9oV/XtWVXEWWGPDHv8wIDAQAB"}, + {"ModuleConfiguration:Jwt:Issuer", "Issuer"}, + {"ModuleConfiguration:Jwt:Audience", "Audience"}, + }; + + _moduleIdentity = new() + { + UserGuid = "UserGuid", + InstanceGuid = "InstanceGuid", + UserName = "UserName" + }; + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(configMock) + .Build(); + + _assymetricKmsJwtService = new(configuration); + } + + [Fact] + public async Task JwtValidationOK() + { + var token = await _assymetricKmsJwtService.GenerateTokenAsync(_moduleIdentity, 60, "Issuer", "Audience"); + var isValid = await _assymetricKmsJwtService.ValidateTokenAsync(token); + Assert.True(isValid); + } + + [Fact] + public async Task JwtWithIssuerMismatch() + { + var token = await _assymetricKmsJwtService.GenerateTokenAsync(_moduleIdentity, 60, "Issuer2", "Audience"); + var isValid = await _assymetricKmsJwtService.ValidateTokenAsync(token); + Assert.False(isValid); + } + + [Fact] + public async Task JwtWithAudienceMismatch() + { + var token = await _assymetricKmsJwtService.GenerateTokenAsync(_moduleIdentity, 60, "Issuer", "Audience2"); + var isValid = await _assymetricKmsJwtService.ValidateTokenAsync(token); + Assert.False(isValid); + } + } +} From 21d1c25519af845e4cf634bb363444feb85b22ab Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Mon, 20 Jun 2022 08:44:43 +1000 Subject: [PATCH 19/56] [MicroserviceTemplate] Restructured settings file --- .../HealthCheck/DynamoDbHealthCheck.cs | 10 ++--- .../appsettings.Development.json | 38 +++++++++--------- .../src/ServiceName.API/appsettings.json | 39 +++++++++++++------ .../Authentication/AssymetricKmsJwtService.cs | 1 - .../ConfigureServices.cs | 18 ++++----- 5 files changed, 59 insertions(+), 47 deletions(-) diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs index 75e562e..cddacf7 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs @@ -18,11 +18,11 @@ public Task CheckHealthAsync(HealthCheckContext context, Canc { try { - var accessKey = _configuration["ModuleConfiguration:ConnectionStrings:DynamoDb:AccessKey"]; - var secretKey = _configuration["ModuleConfiguration:ConnectionStrings:DynamoDb:SecretKey"]; - var regionEndpoint = RegionEndpoint.GetBySystemName(_configuration["ModuleConfiguration:ConnectionStrings:DynamoDb:RegionEndpoint"]); - var tableName = _configuration["ModuleConfiguration:ConnectionStrings:DynamoDb:TableName"]; - var localTestEndpoint = _configuration["ModuleConfiguration:ConnectionStrings:DynamoDb:LocalTestEndpoint"]; + var accessKey = _configuration["ModuleConfiguration:AwsServices:DynamoDb:AccessKey"]; + var secretKey = _configuration["ModuleConfiguration:AwsServices:DynamoDb:SecretKey"]; + var regionEndpoint = RegionEndpoint.GetBySystemName(_configuration["ModuleConfiguration:AwsServices:DynamoDb:RegionEndpoint"]); + var tableName = _configuration["ModuleConfiguration:AwsServices:DynamoDb:TableName"]; + var localTestEndpoint = _configuration["ModuleConfiguration:AwsServices:DynamoDb:LocalTestEndpoint"]; AmazonDynamoDBConfig clientConfig = new() { diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json index 6dcb120..41c45d7 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json @@ -11,14 +11,7 @@ "IsSwaggerUIEnabled": true, "ConnectionStrings": { "SqlServer": "Data Source=host.docker.internal,1433;Initial Catalog=DBName;User ID=sa;Password=M3rz0ug4!!!!", - "Redis": "host.docker.internal:6379", - "DynamoDb": { - "AccessKey": "test", - "SecretKey": "test", - "RegionEndpoint": "ap-southeast-2", - "TableName": "ServiceName_Setting", - "LocalTestEndpoint": "http://host.docker.internal:8000" - } + "Redis": "host.docker.internal:6379" }, "Logging": { @@ -27,28 +20,33 @@ { "ServerUrl": "http://localhost:5341", "ApiKey": "" - }, - "AwsCloudWatch": - { - "AccessKey": "test", - "SecretKey": "test", - "RegionEndpoint": "ap-southeast-2", - "LogGroupName": "/LocalStack/Microservice/Logs", - "LocalTestEndpoint": "http://host.docker.internal:4566" } }, - "AwsServices": - { - "Kms": - { + "AwsServices": { + "Kms": { "AccessKey": "test", "SecretKey": "test", "RegionEndpoint": "eu-west-2", "LocalTestEndpoint": "http://localhost:52002" + }, + "CloudWatchLogs": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "LocalTestEndpoint": "http://host.docker.internal:4566", + "LogGroupName": "/LocalStack/Microservice/Logs" + }, + "DynamoDb": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "LocalTestEndpoint": "http://host.docker.internal:8000", + "TableName": "ServiceName_Setting" } }, "Jwt": { + "TestMode": false, "SigningKeyId": "6732c7ca-6ec9-4b96-9711-fd1c7d637c8e", "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA56tfR6w3YJpbH5XZ6Ze2kB2evnUpbyZJiTLPKSc/VeA46m09lVB7bJRp0pKX2LusT2pccrVe5AYtbnikKqhOQWUdjLJnSONPNpd4yjEseqPblsXicA+xdP+Fk2W0yDxOc79LUAywgjV8JqNbbtVbhzqVPOLalJYnPEAVa3NQV138dnU7NzxbAjPjXINi7BBZ2OLRuocJRMfe16AUiQtH8MaWfRnnRRwdCBLJCXnZy+0hVc701SrVoTS+CA8RfGTCnzutx9MXW7t4SCEjZH0MSfhSZbKggPfi36HeUdClacgD6L0+FhSBKzd8kOC06CDf5WM9oV/XtWVXEWWGPDHv8wIDAQAB", "Issuer": "Issuer", diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.json index 98f62c8..9068739 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.json @@ -10,28 +10,43 @@ "IsSwaggerUIEnabled": true, "ConnectionStrings": { "SqlServer": "Data Source=host.docker.internal,1433;Initial Catalog=DBName;User ID=sa;Password=M3rz0ug4!!!!", - "Redis": "host.docker.internal:6379", - "DynamoDb": { - "AccessKey": "test", - "SecretKey": "test", - "RegionEndpoint": "ap-southeast-2", - "TableName": "ServiceName_Setting", - "LocalTestEndpoint": "http://host.docker.internal:8000" - } + "Redis": "host.docker.internal:6379" }, "Logging": { - "Sink": "AwsCloudWatch", + "Sink": "Seq", "Seq": { "ServerUrl": "http://localhost:5341", "ApiKey": "" + } + }, + "AwsServices": { + "Kms": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "eu-west-2", + "LocalTestEndpoint": "http://localhost:52002" }, - "AwsCloudWatch": { + "CloudWatchLogs": { "AccessKey": "test", "SecretKey": "test", "RegionEndpoint": "ap-southeast-2", - "LogGroupName": "/LocalStack/Microservice/Logs", - "LocalTestEndpoint": "http://host.docker.internal:4566" + "LocalTestEndpoint": "http://host.docker.internal:4566", + "LogGroupName": "/LocalStack/Microservice/Logs" + }, + "DynamoDb": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "LocalTestEndpoint": "http://host.docker.internal:8000", + "TableName": "ServiceName_Setting" } + }, + "Jwt": { + "TestMode": false, + "SigningKeyId": "6732c7ca-6ec9-4b96-9711-fd1c7d637c8e", + "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA56tfR6w3YJpbH5XZ6Ze2kB2evnUpbyZJiTLPKSc/VeA46m09lVB7bJRp0pKX2LusT2pccrVe5AYtbnikKqhOQWUdjLJnSONPNpd4yjEseqPblsXicA+xdP+Fk2W0yDxOc79LUAywgjV8JqNbbtVbhzqVPOLalJYnPEAVa3NQV138dnU7NzxbAjPjXINi7BBZ2OLRuocJRMfe16AUiQtH8MaWfRnnRRwdCBLJCXnZy+0hVc701SrVoTS+CA8RfGTCnzutx9MXW7t4SCEjZH0MSfhSZbKggPfi36HeUdClacgD6L0+FhSBKzd8kOC06CDf5WM9oV/XtWVXEWWGPDHv8wIDAQAB", + "Issuer": "Issuer", + "Audience": "Audience" } } } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs index 8a09046..8c8bd65 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs @@ -2,7 +2,6 @@ using System.Security.Cryptography; using System.Text; using System.Text.Json; -using Amazon; using Amazon.KeyManagementService; using Amazon.KeyManagementService.Model; using Microsoft.Extensions.Configuration; diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs index dbcf3e0..56985a2 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs @@ -98,11 +98,11 @@ private static ILogger GetCloudSeqLogger() private static ILogger GetCloudWatchLogger() { - var accessKey = _configurationManager["ModuleConfiguration:Logging:AwsCloudWatch:AccessKey"]; - var secretKey = _configurationManager["ModuleConfiguration:Logging:AwsCloudWatch:SecretKey"]; - var regionEndpoint = _configurationManager["ModuleConfiguration:Logging:AwsCloudWatch:RegionEndpoint"]; - var localTestEndpoint = _configurationManager["ModuleConfiguration:Logging:AwsCloudWatch:LocalTestEndpoint"]; - var logGroupName = _configurationManager["ModuleConfiguration:Logging:AwsCloudWatch:LogGroupName"]; + var accessKey = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:AccessKey"]; + var secretKey = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:SecretKey"]; + var regionEndpoint = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:RegionEndpoint"]; + var localTestEndpoint = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:LocalTestEndpoint"]; + var logGroupName = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:LogGroupName"]; AWSLoggerConfig configuration; @@ -145,10 +145,10 @@ private static IConfiguration GetConfiguration(ConfigurationManager configuratio private static DynamoDBContext GetDynamoDBContext() { - var accessKey = _configurationManager["ModuleConfiguration:ConnectionStrings:DynamoDb:AccessKey"]; - var secretKey = _configurationManager["ModuleConfiguration:ConnectionStrings:DynamoDb:SecretKey"]; - var regionEndpoint = RegionEndpoint.GetBySystemName(_configurationManager["ModuleConfiguration:ConnectionStrings:DynamoDb:RegionEndpoint"]); - var localTestEndpoint = _configurationManager["ModuleConfiguration:ConnectionStrings:DynamoDb:LocalTestEndpoint"]; + var accessKey = _configurationManager["ModuleConfiguration:AwsServices:DynamoDb:AccessKey"]; + var secretKey = _configurationManager["ModuleConfiguration:AwsServices:DynamoDb:SecretKey"]; + var regionEndpoint = RegionEndpoint.GetBySystemName(_configurationManager["ModuleConfiguration:AwsServices:DynamoDb:RegionEndpoint"]); + var localTestEndpoint = _configurationManager["ModuleConfiguration:AwsServices:DynamoDb:LocalTestEndpoint"]; var dynamoDBContextConfig = new DynamoDBContextConfig() { ConsistentRead = true }; From d95072ba57da78349e137de00d2b743078a149be Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Mon, 20 Jun 2022 10:20:54 +1000 Subject: [PATCH 20/56] [MicroserviceTemplate] Added JWT Authentication --- .../Extensions/AuthExtension.cs | 38 ++++++ .../Extensions/EndpointExtensions.cs | 30 +++-- .../Extensions/SwaggerExtensions.cs | 34 +++--- .../src/ServiceName.API/Program.cs | 17 ++- .../ServiceName.API/ServiceName.API.csproj | 1 + .../appsettings.Development.json | 20 ++-- .../src/ServiceName.API/appsettings.json | 14 +-- .../Common/Security/SecurityHelper.cs | 24 ++++ .../ServiceName.Core/ServiceName.Core.csproj | 2 + .../Authentication/AssymetricKmsJwtService.cs | 13 +-- .../BasicAuthenticationHandler.cs | 46 -------- .../SymmetricJwtAuthenticationService.cs | 2 +- .../ConfigureServices.cs | 110 +++++++++--------- .../ServiceName.Test/Helpers/TestHelper.cs | 26 ++++- .../tests/ServiceName.Test/JwtTests.cs | 69 ++++++++--- 15 files changed, 269 insertions(+), 177 deletions(-) create mode 100644 MicroserviceTemplate/src/ServiceName.API/Extensions/AuthExtension.cs create mode 100644 MicroserviceTemplate/src/ServiceName.Core/Common/Security/SecurityHelper.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/BasicAuthenticationHandler.cs diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthExtension.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthExtension.cs new file mode 100644 index 0000000..34bc2b3 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthExtension.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; +using ServiceName.Core.Common.Security; + +namespace ServiceName.API.Extensions +{ + public static class SecurityExtension + { + public static void AddAuthSupport(this IServiceCollection services, ConfigurationManager configurationManager) + { + services.AddAuthentication(o => + { + o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; + }).AddJwtBearer(o => + { + o.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + ValidateIssuer = true, + ValidateAudience = true, + ValidIssuer = configurationManager["ModuleConfiguration:Jwt:Issuer"], + ValidAudience = configurationManager["ModuleConfiguration:Jwt:Audience"], + IssuerSigningKey = SecurityHelper.GetRsaSecurityKey(configurationManager["ModuleConfiguration:AwsServices:KMS:PublicKey"]) + }; + }); + + services.AddAuthorization(); + } + + public static void UseAuth(this WebApplication app) { + + app.UseAuthentication(); + app.UseAuthorization(); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs index 9ae63a4..5dbe93a 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs @@ -1,6 +1,8 @@ using Asp.Versioning.Conventions; using MediatR; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using ServiceName.Core.Common.Interfaces; using ServiceName.Core.CQRS.Commands; using ServiceName.Core.CQRS.Queries; using ServiceName.Core.Model; @@ -9,27 +11,39 @@ namespace ServiceName.API.Extensions { public static class EndpointExtensions { - public static void MapEndpoints(this WebApplication app) + public static void MapEndpoints(this WebApplication app, ConfigurationManager configuration) { // define a 'version set' that applies to an API group var versionSet = app.NewApiVersionSet() .HasApiVersion(1.0) - .HasApiVersion(2.0) + //.HasApiVersion(2.0) .ReportApiVersions() .Build(); - app.MapGet("/settings/{tenantId}", async (IMediator mediator, string tenantId) => await mediator.Send(new GetSettingsQueryRequest() { TenantId = Guid.Parse(tenantId) })) + app.MapGet("/settings/{tenantId}", [Authorize] async (IMediator mediator, string tenantId) => await mediator.Send(new GetSettingsQueryRequest() { TenantId = Guid.Parse(tenantId) })) .WithApiVersionSet(versionSet) .MapToApiVersion(1.0); - app.MapGet("/settings/{tenantId}", async (string tenantId) => await Task.FromResult(tenantId)) - .WithApiVersionSet(versionSet) - .MapToApiVersion(2.0); + //app.MapGet("/settings/{tenantId}", [Authorize] async (string tenantId) => await Task.FromResult(tenantId)) + // .WithApiVersionSet(versionSet) + // .MapToApiVersion(2.0); - app.MapPost("/settings/{tenantId}", async (IMediator mediator, [FromBody] Settings settings, string tenantId) => await mediator.Send(new SaveSettingsCommandRequest() { TenantId = Guid.Parse(tenantId), Settings = settings })) + app.MapPost("/settings/{tenantId}", [Authorize] async (IMediator mediator, [FromBody] Settings settings, string tenantId) => await mediator.Send(new SaveSettingsCommandRequest() { TenantId = Guid.Parse(tenantId), Settings = settings })) .WithApiVersionSet(versionSet) .MapToApiVersion(1.0); + + //These endpoints are only for JWT Testing & Troubleshooting Purposes + if (bool.Parse(configuration["ModuleConfiguration:Jwt:TestMode"])) + { + app.MapPost("/test/token/generate", [AllowAnonymous] async (IJwtAuthenticationService authService, string issuer, string audience) => await authService.GenerateTokenAsync(new ModuleIdentity(), 60, issuer, audience)) + .WithApiVersionSet(versionSet) + .MapToApiVersion(1.0); + + app.MapPost("/test/token/validate", [AllowAnonymous] async (IJwtAuthenticationService authService, string token) => await authService.ValidateTokenAsync(token)) + .WithApiVersionSet(versionSet) + .MapToApiVersion(1.0); + } } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs index 74693be..ea1e41a 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs @@ -15,29 +15,33 @@ public static void AddSwaggerSupport(this IServiceCollection services) { options.OperationFilter(); - options.AddSecurityDefinition("basic", new OpenApiSecurityScheme + var securityScheme = new OpenApiSecurityScheme() { Name = "Authorization", - Type = SecuritySchemeType.Http, - Scheme = "basic", + Type = SecuritySchemeType.ApiKey, + Scheme = "Bearer", + BearerFormat = "JWT", In = ParameterLocation.Header, - Description = "Basic Authorization header using the Bearer scheme." - }); + Description = "JSON Web Token based security", + }; - options.AddSecurityRequirement(new OpenApiSecurityRequirement + var securityReq = new OpenApiSecurityRequirement() { { - new OpenApiSecurityScheme + new OpenApiSecurityScheme + { + Reference = new OpenApiReference { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = "basic" - } - }, - new string[] {} + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + new string[] {} } - }); + }; + + options.AddSecurityDefinition("Bearer", securityScheme); + options.AddSecurityRequirement(securityReq); }); } diff --git a/MicroserviceTemplate/src/ServiceName.API/Program.cs b/MicroserviceTemplate/src/ServiceName.API/Program.cs index 61a9035..b78b970 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Program.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Program.cs @@ -1,5 +1,9 @@ +using System.Text; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; using ServiceName.API.Extensions; using ServiceName.Core; +using ServiceName.Core.Common.Security; using ServiceName.Infrastructure; var builder = WebApplication.CreateBuilder(args); @@ -13,8 +17,6 @@ builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi); -builder.Services.AddEndpointsApiExplorer(); - builder.Services.AddInfrastructureServices(builder.Configuration); builder.Services.AddApplicationServices(); @@ -25,11 +27,15 @@ builder.Services.AddHealthCheckSupport(builder.Configuration); +builder.Services.AddAuthSupport(builder.Configuration); + +builder.Services.AddEndpointsApiExplorer(); + var app = builder.Build(); app.MapControllers(); -app.MapEndpoints(); +app.MapEndpoints(builder.Configuration); app.ConfigureHealthCheck(); @@ -38,10 +44,9 @@ app.ConfigureSwaggerUI(); } -app.UseAuthentication(); -app.UseAuthorization(); -app.UseHttpsRedirection(); +app.UseAuth(); +app.UseHttpsRedirection(); app.Run(); diff --git a/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj b/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj index ea2cf05..aa28b3e 100644 --- a/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj +++ b/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj @@ -19,6 +19,7 @@ + diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json index 41c45d7..d398221 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json @@ -13,11 +13,9 @@ "SqlServer": "Data Source=host.docker.internal,1433;Initial Catalog=DBName;User ID=sa;Password=M3rz0ug4!!!!", "Redis": "host.docker.internal:6379" }, - "Logging": - { - "Sink": "Seq", - "Seq": - { + "Logging": { + "Sink": "CloudWatchLogs", + "Seq": { "ServerUrl": "http://localhost:5341", "ApiKey": "" } @@ -27,7 +25,9 @@ "AccessKey": "test", "SecretKey": "test", "RegionEndpoint": "eu-west-2", - "LocalTestEndpoint": "http://localhost:52002" + "LocalTestEndpoint": "http://localhost:52002", + "SigningKeyId": "6732c7ca-6ec9-4b96-9711-fd1c7d637c8e", + "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA56tfR6w3YJpbH5XZ6Ze2kB2evnUpbyZJiTLPKSc/VeA46m09lVB7bJRp0pKX2LusT2pccrVe5AYtbnikKqhOQWUdjLJnSONPNpd4yjEseqPblsXicA+xdP+Fk2W0yDxOc79LUAywgjV8JqNbbtVbhzqVPOLalJYnPEAVa3NQV138dnU7NzxbAjPjXINi7BBZ2OLRuocJRMfe16AUiQtH8MaWfRnnRRwdCBLJCXnZy+0hVc701SrVoTS+CA8RfGTCnzutx9MXW7t4SCEjZH0MSfhSZbKggPfi36HeUdClacgD6L0+FhSBKzd8kOC06CDf5WM9oV/XtWVXEWWGPDHv8wIDAQAB" }, "CloudWatchLogs": { "AccessKey": "test", @@ -46,11 +46,9 @@ }, "Jwt": { - "TestMode": false, - "SigningKeyId": "6732c7ca-6ec9-4b96-9711-fd1c7d637c8e", - "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA56tfR6w3YJpbH5XZ6Ze2kB2evnUpbyZJiTLPKSc/VeA46m09lVB7bJRp0pKX2LusT2pccrVe5AYtbnikKqhOQWUdjLJnSONPNpd4yjEseqPblsXicA+xdP+Fk2W0yDxOc79LUAywgjV8JqNbbtVbhzqVPOLalJYnPEAVa3NQV138dnU7NzxbAjPjXINi7BBZ2OLRuocJRMfe16AUiQtH8MaWfRnnRRwdCBLJCXnZy+0hVc701SrVoTS+CA8RfGTCnzutx9MXW7t4SCEjZH0MSfhSZbKggPfi36HeUdClacgD6L0+FhSBKzd8kOC06CDf5WM9oV/XtWVXEWWGPDHv8wIDAQAB", - "Issuer": "Issuer", - "Audience": "Audience" + "TestMode": true, + "Issuer": "issuer", + "Audience": "audience" } } } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.json index 9068739..5a68c6d 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.json @@ -13,7 +13,7 @@ "Redis": "host.docker.internal:6379" }, "Logging": { - "Sink": "Seq", + "Sink": "CloudWatchLogs", "Seq": { "ServerUrl": "http://localhost:5341", "ApiKey": "" @@ -24,7 +24,9 @@ "AccessKey": "test", "SecretKey": "test", "RegionEndpoint": "eu-west-2", - "LocalTestEndpoint": "http://localhost:52002" + "LocalTestEndpoint": "http://localhost:52002", + "SigningKeyId": "6732c7ca-6ec9-4b96-9711-fd1c7d637c8e", + "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA56tfR6w3YJpbH5XZ6Ze2kB2evnUpbyZJiTLPKSc/VeA46m09lVB7bJRp0pKX2LusT2pccrVe5AYtbnikKqhOQWUdjLJnSONPNpd4yjEseqPblsXicA+xdP+Fk2W0yDxOc79LUAywgjV8JqNbbtVbhzqVPOLalJYnPEAVa3NQV138dnU7NzxbAjPjXINi7BBZ2OLRuocJRMfe16AUiQtH8MaWfRnnRRwdCBLJCXnZy+0hVc701SrVoTS+CA8RfGTCnzutx9MXW7t4SCEjZH0MSfhSZbKggPfi36HeUdClacgD6L0+FhSBKzd8kOC06CDf5WM9oV/XtWVXEWWGPDHv8wIDAQAB" }, "CloudWatchLogs": { "AccessKey": "test", @@ -42,11 +44,9 @@ } }, "Jwt": { - "TestMode": false, - "SigningKeyId": "6732c7ca-6ec9-4b96-9711-fd1c7d637c8e", - "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA56tfR6w3YJpbH5XZ6Ze2kB2evnUpbyZJiTLPKSc/VeA46m09lVB7bJRp0pKX2LusT2pccrVe5AYtbnikKqhOQWUdjLJnSONPNpd4yjEseqPblsXicA+xdP+Fk2W0yDxOc79LUAywgjV8JqNbbtVbhzqVPOLalJYnPEAVa3NQV138dnU7NzxbAjPjXINi7BBZ2OLRuocJRMfe16AUiQtH8MaWfRnnRRwdCBLJCXnZy+0hVc701SrVoTS+CA8RfGTCnzutx9MXW7t4SCEjZH0MSfhSZbKggPfi36HeUdClacgD6L0+FhSBKzd8kOC06CDf5WM9oV/XtWVXEWWGPDHv8wIDAQAB", - "Issuer": "Issuer", - "Audience": "Audience" + "TestMode": true, + "Issuer": "issuer", + "Audience": "audience" } } } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Security/SecurityHelper.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Security/SecurityHelper.cs new file mode 100644 index 0000000..c04ac30 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Security/SecurityHelper.cs @@ -0,0 +1,24 @@ +using System.Security.Cryptography; +using Microsoft.IdentityModel.Tokens; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace ServiceName.Core.Common.Security +{ + public static class SecurityHelper + { + public static RsaSecurityKey GetRsaSecurityKey(string publicKey) + { + var publicKeyBase64 = Convert.FromBase64String(publicKey); + var asymmetricKeyParameter = PublicKeyFactory.CreateKey(publicKeyBase64); + var rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter; + var rsaParameters = new RSAParameters + { + Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned(), + Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned() + }; + var rsaSecurityKey = new RsaSecurityKey(rsaParameters); + return rsaSecurityKey; + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.Core/ServiceName.Core.csproj b/MicroserviceTemplate/src/ServiceName.Core/ServiceName.Core.csproj index 4f68052..e3c8a48 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/ServiceName.Core.csproj +++ b/MicroserviceTemplate/src/ServiceName.Core/ServiceName.Core.csproj @@ -10,6 +10,8 @@ + + diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs index 8c8bd65..c0d0b34 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs @@ -10,6 +10,7 @@ using Org.BouncyCastle.Security; using Serilog; using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Common.Security; using ServiceName.Core.Model; using ServiceName.Infrastructure.Authentication.JWT; @@ -36,7 +37,7 @@ public AssymetricKmsJwtService(IConfiguration configuration, IAmazonKeyManagemen public async Task GenerateTokenAsync(ModuleIdentity identity, int lifetimeSeconds, string issuer, string audience) { - var signingKeyId = _configuration["ModuleConfiguration:Jwt:SigningKeyId"]; + var signingKeyId = _configuration["ModuleConfiguration:AwsServices:KMS:SigningKeyId"]; if (string.IsNullOrWhiteSpace(signingKeyId)) { @@ -82,15 +83,7 @@ public async Task GenerateTokenAsync(ModuleIdentity identity, int lifeti public async Task ValidateTokenAsync(string token) { - var publicKey = Convert.FromBase64String(_configuration["ModuleConfiguration:Jwt:PublicKey"]); - var asymmetricKeyParameter = PublicKeyFactory.CreateKey(publicKey); - var rsaKeyParameters = (RsaKeyParameters)asymmetricKeyParameter; - var rsaParameters = new RSAParameters - { - Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned(), - Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned() - }; - var rsaSecurityKey = new RsaSecurityKey(rsaParameters); + RsaSecurityKey rsaSecurityKey = SecurityHelper.GetRsaSecurityKey(_configuration["ModuleConfiguration:AwsServices:KMS:PublicKey"]); var issuer = _configuration["ModuleConfiguration:Jwt:Issuer"]; var audience = _configuration["ModuleConfiguration:Jwt:Audience"]; diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/BasicAuthenticationHandler.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/BasicAuthenticationHandler.cs deleted file mode 100644 index 42459df..0000000 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/BasicAuthenticationHandler.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Security.Claims; -using System.Text; -using System.Text.Encodings.Web; -using Microsoft.AspNetCore.Authentication; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace ServiceName.Infrastructure.Authentication -{ - public class BasicAuthenticationHandler : AuthenticationHandler - { - public BasicAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) - { - } - - protected override Task HandleAuthenticateAsync() - { - var authHeader = Request.Headers["Authorization"].ToString(); - if (authHeader != null && authHeader.StartsWith("basic", StringComparison.OrdinalIgnoreCase)) - { - var token = authHeader.Substring("Basic ".Length).Trim(); - Console.WriteLine(token); - var credentialstring = Encoding.UTF8.GetString(Convert.FromBase64String(token)); - var credentials = credentialstring.Split(':'); - - if (credentials[1] == "admin") - { - var claims = new[] { new Claim("name", "Admin"), new Claim(ClaimTypes.Role, "Admin") }; - var identity = new ClaimsIdentity(claims, "Basic"); - var claimsPrincipal = new ClaimsPrincipal(identity); - return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal, Scheme.Name))); - } - - Response.StatusCode = 401; - //Response.Headers.Add("WWW-Authenticate", "Basic realm=\"dotnetthoughts.net\""); - return Task.FromResult(AuthenticateResult.Fail("Invalid Authorization Header")); - } - else - { - Response.StatusCode = 401; - //Response.Headers.Add("WWW-Authenticate", "Basic realm=\"dotnetthoughts.net\""); - return Task.FromResult(AuthenticateResult.Fail("Invalid Authorization Header")); - } - } - } -} diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/SymmetricJwtAuthenticationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/SymmetricJwtAuthenticationService.cs index 68cd259..e84649f 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/SymmetricJwtAuthenticationService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/SymmetricJwtAuthenticationService.cs @@ -36,7 +36,7 @@ public Task GenerateTokenAsync(ModuleIdentity identity, int lifetimeSeco public async Task ValidateTokenAsync(string token) { - var mySecret = "asdv234234^&%&^%&^hjsdfb2%%%"; + var mySecret = "asdv234235^&%&^%&^hjsdfb2%%%"; var mySecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(mySecret)); var myIssuer = "http://mysite.com"; var myAudience = "http://myaudience.com"; diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs index 56985a2..f5644ad 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs @@ -12,6 +12,7 @@ using Serilog; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; +using ServiceName.Infrastructure.Authentication; using ServiceName.Infrastructure.Repositories; namespace ServiceName.Infrastructure @@ -25,23 +26,12 @@ public static IServiceCollection AddInfrastructureServices(this IServiceCollecti _configurationManager = configurationManager; services.AddSingleton(GetConfiguration(configurationManager)); - services.AddScoped, SettingsRepositoryService>(); - - var loggingSink = _configurationManager["ModuleConfiguration:Logging:Sink"]; - - switch (loggingSink) - { - case "Seq": - services.AddSingleton(GetCloudSeqLogger()); - break; - default: - services.AddSingleton(GetCloudWatchLogger()); - break; - } - + services.AddSingleton(GetLogger()); services.AddSingleton(GetRedisCache()); services.AddSingleton(GetDynamoDBContext()); services.AddSingleton(GetAmazonKms()); + services.AddScoped, SettingsRepositoryService>(); + services.AddScoped(); return services; } @@ -82,53 +72,63 @@ private static IDistributedCache GetRedisCache() return cache; } - private static ILogger GetCloudSeqLogger() - { - var serverUrl = _configurationManager["ModuleConfiguration:Logging:Seq:ServerUrl"]; - var apiKey = _configurationManager["ModuleConfiguration:Logging:Seq:ApiKey"]; - - Log.Logger = new LoggerConfiguration() - .WriteTo.Console() - .WriteTo.Seq(serverUrl: serverUrl, - apiKey: apiKey) - .CreateLogger(); - - return Log.Logger; - } - private static ILogger GetCloudWatchLogger() + private static ILogger GetLogger() { - var accessKey = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:AccessKey"]; - var secretKey = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:SecretKey"]; - var regionEndpoint = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:RegionEndpoint"]; - var localTestEndpoint = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:LocalTestEndpoint"]; - var logGroupName = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:LogGroupName"]; - AWSLoggerConfig configuration; + var loggingSink = _configurationManager["ModuleConfiguration:Logging:Sink"]; - //If logGroupName is empty it uses the default AWS Lambad Log Group - if (!string.IsNullOrEmpty(logGroupName)) + switch (loggingSink) { - configuration = new(logGroupName) - { - Region = regionEndpoint, - Credentials = new BasicAWSCredentials(accessKey, secretKey) - }; - - //used for local testing only - if (!string.IsNullOrEmpty(localTestEndpoint)) - { - configuration.ServiceUrl = localTestEndpoint; - } - - return new LoggerConfiguration().WriteTo.AWSSeriLog(configuration) - .WriteTo.Console() - .CreateLogger(); - } + case "Seq": + + var serverUrl = _configurationManager["ModuleConfiguration:Logging:Seq:ServerUrl"]; + var apiKey = _configurationManager["ModuleConfiguration:Logging:Seq:ApiKey"]; + + return new LoggerConfiguration() + .WriteTo.Console() + .WriteTo.Seq(serverUrl: serverUrl, + apiKey: apiKey) + .CreateLogger(); + + case "CloudWatchLogs": + + var accessKey = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:AccessKey"]; + var secretKey = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:SecretKey"]; + var regionEndpoint = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:RegionEndpoint"]; + var localTestEndpoint = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:LocalTestEndpoint"]; + var logGroupName = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:LogGroupName"]; + + AWSLoggerConfig configuration; + + //If logGroupName is empty it uses the default AWS Lambad Log Group + if (!string.IsNullOrEmpty(logGroupName)) + { + configuration = new(logGroupName) + { + Region = regionEndpoint, + Credentials = new BasicAWSCredentials(accessKey, secretKey) + }; + + //used for local testing only + if (!string.IsNullOrEmpty(localTestEndpoint)) + { + configuration.ServiceUrl = localTestEndpoint; + } + + return new LoggerConfiguration().WriteTo.AWSSeriLog(configuration) + .WriteTo.Console() + .CreateLogger(); + } + + return new LoggerConfiguration().WriteTo.AWSSeriLog() + .WriteTo.Console() + .CreateLogger(); + + default: + throw new Exception("Logger Sink is not supported."); - return new LoggerConfiguration().WriteTo.AWSSeriLog() - .WriteTo.Console() - .CreateLogger(); + } } private static IConfiguration GetConfiguration(ConfigurationManager configurationManager) diff --git a/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs b/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs index 51bf6c1..57b88dc 100644 --- a/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs +++ b/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs @@ -1,4 +1,5 @@ -using ServiceName.Infrastructure.Repositories.DynamoDBModel; +using Microsoft.Extensions.Configuration; +using ServiceName.Infrastructure.Repositories.DynamoDBModel; namespace ServiceName.Test.Helpers { @@ -24,5 +25,28 @@ public static SettingDbRecord GetDynamoDBRecord(string tenantId) return null; } } + + internal static IConfigurationRoot GetConfigurationMock() + { + var configDictionary = new Dictionary + { + {"ModuleConfiguration:AwsServices:Kms:AccessKey", "test"}, + {"ModuleConfiguration:AwsServices:Kms:SecretKey", "test"}, + {"ModuleConfiguration:AwsServices:Kms:RegionEndpoint", "eu-west-2"}, + {"ModuleConfiguration:AwsServices:Kms:LocalTestEndpoint", "http://localhost:52002"}, + {"ModuleConfiguration:AwsServices:KMS:SigningKeyId", "6732c7ca-6ec9-4b96-9711-fd1c7d637c8e"}, + {"ModuleConfiguration:AwsServices:KMS:PublicKey", "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA56tfR6w3YJpbH5XZ6Ze2kB2evnUpbyZJiTLPKSc/VeA46m09lVB7bJRp0pKX2LusT2pccrVe5AYtbnikKqhOQWUdjLJnSONPNpd4yjEseqPblsXicA+xdP+Fk2W0yDxOc79LUAywgjV8JqNbbtVbhzqVPOLalJYnPEAVa3NQV138dnU7NzxbAjPjXINi7BBZ2OLRuocJRMfe16AUiQtH8MaWfRnnRRwdCBLJCXnZy+0hVc701SrVoTS+CA8RfGTCnzutx9MXW7t4SCEjZH0MSfhSZbKggPfi36HeUdClacgD6L0+FhSBKzd8kOC06CDf5WM9oV/XtWVXEWWGPDHv8wIDAQAB"}, + {"ModuleConfiguration:Jwt:Issuer", "Issuer"}, + {"ModuleConfiguration:Jwt:Audience", "Audience"}, + }; + + + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(configDictionary) + .Build(); + + return configuration; + } } } diff --git a/MicroserviceTemplate/tests/ServiceName.Test/JwtTests.cs b/MicroserviceTemplate/tests/ServiceName.Test/JwtTests.cs index 3c8684a..8b1bba4 100644 --- a/MicroserviceTemplate/tests/ServiceName.Test/JwtTests.cs +++ b/MicroserviceTemplate/tests/ServiceName.Test/JwtTests.cs @@ -1,6 +1,15 @@ -using Microsoft.Extensions.Configuration; +using System.Text; +using System.Text.Json; +using Amazon; +using Amazon.KeyManagementService; +using Amazon.KeyManagementService.Model; +using Microsoft.Extensions.Configuration; +using Moq; +using Moq.AutoMock; +using Serilog; using ServiceName.Core.Model; using ServiceName.Infrastructure.Authentication; +using ServiceName.Test.Helpers; namespace ServiceName.Test { @@ -8,20 +17,12 @@ public class JwtTests { AssymetricKmsJwtService _assymetricKmsJwtService; ModuleIdentity _moduleIdentity; + IConfiguration _configurationMock; public JwtTests() { - var configMock = new Dictionary - { - {"ModuleConfiguration:AwsServices:Kms:AccessKey", "test"}, - {"ModuleConfiguration:AwsServices:Kms:SecretKey", "test"}, - {"ModuleConfiguration:AwsServices:Kms:RegionEndpoint", "eu-west-2"}, - {"ModuleConfiguration:AwsServices:Kms:LocalTestEndpoint", "http://localhost:52002"}, - {"ModuleConfiguration:Jwt:SigningKeyId", "6732c7ca-6ec9-4b96-9711-fd1c7d637c8e"}, - {"ModuleConfiguration:Jwt:PublicKey", "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA56tfR6w3YJpbH5XZ6Ze2kB2evnUpbyZJiTLPKSc/VeA46m09lVB7bJRp0pKX2LusT2pccrVe5AYtbnikKqhOQWUdjLJnSONPNpd4yjEseqPblsXicA+xdP+Fk2W0yDxOc79LUAywgjV8JqNbbtVbhzqVPOLalJYnPEAVa3NQV138dnU7NzxbAjPjXINi7BBZ2OLRuocJRMfe16AUiQtH8MaWfRnnRRwdCBLJCXnZy+0hVc701SrVoTS+CA8RfGTCnzutx9MXW7t4SCEjZH0MSfhSZbKggPfi36HeUdClacgD6L0+FhSBKzd8kOC06CDf5WM9oV/XtWVXEWWGPDHv8wIDAQAB"}, - {"ModuleConfiguration:Jwt:Issuer", "Issuer"}, - {"ModuleConfiguration:Jwt:Audience", "Audience"}, - }; + var _autoMocker = new AutoMocker(); + _configurationMock = TestHelper.GetConfigurationMock(); _moduleIdentity = new() { @@ -30,13 +31,47 @@ public JwtTests() UserName = "UserName" }; - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(configMock) - .Build(); + //SignResponse mockSignResponse = JsonSerializer.Deserialize(@"{""KeyId"":""arn:aws:kms:eu-west-2:111122223333:key/6732c7ca-6ec9-4b96-9711-fd1c7d637c8e"",""Signature"":null,""SigningAlgorithm"":{""Value"":""RSASSA_PKCS1_V1_5_SHA_256""},""ResponseMetadata"":{""RequestId"":"""",""Metadata"":{},""ChecksumAlgorithm"":0,""ChecksumValidationStatus"":0},""ContentLength"":493,""HttpStatusCode"":200}"); + //byte[] byteArray = Encoding.UTF8.GetBytes("hzxyCYR5Zse5pzb49qr9ydvusAiPkCCYlr961/orhNUYLo0oOyLeBcW6rIlaI8id7TeIHtENCOrPGc8aUXxLjsWW4KuKthPaU/1LC3lBBaEzA1gs2VpRZajzWbCCPHhwcI522dypVi4TwabMgmlRh8iPD6QOxPexvtPnibetIcBwTZx6viLdepyz1mdd9RKAQprjSvI4K9Lm84NRaUXs969qfXlfKSRUVpDpWxWQ2pDnPt847WbDQZM8AR2U3aEfVN+56gilzOSE4LAlXPqgfRmzdtJzZA3Lv3wULBS96Eq1LkPfaXovk2yzU/dQL6/T3X/azDenl5kyymDovuCmvw=="); + //mockSignResponse.Signature = new MemoryStream(byteArray); + //_autoMocker.GetMock().Setup(x => x.SignAsync(It.IsAny(), default)).ReturnsAsync(mockSignResponse); - _assymetricKmsJwtService = new(configuration); + _autoMocker.Use(GetAmazonKms()); //This should be replaced with a SignResponse mock as response of the SignAsync method + _autoMocker.Use(_configurationMock); + _assymetricKmsJwtService = _autoMocker.CreateInstance(); } - + + /// + /// TODO: Move to Helper class + /// + /// + private IAmazonKeyManagementService GetAmazonKms() + { + var accessKey = _configurationMock["ModuleConfiguration:AwsServices:Kms:AccessKey"]; + var secretKey = _configurationMock["ModuleConfiguration:AwsServices:Kms:SecretKey"]; + var regionEndpoint = RegionEndpoint.GetBySystemName(_configurationMock["ModuleConfiguration:AwsServices:Kms:RegionEndpoint"]); + var localTestEndpoint = _configurationMock["ModuleConfiguration:AwsServices:Kms:LocalTestEndpoint"]; + + AmazonKeyManagementServiceConfig amazonKeyManagementServiceConfig = new() + { + RegionEndpoint = regionEndpoint, + }; + + if (!string.IsNullOrEmpty(localTestEndpoint)) + { + amazonKeyManagementServiceConfig.UseHttp = true; + amazonKeyManagementServiceConfig.ServiceURL = localTestEndpoint; + } + + if (!string.IsNullOrEmpty(accessKey) && !string.IsNullOrEmpty(secretKey)) + { + return new AmazonKeyManagementServiceClient(accessKey, secretKey, amazonKeyManagementServiceConfig); + } + + return new AmazonKeyManagementServiceClient(amazonKeyManagementServiceConfig); + } + + [Fact] public async Task JwtValidationOK() { From b1176abd75c43a202cb8a1b15708ecacb8bdd2ff Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Mon, 20 Jun 2022 13:14:33 +1000 Subject: [PATCH 21/56] [MicroserviceTemplate] Moved settings under Infrastructure section --- .../Extensions/AuthExtension.cs | 2 +- .../HealthCheck/DynamoDbHealthCheck.cs | 10 ++--- .../Extensions/HealthCheckExtensions.cs | 18 ++++++++- .../appsettings.Development.json | 35 +++++++++-------- .../src/ServiceName.API/appsettings.json | 29 ++++++++------ .../Authentication/AssymetricKmsJwtService.cs | 4 +- .../ConfigureServices.cs | 39 +++++++++++-------- .../ServiceName.Test/Helpers/TestHelper.cs | 12 +++--- .../tests/ServiceName.Test/JwtTests.cs | 10 ++--- 9 files changed, 93 insertions(+), 66 deletions(-) diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthExtension.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthExtension.cs index 34bc2b3..ce2efc5 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthExtension.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthExtension.cs @@ -22,7 +22,7 @@ public static void AddAuthSupport(this IServiceCollection services, Configuratio ValidateAudience = true, ValidIssuer = configurationManager["ModuleConfiguration:Jwt:Issuer"], ValidAudience = configurationManager["ModuleConfiguration:Jwt:Audience"], - IssuerSigningKey = SecurityHelper.GetRsaSecurityKey(configurationManager["ModuleConfiguration:AwsServices:KMS:PublicKey"]) + IssuerSigningKey = SecurityHelper.GetRsaSecurityKey(configurationManager["ModuleConfiguration:Infrastructure:Kms:PublicKey"]) }; }); diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs index cddacf7..a2e06a2 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs @@ -18,11 +18,11 @@ public Task CheckHealthAsync(HealthCheckContext context, Canc { try { - var accessKey = _configuration["ModuleConfiguration:AwsServices:DynamoDb:AccessKey"]; - var secretKey = _configuration["ModuleConfiguration:AwsServices:DynamoDb:SecretKey"]; - var regionEndpoint = RegionEndpoint.GetBySystemName(_configuration["ModuleConfiguration:AwsServices:DynamoDb:RegionEndpoint"]); - var tableName = _configuration["ModuleConfiguration:AwsServices:DynamoDb:TableName"]; - var localTestEndpoint = _configuration["ModuleConfiguration:AwsServices:DynamoDb:LocalTestEndpoint"]; + var accessKey = _configuration["ModuleConfiguration:Infrastructure:DynamoDb:AccessKey"]; + var secretKey = _configuration["ModuleConfiguration:Infrastructure:DynamoDb:SecretKey"]; + var regionEndpoint = RegionEndpoint.GetBySystemName(_configuration["ModuleConfiguration:Infrastructure:DynamoDb:RegionEndpoint"]); + var tableName = _configuration["ModuleConfiguration:Infrastructure:DynamoDb:TableName"]; + var localTestEndpoint = _configuration["ModuleConfiguration:Infrastructure:DynamoDb:LocalTestEndpoint"]; AmazonDynamoDBConfig clientConfig = new() { diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs index 06e26de..993269b 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs @@ -20,10 +20,24 @@ public static class HealthCheckExtensions public static void AddHealthCheckSupport(this IServiceCollection services, ConfigurationManager configurationManager) { + var sqlServerName = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Server"]; + var sqlPort = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Port"]; + var sqlDatabase = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Database"]; + var sqlUsername = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Username"]; + var sqlPassword = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Password"]; + + var sqlConnectionString = $"Data Source={sqlServerName},{sqlPort};Initial Catalog={sqlDatabase};User ID={sqlUsername};Password={sqlPassword}"; + + + var redisServerName = configurationManager["ModuleConfiguration:Infrastructure:Redis:Server"]; + var redisPort = configurationManager["ModuleConfiguration:Infrastructure:Redis:Port"]; + + var redisConnectionString = $"{redisServerName}:{redisPort}"; + services.AddHealthChecks() .AddCheck("dynamodb") - .AddSqlServer(configurationManager["ModuleConfiguration:ConnectionStrings:SqlServer"]) - .AddRedis(configurationManager["ModuleConfiguration:ConnectionStrings:Redis"]); + .AddSqlServer(sqlConnectionString) + .AddRedis(redisConnectionString); services.AddHealthChecksUI(s => { diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json index d398221..4606b7e 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json @@ -1,26 +1,15 @@ { "Logging": { + "Sink": "CloudWatchLogs", "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", - "ModuleConfiguration": - { + "ModuleConfiguration": { "IsSwaggerUIEnabled": true, - "ConnectionStrings": { - "SqlServer": "Data Source=host.docker.internal,1433;Initial Catalog=DBName;User ID=sa;Password=M3rz0ug4!!!!", - "Redis": "host.docker.internal:6379" - }, - "Logging": { - "Sink": "CloudWatchLogs", - "Seq": { - "ServerUrl": "http://localhost:5341", - "ApiKey": "" - } - }, - "AwsServices": { + "Infrastructure": { "Kms": { "AccessKey": "test", "SecretKey": "test", @@ -42,10 +31,24 @@ "RegionEndpoint": "ap-southeast-2", "LocalTestEndpoint": "http://host.docker.internal:8000", "TableName": "ServiceName_Setting" + }, + "Seq": { + "ServerUrl": "http://localhost:5341", + "ApiKey": "" + }, + "SqlServer": { + "Server": "host.docker.internal", + "Port": "1433", + "Database": "DBName", + "Username": "sa", + "Password": "M3rz0ug4!!!!" + }, + "Redis": { + "Server": "host.docker.internal", + "Port": "6379" } }, - "Jwt": - { + "Jwt": { "TestMode": true, "Issuer": "issuer", "Audience": "audience" diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.json index 5a68c6d..4606b7e 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.json @@ -1,5 +1,6 @@ { "Logging": { + "Sink": "CloudWatchLogs", "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" @@ -8,18 +9,7 @@ "AllowedHosts": "*", "ModuleConfiguration": { "IsSwaggerUIEnabled": true, - "ConnectionStrings": { - "SqlServer": "Data Source=host.docker.internal,1433;Initial Catalog=DBName;User ID=sa;Password=M3rz0ug4!!!!", - "Redis": "host.docker.internal:6379" - }, - "Logging": { - "Sink": "CloudWatchLogs", - "Seq": { - "ServerUrl": "http://localhost:5341", - "ApiKey": "" - } - }, - "AwsServices": { + "Infrastructure": { "Kms": { "AccessKey": "test", "SecretKey": "test", @@ -41,6 +31,21 @@ "RegionEndpoint": "ap-southeast-2", "LocalTestEndpoint": "http://host.docker.internal:8000", "TableName": "ServiceName_Setting" + }, + "Seq": { + "ServerUrl": "http://localhost:5341", + "ApiKey": "" + }, + "SqlServer": { + "Server": "host.docker.internal", + "Port": "1433", + "Database": "DBName", + "Username": "sa", + "Password": "M3rz0ug4!!!!" + }, + "Redis": { + "Server": "host.docker.internal", + "Port": "6379" } }, "Jwt": { diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs index c0d0b34..2a69638 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs @@ -37,7 +37,7 @@ public AssymetricKmsJwtService(IConfiguration configuration, IAmazonKeyManagemen public async Task GenerateTokenAsync(ModuleIdentity identity, int lifetimeSeconds, string issuer, string audience) { - var signingKeyId = _configuration["ModuleConfiguration:AwsServices:KMS:SigningKeyId"]; + var signingKeyId = _configuration["ModuleConfiguration:Infrastructure:Kms:SigningKeyId"]; if (string.IsNullOrWhiteSpace(signingKeyId)) { @@ -83,7 +83,7 @@ public async Task GenerateTokenAsync(ModuleIdentity identity, int lifeti public async Task ValidateTokenAsync(string token) { - RsaSecurityKey rsaSecurityKey = SecurityHelper.GetRsaSecurityKey(_configuration["ModuleConfiguration:AwsServices:KMS:PublicKey"]); + RsaSecurityKey rsaSecurityKey = SecurityHelper.GetRsaSecurityKey(_configuration["ModuleConfiguration:Infrastructure:Kms:PublicKey"]); var issuer = _configuration["ModuleConfiguration:Jwt:Issuer"]; var audience = _configuration["ModuleConfiguration:Jwt:Audience"]; diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs index f5644ad..648f30b 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs @@ -38,10 +38,10 @@ public static IServiceCollection AddInfrastructureServices(this IServiceCollecti private static IAmazonKeyManagementService GetAmazonKms() { - var accessKey = _configurationManager["ModuleConfiguration:AwsServices:Kms:AccessKey"]; - var secretKey = _configurationManager["ModuleConfiguration:AwsServices:Kms:SecretKey"]; - var regionEndpoint = RegionEndpoint.GetBySystemName(_configurationManager["ModuleConfiguration:AwsServices:Kms:RegionEndpoint"]); - var localTestEndpoint = _configurationManager["ModuleConfiguration:AwsServices:Kms:LocalTestEndpoint"]; + var accessKey = _configurationManager["ModuleConfiguration:Infrastructure:Kms:AccessKey"]; + var secretKey = _configurationManager["ModuleConfiguration:Infrastructure:Kms:SecretKey"]; + var regionEndpoint = RegionEndpoint.GetBySystemName(_configurationManager["ModuleConfiguration:Infrastructure:Kms:RegionEndpoint"]); + var localTestEndpoint = _configurationManager["ModuleConfiguration:Infrastructure:Kms:LocalTestEndpoint"]; AmazonKeyManagementServiceConfig amazonKeyManagementServiceConfig = new() { @@ -64,9 +64,14 @@ private static IAmazonKeyManagementService GetAmazonKms() private static IDistributedCache GetRedisCache() { + var redisServerName = _configurationManager["ModuleConfiguration:Infrastructure:Redis:Server"]; + var redisPort = _configurationManager["ModuleConfiguration:Infrastructure:Redis:Port"]; + + var redisConnectionString = $"{redisServerName}:{redisPort}"; + var cache = new RedisCache(new RedisCacheOptions { - Configuration = _configurationManager["ModuleConfiguration:ConnectionStrings:Redis"] + Configuration = redisConnectionString }); return cache; @@ -76,14 +81,14 @@ private static IDistributedCache GetRedisCache() private static ILogger GetLogger() { - var loggingSink = _configurationManager["ModuleConfiguration:Logging:Sink"]; + var loggingSink = _configurationManager["Logging:Sink"]; switch (loggingSink) { case "Seq": - var serverUrl = _configurationManager["ModuleConfiguration:Logging:Seq:ServerUrl"]; - var apiKey = _configurationManager["ModuleConfiguration:Logging:Seq:ApiKey"]; + var serverUrl = _configurationManager["ModuleConfiguration:Infrastructure:Seq:ServerUrl"]; + var apiKey = _configurationManager["ModuleConfiguration:Infrastructure:Seq:ApiKey"]; return new LoggerConfiguration() .WriteTo.Console() @@ -93,11 +98,11 @@ private static ILogger GetLogger() case "CloudWatchLogs": - var accessKey = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:AccessKey"]; - var secretKey = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:SecretKey"]; - var regionEndpoint = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:RegionEndpoint"]; - var localTestEndpoint = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:LocalTestEndpoint"]; - var logGroupName = _configurationManager["ModuleConfiguration:AwsServices:CloudWatchLogs:LogGroupName"]; + var accessKey = _configurationManager["ModuleConfiguration:Infrastructure:CloudWatchLogs:AccessKey"]; + var secretKey = _configurationManager["ModuleConfiguration:Infrastructure:CloudWatchLogs:SecretKey"]; + var regionEndpoint = _configurationManager["ModuleConfiguration:Infrastructure:CloudWatchLogs:RegionEndpoint"]; + var localTestEndpoint = _configurationManager["ModuleConfiguration:Infrastructure:CloudWatchLogs:LocalTestEndpoint"]; + var logGroupName = _configurationManager["ModuleConfiguration:Infrastructure:CloudWatchLogs:LogGroupName"]; AWSLoggerConfig configuration; @@ -145,10 +150,10 @@ private static IConfiguration GetConfiguration(ConfigurationManager configuratio private static DynamoDBContext GetDynamoDBContext() { - var accessKey = _configurationManager["ModuleConfiguration:AwsServices:DynamoDb:AccessKey"]; - var secretKey = _configurationManager["ModuleConfiguration:AwsServices:DynamoDb:SecretKey"]; - var regionEndpoint = RegionEndpoint.GetBySystemName(_configurationManager["ModuleConfiguration:AwsServices:DynamoDb:RegionEndpoint"]); - var localTestEndpoint = _configurationManager["ModuleConfiguration:AwsServices:DynamoDb:LocalTestEndpoint"]; + var accessKey = _configurationManager["ModuleConfiguration:Infrastructure:DynamoDb:AccessKey"]; + var secretKey = _configurationManager["ModuleConfiguration:Infrastructure:DynamoDb:SecretKey"]; + var regionEndpoint = RegionEndpoint.GetBySystemName(_configurationManager["ModuleConfiguration:Infrastructure:DynamoDb:RegionEndpoint"]); + var localTestEndpoint = _configurationManager["ModuleConfiguration:Infrastructure:DynamoDb:LocalTestEndpoint"]; var dynamoDBContextConfig = new DynamoDBContextConfig() { ConsistentRead = true }; diff --git a/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs b/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs index 57b88dc..549d22a 100644 --- a/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs +++ b/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs @@ -30,12 +30,12 @@ internal static IConfigurationRoot GetConfigurationMock() { var configDictionary = new Dictionary { - {"ModuleConfiguration:AwsServices:Kms:AccessKey", "test"}, - {"ModuleConfiguration:AwsServices:Kms:SecretKey", "test"}, - {"ModuleConfiguration:AwsServices:Kms:RegionEndpoint", "eu-west-2"}, - {"ModuleConfiguration:AwsServices:Kms:LocalTestEndpoint", "http://localhost:52002"}, - {"ModuleConfiguration:AwsServices:KMS:SigningKeyId", "6732c7ca-6ec9-4b96-9711-fd1c7d637c8e"}, - {"ModuleConfiguration:AwsServices:KMS:PublicKey", "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA56tfR6w3YJpbH5XZ6Ze2kB2evnUpbyZJiTLPKSc/VeA46m09lVB7bJRp0pKX2LusT2pccrVe5AYtbnikKqhOQWUdjLJnSONPNpd4yjEseqPblsXicA+xdP+Fk2W0yDxOc79LUAywgjV8JqNbbtVbhzqVPOLalJYnPEAVa3NQV138dnU7NzxbAjPjXINi7BBZ2OLRuocJRMfe16AUiQtH8MaWfRnnRRwdCBLJCXnZy+0hVc701SrVoTS+CA8RfGTCnzutx9MXW7t4SCEjZH0MSfhSZbKggPfi36HeUdClacgD6L0+FhSBKzd8kOC06CDf5WM9oV/XtWVXEWWGPDHv8wIDAQAB"}, + {"ModuleConfiguration:Infrastructure:Kms:AccessKey", "test"}, + {"ModuleConfiguration:Infrastructure:Kms:SecretKey", "test"}, + {"ModuleConfiguration:Infrastructure:Kms:RegionEndpoint", "eu-west-2"}, + {"ModuleConfiguration:Infrastructure:Kms:LocalTestEndpoint", "http://localhost:52002"}, + {"ModuleConfiguration:Infrastructure:Kms:SigningKeyId", "6732c7ca-6ec9-4b96-9711-fd1c7d637c8e"}, + {"ModuleConfiguration:Infrastructure:Kms:PublicKey", "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA56tfR6w3YJpbH5XZ6Ze2kB2evnUpbyZJiTLPKSc/VeA46m09lVB7bJRp0pKX2LusT2pccrVe5AYtbnikKqhOQWUdjLJnSONPNpd4yjEseqPblsXicA+xdP+Fk2W0yDxOc79LUAywgjV8JqNbbtVbhzqVPOLalJYnPEAVa3NQV138dnU7NzxbAjPjXINi7BBZ2OLRuocJRMfe16AUiQtH8MaWfRnnRRwdCBLJCXnZy+0hVc701SrVoTS+CA8RfGTCnzutx9MXW7t4SCEjZH0MSfhSZbKggPfi36HeUdClacgD6L0+FhSBKzd8kOC06CDf5WM9oV/XtWVXEWWGPDHv8wIDAQAB"}, {"ModuleConfiguration:Jwt:Issuer", "Issuer"}, {"ModuleConfiguration:Jwt:Audience", "Audience"}, }; diff --git a/MicroserviceTemplate/tests/ServiceName.Test/JwtTests.cs b/MicroserviceTemplate/tests/ServiceName.Test/JwtTests.cs index 8b1bba4..3b162c0 100644 --- a/MicroserviceTemplate/tests/ServiceName.Test/JwtTests.cs +++ b/MicroserviceTemplate/tests/ServiceName.Test/JwtTests.cs @@ -31,7 +31,7 @@ public JwtTests() UserName = "UserName" }; - //SignResponse mockSignResponse = JsonSerializer.Deserialize(@"{""KeyId"":""arn:aws:kms:eu-west-2:111122223333:key/6732c7ca-6ec9-4b96-9711-fd1c7d637c8e"",""Signature"":null,""SigningAlgorithm"":{""Value"":""RSASSA_PKCS1_V1_5_SHA_256""},""ResponseMetadata"":{""RequestId"":"""",""Metadata"":{},""ChecksumAlgorithm"":0,""ChecksumValidationStatus"":0},""ContentLength"":493,""HttpStatusCode"":200}"); + //SignResponse mockSignResponse = JsonSerializer.Deserialize(@"{""KeyId"":""arn:aws:Kms:eu-west-2:111122223333:key/6732c7ca-6ec9-4b96-9711-fd1c7d637c8e"",""Signature"":null,""SigningAlgorithm"":{""Value"":""RSASSA_PKCS1_V1_5_SHA_256""},""ResponseMetadata"":{""RequestId"":"""",""Metadata"":{},""ChecksumAlgorithm"":0,""ChecksumValidationStatus"":0},""ContentLength"":493,""HttpStatusCode"":200}"); //byte[] byteArray = Encoding.UTF8.GetBytes("hzxyCYR5Zse5pzb49qr9ydvusAiPkCCYlr961/orhNUYLo0oOyLeBcW6rIlaI8id7TeIHtENCOrPGc8aUXxLjsWW4KuKthPaU/1LC3lBBaEzA1gs2VpRZajzWbCCPHhwcI522dypVi4TwabMgmlRh8iPD6QOxPexvtPnibetIcBwTZx6viLdepyz1mdd9RKAQprjSvI4K9Lm84NRaUXs969qfXlfKSRUVpDpWxWQ2pDnPt847WbDQZM8AR2U3aEfVN+56gilzOSE4LAlXPqgfRmzdtJzZA3Lv3wULBS96Eq1LkPfaXovk2yzU/dQL6/T3X/azDenl5kyymDovuCmvw=="); //mockSignResponse.Signature = new MemoryStream(byteArray); //_autoMocker.GetMock().Setup(x => x.SignAsync(It.IsAny(), default)).ReturnsAsync(mockSignResponse); @@ -47,10 +47,10 @@ public JwtTests() /// private IAmazonKeyManagementService GetAmazonKms() { - var accessKey = _configurationMock["ModuleConfiguration:AwsServices:Kms:AccessKey"]; - var secretKey = _configurationMock["ModuleConfiguration:AwsServices:Kms:SecretKey"]; - var regionEndpoint = RegionEndpoint.GetBySystemName(_configurationMock["ModuleConfiguration:AwsServices:Kms:RegionEndpoint"]); - var localTestEndpoint = _configurationMock["ModuleConfiguration:AwsServices:Kms:LocalTestEndpoint"]; + var accessKey = _configurationMock["ModuleConfiguration:Infrastructure:Kms:AccessKey"]; + var secretKey = _configurationMock["ModuleConfiguration:Infrastructure:Kms:SecretKey"]; + var regionEndpoint = RegionEndpoint.GetBySystemName(_configurationMock["ModuleConfiguration:Infrastructure:Kms:RegionEndpoint"]); + var localTestEndpoint = _configurationMock["ModuleConfiguration:Infrastructure:Kms:LocalTestEndpoint"]; AmazonKeyManagementServiceConfig amazonKeyManagementServiceConfig = new() { From 89d44245c5439d4623c837b936023ddc5a08c5c0 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Mon, 20 Jun 2022 13:55:37 +1000 Subject: [PATCH 22/56] [MicroserviceTemplate] Get TenantId from JWT --- .../Extensions/EndpointExtensions.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs index 5dbe93a..ad01e10 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs @@ -1,4 +1,5 @@ -using Asp.Versioning.Conventions; +using System.IdentityModel.Tokens.Jwt; +using Asp.Versioning.Conventions; using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -21,7 +22,7 @@ public static void MapEndpoints(this WebApplication app, ConfigurationManager co .Build(); - app.MapGet("/settings/{tenantId}", [Authorize] async (IMediator mediator, string tenantId) => await mediator.Send(new GetSettingsQueryRequest() { TenantId = Guid.Parse(tenantId) })) + app.MapGet("/settings", [Authorize] async (IMediator mediator, [FromHeader] string authorization) => await mediator.Send(new GetSettingsQueryRequest() { TenantId = GetTenantIdFromJwt(authorization) })) .WithApiVersionSet(versionSet) .MapToApiVersion(1.0); @@ -29,14 +30,14 @@ public static void MapEndpoints(this WebApplication app, ConfigurationManager co // .WithApiVersionSet(versionSet) // .MapToApiVersion(2.0); - app.MapPost("/settings/{tenantId}", [Authorize] async (IMediator mediator, [FromBody] Settings settings, string tenantId) => await mediator.Send(new SaveSettingsCommandRequest() { TenantId = Guid.Parse(tenantId), Settings = settings })) + app.MapPost("/settings", [Authorize] async (IMediator mediator, [FromHeader] string authorization, [FromBody] Settings settings) => await mediator.Send(new SaveSettingsCommandRequest() { TenantId = GetTenantIdFromJwt(authorization), Settings = settings })) .WithApiVersionSet(versionSet) .MapToApiVersion(1.0); //These endpoints are only for JWT Testing & Troubleshooting Purposes if (bool.Parse(configuration["ModuleConfiguration:Jwt:TestMode"])) { - app.MapPost("/test/token/generate", [AllowAnonymous] async (IJwtAuthenticationService authService, string issuer, string audience) => await authService.GenerateTokenAsync(new ModuleIdentity(), 60, issuer, audience)) + app.MapPost("/test/token/generate", [AllowAnonymous] async (IJwtAuthenticationService authService, string tenantId, string issuer, string audience) => await authService.GenerateTokenAsync(new ModuleIdentity() { InstanceGuid = tenantId }, 60, issuer, audience)) .WithApiVersionSet(versionSet) .MapToApiVersion(1.0); @@ -45,5 +46,13 @@ public static void MapEndpoints(this WebApplication app, ConfigurationManager co .MapToApiVersion(1.0); } } + + private static Guid GetTenantIdFromJwt(string token) + { + token = token.Replace("Bearer ", ""); + var jwtToken = new JwtSecurityToken(token); + var tenantId = jwtToken.Payload.FirstOrDefault(p => p.Key.Equals("custom:tenantId")).Value.ToString(); + return Guid.Parse(tenantId); + } } } \ No newline at end of file From e64ef7e49f015508462410244aee59b27919d7b9 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Wed, 22 Jun 2022 08:20:17 +1000 Subject: [PATCH 23/56] [MicroserviceTemplate] Moved Authentication to its own service --- .../Authentication.API.csproj | 28 +++++ .../Extensions/AuthExtension.cs | 38 +++++++ .../Extensions/EndpointExtensions.cs | 28 +++++ .../HealthCheck/DynamoDbHealthCheck.cs | 52 +++++++++ .../Extensions/HealthCheckExtensions.cs | 62 +++++++++++ .../Swagger/ConfigureSwaggerOptions.cs | 55 +++++++++ .../Swagger/SwaggerDefaultValues.cs | 48 ++++++++ .../Extensions/SwaggerExtensions.cs | 67 +++++++++++ .../Extensions/VersionExtension.cs | 21 ++++ .../src/Authentication.API/Program.cs | 52 +++++++++ .../Properties/launchSettings.json | 18 +++ .../src/Authentication.API/Readme.md | 51 +++++++++ .../appsettings.Development.json | 56 ++++++++++ .../src/Authentication.API/appsettings.json | 56 ++++++++++ .../aws-lambda-tools-defaults.json | 14 +++ .../Authentication.API/serverless.template | 47 ++++++++ .../Extensions/EndpointExtensions.cs | 12 -- .../Swagger/ConfigureSwaggerOptions.cs | 2 +- .../Swagger/SwaggerDefaultValues.cs | 6 +- .../appsettings.Development.json | 105 +++++++++--------- .../src/ServiceName.API/appsettings.json | 1 - MicroserviceTemplate/src/ServiceTemplate.sln | 9 +- 22 files changed, 758 insertions(+), 70 deletions(-) create mode 100644 MicroserviceTemplate/src/Authentication.API/Authentication.API.csproj create mode 100644 MicroserviceTemplate/src/Authentication.API/Extensions/AuthExtension.cs create mode 100644 MicroserviceTemplate/src/Authentication.API/Extensions/EndpointExtensions.cs create mode 100644 MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs create mode 100644 MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheckExtensions.cs create mode 100644 MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/ConfigureSwaggerOptions.cs create mode 100644 MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/SwaggerDefaultValues.cs create mode 100644 MicroserviceTemplate/src/Authentication.API/Extensions/SwaggerExtensions.cs create mode 100644 MicroserviceTemplate/src/Authentication.API/Extensions/VersionExtension.cs create mode 100644 MicroserviceTemplate/src/Authentication.API/Program.cs create mode 100644 MicroserviceTemplate/src/Authentication.API/Properties/launchSettings.json create mode 100644 MicroserviceTemplate/src/Authentication.API/Readme.md create mode 100644 MicroserviceTemplate/src/Authentication.API/appsettings.Development.json create mode 100644 MicroserviceTemplate/src/Authentication.API/appsettings.json create mode 100644 MicroserviceTemplate/src/Authentication.API/aws-lambda-tools-defaults.json create mode 100644 MicroserviceTemplate/src/Authentication.API/serverless.template diff --git a/MicroserviceTemplate/src/Authentication.API/Authentication.API.csproj b/MicroserviceTemplate/src/Authentication.API/Authentication.API.csproj new file mode 100644 index 0000000..aa28b3e --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/Authentication.API.csproj @@ -0,0 +1,28 @@ + + + net6.0 + enable + enable + true + Lambda + + true + + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/AuthExtension.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/AuthExtension.cs new file mode 100644 index 0000000..ce2efc5 --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/Extensions/AuthExtension.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; +using ServiceName.Core.Common.Security; + +namespace ServiceName.API.Extensions +{ + public static class SecurityExtension + { + public static void AddAuthSupport(this IServiceCollection services, ConfigurationManager configurationManager) + { + services.AddAuthentication(o => + { + o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; + }).AddJwtBearer(o => + { + o.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + ValidateIssuer = true, + ValidateAudience = true, + ValidIssuer = configurationManager["ModuleConfiguration:Jwt:Issuer"], + ValidAudience = configurationManager["ModuleConfiguration:Jwt:Audience"], + IssuerSigningKey = SecurityHelper.GetRsaSecurityKey(configurationManager["ModuleConfiguration:Infrastructure:Kms:PublicKey"]) + }; + }); + + services.AddAuthorization(); + } + + public static void UseAuth(this WebApplication app) { + + app.UseAuthentication(); + app.UseAuthorization(); + } + } +} diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/EndpointExtensions.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/EndpointExtensions.cs new file mode 100644 index 0000000..797130c --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/Extensions/EndpointExtensions.cs @@ -0,0 +1,28 @@ +using Asp.Versioning.Conventions; +using Microsoft.AspNetCore.Authorization; +using ServiceName.Core.Common.Interfaces; +using ServiceName.Core.Model; + +namespace ServiceName.API.Extensions +{ + public static class EndpointExtensions + { + public static void MapEndpoints(this WebApplication app, ConfigurationManager configuration) + { + // define a 'version set' that applies to an API group + var versionSet = app.NewApiVersionSet() + .HasApiVersion(1.0) + //.HasApiVersion(2.0) + .ReportApiVersions() + .Build(); + + app.MapPost("token/generate", [AllowAnonymous] async (IJwtAuthenticationService authService, string tenantId, string issuer, string audience) => await authService.GenerateTokenAsync(new ModuleIdentity() { InstanceGuid = tenantId }, 60, issuer, audience)) + .WithApiVersionSet(versionSet) + .MapToApiVersion(1.0); + + app.MapPost("token/validate", [AllowAnonymous] async (IJwtAuthenticationService authService, string token) => await authService.ValidateTokenAsync(token)) + .WithApiVersionSet(versionSet) + .MapToApiVersion(1.0); + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs new file mode 100644 index 0000000..a2e06a2 --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs @@ -0,0 +1,52 @@ +using Amazon; +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.DocumentModel; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace ServiceName.API.Extensions.HealthCheck +{ + public class DynamoDbHealthCheck : IHealthCheck + { + readonly IConfiguration _configuration; + + public DynamoDbHealthCheck(IConfiguration configuration) + { + _configuration = configuration; + } + + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + try + { + var accessKey = _configuration["ModuleConfiguration:Infrastructure:DynamoDb:AccessKey"]; + var secretKey = _configuration["ModuleConfiguration:Infrastructure:DynamoDb:SecretKey"]; + var regionEndpoint = RegionEndpoint.GetBySystemName(_configuration["ModuleConfiguration:Infrastructure:DynamoDb:RegionEndpoint"]); + var tableName = _configuration["ModuleConfiguration:Infrastructure:DynamoDb:TableName"]; + var localTestEndpoint = _configuration["ModuleConfiguration:Infrastructure:DynamoDb:LocalTestEndpoint"]; + + AmazonDynamoDBConfig clientConfig = new() + { + RegionEndpoint = regionEndpoint, + }; + + if (!string.IsNullOrEmpty(localTestEndpoint)) + { + clientConfig.UseHttp = true; + clientConfig.ServiceURL = localTestEndpoint; + } + + var amazonDynamoDBClient = new AmazonDynamoDBClient(accessKey, secretKey, clientConfig); + + Table.LoadTable(amazonDynamoDBClient, tableName); + + return Task.FromResult(HealthCheckResult.Healthy($"Table {tableName} exists.")); + } + catch (Exception ex) + { + return Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus, ex.Message)); + } + + + } + } +} diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheckExtensions.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheckExtensions.cs new file mode 100644 index 0000000..993269b --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheckExtensions.cs @@ -0,0 +1,62 @@ +using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using ServiceName.API.Extensions.HealthCheck; + +namespace ServiceName.API.Extensions +{ + /// + /// https://docs.steeltoe.io/api/v3/management/health.html + /// https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-6.0 + /// https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/monitor-app-health + /// https://blog.zhaytam.com/2020/04/30/health-checks-aspnetcore/ + + //AspNetCore.HealthChecks.UI which adds the UI. + //AspNetCore.HealthChecks.UI.Client which turns our old response(e.g.Healthy) into a more detailed response. + //AspNetCore.HealthChecks.UI.InMemory.Storage which saves the results in memory for the UI to use. + + /// + public static class HealthCheckExtensions + { + + public static void AddHealthCheckSupport(this IServiceCollection services, ConfigurationManager configurationManager) + { + var sqlServerName = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Server"]; + var sqlPort = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Port"]; + var sqlDatabase = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Database"]; + var sqlUsername = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Username"]; + var sqlPassword = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Password"]; + + var sqlConnectionString = $"Data Source={sqlServerName},{sqlPort};Initial Catalog={sqlDatabase};User ID={sqlUsername};Password={sqlPassword}"; + + + var redisServerName = configurationManager["ModuleConfiguration:Infrastructure:Redis:Server"]; + var redisPort = configurationManager["ModuleConfiguration:Infrastructure:Redis:Port"]; + + var redisConnectionString = $"{redisServerName}:{redisPort}"; + + services.AddHealthChecks() + .AddCheck("dynamodb") + .AddSqlServer(sqlConnectionString) + .AddRedis(redisConnectionString); + + services.AddHealthChecksUI(s => + { + s.AddHealthCheckEndpoint("ServiceName", "/health"); + }).AddInMemoryStorage(); + } + + public static void ConfigureHealthCheck(this WebApplication app) + { + app.UseHealthChecksUI(config => config.UIPath = "/health-ui"); + + app.MapHealthChecksUI(); + + app.MapHealthChecks("/health", new HealthCheckOptions() + { + Predicate = _ => true, + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse, + AllowCachingResponses = false + }); + } + } +} diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/ConfigureSwaggerOptions.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/ConfigureSwaggerOptions.cs new file mode 100644 index 0000000..a3abfc9 --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/ConfigureSwaggerOptions.cs @@ -0,0 +1,55 @@ +namespace ServiceName.API.Extensions.Swagger +{ + using Asp.Versioning.ApiExplorer; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Options; + using Microsoft.OpenApi.Models; + using Swashbuckle.AspNetCore.SwaggerGen; + using System; + + /// + /// Configures the Swagger generation options. + /// + /// This allows API versioning to define a Swagger document per API version after the + /// service has been resolved from the service container. + public class ConfigureSwaggerOptions : IConfigureOptions + { + readonly IApiVersionDescriptionProvider provider; + + /// + /// Initializes a new instance of the class. + /// + /// The provider used to generate Swagger documents. + public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => this.provider = provider; + + /// + public void Configure(SwaggerGenOptions options) + { + // add a swagger document for each discovered API version + // note: you might choose to skip or document deprecated API versions differently + foreach (var description in provider.ApiVersionDescriptions) + { + options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); + } + } + + static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) + { + var info = new OpenApiInfo() + { + Title = "Authentication API", + Version = description.ApiVersion.ToString(), + Description = "A sample application with Swagger, Swashbuckle, and API versioning.", + Contact = new OpenApiContact() { Name = "Leandro", Email = "leandro@somewhere.com" }, + License = new OpenApiLicense() { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") } + }; + + if (description.IsDeprecated) + { + info.Description += " This API version has been deprecated."; + } + + return info; + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/SwaggerDefaultValues.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/SwaggerDefaultValues.cs new file mode 100644 index 0000000..af07330 --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/SwaggerDefaultValues.cs @@ -0,0 +1,48 @@ +namespace ServiceName.API.Extensions.Swagger +{ + using Microsoft.AspNetCore.Mvc.ApiExplorer; + using Microsoft.OpenApi.Any; + using Microsoft.OpenApi.Models; + using Swashbuckle.AspNetCore.SwaggerGen; + using System.Linq; + + /// + /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter. + /// + /// This is only required due to bugs in the . + /// Once they are fixed and published, this class can be removed. + public class SwaggerDefaultValues : IOperationFilter + { + /// + /// Applies the filter to the specified operation using the given context. + /// + /// The operation to apply the filter to. + /// The current operation filter context. + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + var apiDescription = context.ApiDescription; + operation.Deprecated |= apiDescription.IsDeprecated(); + + if (operation.Parameters == null) + return; + + // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412 + // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413 + foreach (var parameter in operation.Parameters) + { + var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); + if (parameter.Description == null) + { + parameter.Description = description.ModelMetadata?.Description; + } + + if (parameter.Schema.Default == null && description.DefaultValue != null) + { + parameter.Schema.Default = new OpenApiString(description.DefaultValue.ToString()); + } + + parameter.Required |= description.IsRequired; + } + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/SwaggerExtensions.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/SwaggerExtensions.cs new file mode 100644 index 0000000..ea1e41a --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/Extensions/SwaggerExtensions.cs @@ -0,0 +1,67 @@ +using Microsoft.Extensions.Options; +using Microsoft.OpenApi.Models; +using ServiceName.API.Extensions.Swagger; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace ServiceName.API.Extensions +{ + public static class SwaggerExtensions + { + public static void AddSwaggerSupport(this IServiceCollection services) + { + services.AddTransient, ConfigureSwaggerOptions>(); + + services.AddSwaggerGen(options => + { + options.OperationFilter(); + + var securityScheme = new OpenApiSecurityScheme() + { + Name = "Authorization", + Type = SecuritySchemeType.ApiKey, + Scheme = "Bearer", + BearerFormat = "JWT", + In = ParameterLocation.Header, + Description = "JSON Web Token based security", + }; + + var securityReq = new OpenApiSecurityRequirement() + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + new string[] {} + } + }; + + options.AddSecurityDefinition("Bearer", securityScheme); + options.AddSecurityRequirement(securityReq); + }); + } + + public static void ConfigureSwaggerUI(this WebApplication app) + { + app.UseSwagger(); + app.UseSwaggerUI(options => + { + options.RoutePrefix = string.Empty; + + var descriptions = app.DescribeApiVersions(); + + // build a swagger endpoint for each discovered API version + foreach (var description in descriptions) + { + var url = $"./swagger/{description.GroupName}/swagger.json"; + var name = description.GroupName.ToUpperInvariant(); + options.SwaggerEndpoint(url, name); + } + }); + } + } +} diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/VersionExtension.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/VersionExtension.cs new file mode 100644 index 0000000..3ceea13 --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/Extensions/VersionExtension.cs @@ -0,0 +1,21 @@ +namespace ServiceName.API.Extensions +{ + public static class VersionExtension + { + public static void AddApiVersioningSupport(this IServiceCollection services) + { + services.AddApiVersioning() + .AddApiExplorer( + options => + { + // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service + // note: the specified format code will format the version as "'v'major[.minor][-status]" + options.GroupNameFormat = "'v'VVV"; + + // note: this option is only necessary when versioning by url segment. the SubstitutionFormat + // can also be used to control the format of the API version in route templates + options.SubstituteApiVersionInUrl = true; + }); + } + } +} diff --git a/MicroserviceTemplate/src/Authentication.API/Program.cs b/MicroserviceTemplate/src/Authentication.API/Program.cs new file mode 100644 index 0000000..b78b970 --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/Program.cs @@ -0,0 +1,52 @@ +using System.Text; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; +using ServiceName.API.Extensions; +using ServiceName.Core; +using ServiceName.Core.Common.Security; +using ServiceName.Infrastructure; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddControllers(); + +// Add AWS Lambda support. When application is run in Lambda Kestrel is swapped out as the web server with Amazon.Lambda.AspNetCoreServer. This +// package will act as the webserver translating request and responses between the Lambda event source and ASP.NET Core. +// https://aws.amazon.com/blogs/compute/introducing-the-net-6-runtime-for-aws-lambda/ + +builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi); + +builder.Services.AddInfrastructureServices(builder.Configuration); + +builder.Services.AddApplicationServices(); + +builder.Services.AddApiVersioningSupport(); + +builder.Services.AddSwaggerSupport(); + +builder.Services.AddHealthCheckSupport(builder.Configuration); + +builder.Services.AddAuthSupport(builder.Configuration); + +builder.Services.AddEndpointsApiExplorer(); + +var app = builder.Build(); + +app.MapControllers(); + +app.MapEndpoints(builder.Configuration); + +app.ConfigureHealthCheck(); + +if (bool.Parse(builder.Configuration["ModuleConfiguration:IsSwaggerUIEnabled"])) +{ + app.ConfigureSwaggerUI(); +} + +app.UseAuth(); + +app.UseHttpsRedirection(); + +app.Run(); + diff --git a/MicroserviceTemplate/src/Authentication.API/Properties/launchSettings.json b/MicroserviceTemplate/src/Authentication.API/Properties/launchSettings.json new file mode 100644 index 0000000..fc4a0ac --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/Properties/launchSettings.json @@ -0,0 +1,18 @@ +{ + "profiles": { + "Authentication.API": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:62060;http://localhost:62061" + }, + "Mock Lambda Test Tool": { + "commandName": "Executable", + "commandLineArgs": "--port 5050", + "workingDirectory": ".\\bin\\$(Configuration)\\net6.0", + "executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe" + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Readme.md b/MicroserviceTemplate/src/Authentication.API/Readme.md new file mode 100644 index 0000000..3812777 --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/Readme.md @@ -0,0 +1,51 @@ +# ASP.NET Core Minimal API Serverless Application + +This project shows how to run an ASP.NET Core Web API project as an AWS Lambda exposed through Amazon API Gateway. The NuGet package [Amazon.Lambda.AspNetCoreServer](https://www.nuget.org/packages/Amazon.Lambda.AspNetCoreServer) contains a Lambda function that is used to translate requests from API Gateway into the ASP.NET Core framework and then the responses from ASP.NET Core back to API Gateway. + + +For more information about how the Amazon.Lambda.AspNetCoreServer package works and how to extend its behavior view its [README](https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.AspNetCoreServer/README.md) file in GitHub. + +## Executable Assembly ## + +.NET Lambda projects that use C# top level statements like this project must be deployed as an executable assembly instead of a class library. To indicate to Lambda that the .NET function is an executable assembly the +Lambda function handler value is set to the .NET Assembly name. This is different then deploying as a class library where the function handler string includes the assembly, type and method name. + +To deploy as an executable assembly the Lambda runtime client must be started to listen for incoming events to process. For an ASP.NET Core application the Lambda runtime client is started by included the +`Amazon.Lambda.AspNetCoreServer.Hosting` NuGet package and calling `AddAWSLambdaHosting(LambdaEventSource.HttpApi)` passing in the event source while configuring the services of the application. The +event source can be API Gateway REST API and HTTP API or Application Load Balancer. + +### Project Files ### + +* serverless.template - an AWS CloudFormation Serverless Application Model template file for declaring your Serverless functions and other AWS resources +* aws-lambda-tools-defaults.json - default argument settings for use with Visual Studio and command line deployment tools for AWS +* Program.cs - entry point to the application that contains all of the top level statements initializing the ASP.NET Core application. +The call to `AddAWSLambdaHosting` configures the application to work in Lambda when it detects Lambda is the executing environment. +* Controllers\CalculatorController - example Web API controller + +You may also have a test project depending on the options selected. + +## Here are some steps to follow from Visual Studio: + +To deploy your Serverless application, right click the project in Solution Explorer and select *Publish to AWS Lambda*. + +To view your deployed application open the Stack View window by double-clicking the stack name shown beneath the AWS CloudFormation node in the AWS Explorer tree. The Stack View also displays the root URL to your published application. + +## Here are some steps to follow to get started from the command line: + +Once you have edited your template and code you can deploy your application using the [Amazon.Lambda.Tools Global Tool](https://github.com/aws/aws-extensions-for-dotnet-cli#aws-lambda-amazonlambdatools) from the command line. + +Install Amazon.Lambda.Tools Global Tools if not already installed. +``` + dotnet tool install -g Amazon.Lambda.Tools +``` + +If already installed check if new version is available. +``` + dotnet tool update -g Amazon.Lambda.Tools +``` + +Deploy application +``` + cd "MinimalApi/src/MinimalApi" + dotnet lambda deploy-serverless +``` diff --git a/MicroserviceTemplate/src/Authentication.API/appsettings.Development.json b/MicroserviceTemplate/src/Authentication.API/appsettings.Development.json new file mode 100644 index 0000000..a219566 --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/appsettings.Development.json @@ -0,0 +1,56 @@ +{ + "Logging": { + "Sink": "CloudWatchLogs", + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ModuleConfiguration": { + "IsSwaggerUIEnabled": true, + "Infrastructure": { + "Kms": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "eu-west-2", + "LocalTestEndpoint": "http://localhost:52002", + "SigningKeyId": "40ab43d1-5352-4b4e-b9a7-11ed401d2411", + "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu\u002BFcI4zsZvOoCvdIVhMdqJihfwiGGb9BADR\u002BWPD9WifnrNZJYNb4iHQCSCxRmteUjLNqjGgq/dKupFbSOxA22Cx8A63a6SydFpE1eeGARC5LsxD2H37Y6wJJYKtGhpLEXB/mQfo/vTg34bCWVVlY\u002ByM50HpQNTglt4c61FMTtDljXEq4RmP0vRwiQlUB\u002BrPBrhCPNXCXHdSO4GNRYfqBeY41sR5Ezad5C6QCgRzFKIGllQvorLbLXCJ3m9\u002B4I69l3Mtk\u002BHSmu1rNNne2XPzg25XM4JUVC3tOzvaPZXs\u002BAPu4yRpDRleYnAC1hAiUzMqW5NhkBV05aQ6xzFuQaoJ2xwIDAQAB" + }, + "CloudWatchLogs": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "LocalTestEndpoint": "http://host.docker.internal:4566", + "LogGroupName": "/LocalStack/Microservice/Logs" + }, + "DynamoDb": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "LocalTestEndpoint": "http://host.docker.internal:8000", + "TableName": "ServiceName_Setting" + }, + "Seq": { + "ServerUrl": "http://localhost:5341", + "ApiKey": "" + }, + "SqlServer": { + "Server": "host.docker.internal", + "Port": "1433", + "Database": "DBName", + "Username": "sa", + "Password": "M3rz0ug4!!!!" + }, + "Redis": { + "Server": "host.docker.internal", + "Port": "6379" + } + }, + "Jwt": { + "Issuer": "issuer", + "Audience": "audience" + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/appsettings.json b/MicroserviceTemplate/src/Authentication.API/appsettings.json new file mode 100644 index 0000000..294e3e7 --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/appsettings.json @@ -0,0 +1,56 @@ +{ + "Logging": { + "Sink": "CloudWatchLogs", + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ModuleConfiguration": { + "IsSwaggerUIEnabled": true, + "Infrastructure": { + "Kms": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "eu-west-2", + "LocalTestEndpoint": "http://localhost:52002", + "SigningKeyId": "6732c7ca-6ec9-4b96-9711-fd1c7d637c8e", + "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA56tfR6w3YJpbH5XZ6Ze2kB2evnUpbyZJiTLPKSc/VeA46m09lVB7bJRp0pKX2LusT2pccrVe5AYtbnikKqhOQWUdjLJnSONPNpd4yjEseqPblsXicA+xdP+Fk2W0yDxOc79LUAywgjV8JqNbbtVbhzqVPOLalJYnPEAVa3NQV138dnU7NzxbAjPjXINi7BBZ2OLRuocJRMfe16AUiQtH8MaWfRnnRRwdCBLJCXnZy+0hVc701SrVoTS+CA8RfGTCnzutx9MXW7t4SCEjZH0MSfhSZbKggPfi36HeUdClacgD6L0+FhSBKzd8kOC06CDf5WM9oV/XtWVXEWWGPDHv8wIDAQAB" + }, + "CloudWatchLogs": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "LocalTestEndpoint": "http://host.docker.internal:4566", + "LogGroupName": "/LocalStack/Microservice/Logs" + }, + "DynamoDb": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "LocalTestEndpoint": "http://host.docker.internal:8000", + "TableName": "ServiceName_Setting" + }, + "Seq": { + "ServerUrl": "http://localhost:5341", + "ApiKey": "" + }, + "SqlServer": { + "Server": "host.docker.internal", + "Port": "1433", + "Database": "DBName", + "Username": "sa", + "Password": "M3rz0ug4!!!!" + }, + "Redis": { + "Server": "host.docker.internal", + "Port": "6379" + } + }, + "Jwt": { + "Issuer": "issuer", + "Audience": "audience" + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/aws-lambda-tools-defaults.json b/MicroserviceTemplate/src/Authentication.API/aws-lambda-tools-defaults.json new file mode 100644 index 0000000..950200c --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/aws-lambda-tools-defaults.json @@ -0,0 +1,14 @@ +{ + "Information": [ + "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", + "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", + "dotnet lambda help", + "All the command line options for the Lambda command can be specified in this file." + ], + "profile": "", + "region": "", + "configuration": "Release", + "s3-prefix": "MinimalApi/", + "template": "serverless.template", + "template-parameters": "" +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/serverless.template b/MicroserviceTemplate/src/Authentication.API/serverless.template new file mode 100644 index 0000000..74f7093 --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/serverless.template @@ -0,0 +1,47 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Transform": "AWS::Serverless-2016-10-31", + "Description": "An AWS Serverless Application that uses the ASP.NET Core framework running in Amazon Lambda.", + "Parameters": {}, + "Conditions": {}, + "Resources": { + "AspNetCoreFunction": { + "Type": "AWS::Serverless::Function", + "Properties": { + "Handler": "MinimalApi", + "Runtime": "dotnet6", + "CodeUri": "", + "MemorySize": 256, + "Timeout": 30, + "Role": null, + "Policies": [ + "AWSLambda_FullAccess" + ], + "Events": { + "ProxyResource": { + "Type": "Api", + "Properties": { + "Path": "/{proxy+}", + "Method": "ANY" + } + }, + "RootResource": { + "Type": "Api", + "Properties": { + "Path": "/", + "Method": "ANY" + } + } + } + } + } + }, + "Outputs": { + "ApiURL": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs index ad01e10..17a0871 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs @@ -33,18 +33,6 @@ public static void MapEndpoints(this WebApplication app, ConfigurationManager co app.MapPost("/settings", [Authorize] async (IMediator mediator, [FromHeader] string authorization, [FromBody] Settings settings) => await mediator.Send(new SaveSettingsCommandRequest() { TenantId = GetTenantIdFromJwt(authorization), Settings = settings })) .WithApiVersionSet(versionSet) .MapToApiVersion(1.0); - - //These endpoints are only for JWT Testing & Troubleshooting Purposes - if (bool.Parse(configuration["ModuleConfiguration:Jwt:TestMode"])) - { - app.MapPost("/test/token/generate", [AllowAnonymous] async (IJwtAuthenticationService authService, string tenantId, string issuer, string audience) => await authService.GenerateTokenAsync(new ModuleIdentity() { InstanceGuid = tenantId }, 60, issuer, audience)) - .WithApiVersionSet(versionSet) - .MapToApiVersion(1.0); - - app.MapPost("/test/token/validate", [AllowAnonymous] async (IJwtAuthenticationService authService, string token) => await authService.ValidateTokenAsync(token)) - .WithApiVersionSet(versionSet) - .MapToApiVersion(1.0); - } } private static Guid GetTenantIdFromJwt(string token) diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs index 803f75d..c53576f 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs @@ -37,7 +37,7 @@ static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) { var info = new OpenApiInfo() { - Title = "Sample API", + Title = "ServiceName API", Version = description.ApiVersion.ToString(), Description = "A sample application with Swagger, Swashbuckle, and API versioning.", Contact = new OpenApiContact() { Name = "Leandro", Email = "leandro@somewhere.com" }, diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/SwaggerDefaultValues.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/SwaggerDefaultValues.cs index af07330..2cc1783 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/SwaggerDefaultValues.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/SwaggerDefaultValues.cs @@ -40,9 +40,11 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context) { parameter.Schema.Default = new OpenApiString(description.DefaultValue.ToString()); } - - parameter.Required |= description.IsRequired; } + + //Hide Authorization Parameter in Swagger + var authorizationParameter = operation.Parameters.FirstOrDefault(p => p.Name.Equals("authorization")); + operation.Parameters.Remove(authorizationParameter); } } } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json index 4606b7e..a219566 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json @@ -1,57 +1,56 @@ { - "Logging": { - "Sink": "CloudWatchLogs", - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } + "Logging": { + "Sink": "CloudWatchLogs", + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ModuleConfiguration": { + "IsSwaggerUIEnabled": true, + "Infrastructure": { + "Kms": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "eu-west-2", + "LocalTestEndpoint": "http://localhost:52002", + "SigningKeyId": "40ab43d1-5352-4b4e-b9a7-11ed401d2411", + "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu\u002BFcI4zsZvOoCvdIVhMdqJihfwiGGb9BADR\u002BWPD9WifnrNZJYNb4iHQCSCxRmteUjLNqjGgq/dKupFbSOxA22Cx8A63a6SydFpE1eeGARC5LsxD2H37Y6wJJYKtGhpLEXB/mQfo/vTg34bCWVVlY\u002ByM50HpQNTglt4c61FMTtDljXEq4RmP0vRwiQlUB\u002BrPBrhCPNXCXHdSO4GNRYfqBeY41sR5Ezad5C6QCgRzFKIGllQvorLbLXCJ3m9\u002B4I69l3Mtk\u002BHSmu1rNNne2XPzg25XM4JUVC3tOzvaPZXs\u002BAPu4yRpDRleYnAC1hAiUzMqW5NhkBV05aQ6xzFuQaoJ2xwIDAQAB" + }, + "CloudWatchLogs": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "LocalTestEndpoint": "http://host.docker.internal:4566", + "LogGroupName": "/LocalStack/Microservice/Logs" + }, + "DynamoDb": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "LocalTestEndpoint": "http://host.docker.internal:8000", + "TableName": "ServiceName_Setting" + }, + "Seq": { + "ServerUrl": "http://localhost:5341", + "ApiKey": "" + }, + "SqlServer": { + "Server": "host.docker.internal", + "Port": "1433", + "Database": "DBName", + "Username": "sa", + "Password": "M3rz0ug4!!!!" + }, + "Redis": { + "Server": "host.docker.internal", + "Port": "6379" + } }, - "AllowedHosts": "*", - "ModuleConfiguration": { - "IsSwaggerUIEnabled": true, - "Infrastructure": { - "Kms": { - "AccessKey": "test", - "SecretKey": "test", - "RegionEndpoint": "eu-west-2", - "LocalTestEndpoint": "http://localhost:52002", - "SigningKeyId": "6732c7ca-6ec9-4b96-9711-fd1c7d637c8e", - "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA56tfR6w3YJpbH5XZ6Ze2kB2evnUpbyZJiTLPKSc/VeA46m09lVB7bJRp0pKX2LusT2pccrVe5AYtbnikKqhOQWUdjLJnSONPNpd4yjEseqPblsXicA+xdP+Fk2W0yDxOc79LUAywgjV8JqNbbtVbhzqVPOLalJYnPEAVa3NQV138dnU7NzxbAjPjXINi7BBZ2OLRuocJRMfe16AUiQtH8MaWfRnnRRwdCBLJCXnZy+0hVc701SrVoTS+CA8RfGTCnzutx9MXW7t4SCEjZH0MSfhSZbKggPfi36HeUdClacgD6L0+FhSBKzd8kOC06CDf5WM9oV/XtWVXEWWGPDHv8wIDAQAB" - }, - "CloudWatchLogs": { - "AccessKey": "test", - "SecretKey": "test", - "RegionEndpoint": "ap-southeast-2", - "LocalTestEndpoint": "http://host.docker.internal:4566", - "LogGroupName": "/LocalStack/Microservice/Logs" - }, - "DynamoDb": { - "AccessKey": "test", - "SecretKey": "test", - "RegionEndpoint": "ap-southeast-2", - "LocalTestEndpoint": "http://host.docker.internal:8000", - "TableName": "ServiceName_Setting" - }, - "Seq": { - "ServerUrl": "http://localhost:5341", - "ApiKey": "" - }, - "SqlServer": { - "Server": "host.docker.internal", - "Port": "1433", - "Database": "DBName", - "Username": "sa", - "Password": "M3rz0ug4!!!!" - }, - "Redis": { - "Server": "host.docker.internal", - "Port": "6379" - } - }, - "Jwt": { - "TestMode": true, - "Issuer": "issuer", - "Audience": "audience" - } + "Jwt": { + "Issuer": "issuer", + "Audience": "audience" } + } } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.json index 4606b7e..294e3e7 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.json @@ -49,7 +49,6 @@ } }, "Jwt": { - "TestMode": true, "Issuer": "issuer", "Audience": "audience" } diff --git a/MicroserviceTemplate/src/ServiceTemplate.sln b/MicroserviceTemplate/src/ServiceTemplate.sln index bb62a15..47b2151 100644 --- a/MicroserviceTemplate/src/ServiceTemplate.sln +++ b/MicroserviceTemplate/src/ServiceTemplate.sln @@ -18,7 +18,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0750781A EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3FD41DDF-2B43-4372-9FB1-62597803431B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceName.Test", "..\tests\ServiceName.Test\ServiceName.Test.csproj", "{5A8EB360-D518-4F2C-929C-3628C6DA9226}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.Test", "..\tests\ServiceName.Test\ServiceName.Test.csproj", "{5A8EB360-D518-4F2C-929C-3628C6DA9226}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Authentication.API", "Authentication.API\Authentication.API.csproj", "{EEF86165-70C1-4ED1-9D67-F929B2B73455}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -42,6 +44,10 @@ Global {5A8EB360-D518-4F2C-929C-3628C6DA9226}.Debug|Any CPU.Build.0 = Debug|Any CPU {5A8EB360-D518-4F2C-929C-3628C6DA9226}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A8EB360-D518-4F2C-929C-3628C6DA9226}.Release|Any CPU.Build.0 = Release|Any CPU + {EEF86165-70C1-4ED1-9D67-F929B2B73455}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EEF86165-70C1-4ED1-9D67-F929B2B73455}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EEF86165-70C1-4ED1-9D67-F929B2B73455}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EEF86165-70C1-4ED1-9D67-F929B2B73455}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -51,6 +57,7 @@ Global {9B43FACF-50A2-4332-9642-3F723488DE40} = {3FD41DDF-2B43-4372-9FB1-62597803431B} {3733AF37-1B4D-4178-B4A8-46702493BCEC} = {3FD41DDF-2B43-4372-9FB1-62597803431B} {5A8EB360-D518-4F2C-929C-3628C6DA9226} = {0750781A-30FA-4A91-B764-A5E89BD9CCB0} + {EEF86165-70C1-4ED1-9D67-F929B2B73455} = {3FD41DDF-2B43-4372-9FB1-62597803431B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C9A3A709-C3C6-4654-AC2B-A92420A60DF5} From 283f82b9afd7b7dff4ef9c79e7e4f57279222dd5 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Wed, 22 Jun 2022 14:03:36 +1000 Subject: [PATCH 24/56] [MicroserviceTemplate] Added tool to initialize dev environment Added ingress controller hostname configuration --- .../appsettings.Development.json | 4 +- .../appsettings.Development.json | 4 +- MicroserviceTemplate/src/ServiceTemplate.sln | 9 ++ MicroserviceTemplate/src/tye.yaml | 28 +++-- .../EnvironmentInitializer/CmdRunner.csproj | 24 ++++ .../tools/EnvironmentInitializer/Helper.cs | 64 +++++++++++ .../Model/DynamoDbTable.cs | 6 + .../EnvironmentInitializer/Model/KmsKey.cs | 8 ++ .../EnvironmentInitializer/Model/KmsKeys.cs | 7 ++ .../tools/EnvironmentInitializer/Program.cs | 104 ++++++++++++++++++ .../EnvironmentInitializer/appsettings.json | 26 +++++ 11 files changed, 273 insertions(+), 11 deletions(-) create mode 100644 MicroserviceTemplate/tools/EnvironmentInitializer/CmdRunner.csproj create mode 100644 MicroserviceTemplate/tools/EnvironmentInitializer/Helper.cs create mode 100644 MicroserviceTemplate/tools/EnvironmentInitializer/Model/DynamoDbTable.cs create mode 100644 MicroserviceTemplate/tools/EnvironmentInitializer/Model/KmsKey.cs create mode 100644 MicroserviceTemplate/tools/EnvironmentInitializer/Model/KmsKeys.cs create mode 100644 MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs create mode 100644 MicroserviceTemplate/tools/EnvironmentInitializer/appsettings.json diff --git a/MicroserviceTemplate/src/Authentication.API/appsettings.Development.json b/MicroserviceTemplate/src/Authentication.API/appsettings.Development.json index a219566..61043f5 100644 --- a/MicroserviceTemplate/src/Authentication.API/appsettings.Development.json +++ b/MicroserviceTemplate/src/Authentication.API/appsettings.Development.json @@ -15,8 +15,8 @@ "SecretKey": "test", "RegionEndpoint": "eu-west-2", "LocalTestEndpoint": "http://localhost:52002", - "SigningKeyId": "40ab43d1-5352-4b4e-b9a7-11ed401d2411", - "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu\u002BFcI4zsZvOoCvdIVhMdqJihfwiGGb9BADR\u002BWPD9WifnrNZJYNb4iHQCSCxRmteUjLNqjGgq/dKupFbSOxA22Cx8A63a6SydFpE1eeGARC5LsxD2H37Y6wJJYKtGhpLEXB/mQfo/vTg34bCWVVlY\u002ByM50HpQNTglt4c61FMTtDljXEq4RmP0vRwiQlUB\u002BrPBrhCPNXCXHdSO4GNRYfqBeY41sR5Ezad5C6QCgRzFKIGllQvorLbLXCJ3m9\u002B4I69l3Mtk\u002BHSmu1rNNne2XPzg25XM4JUVC3tOzvaPZXs\u002BAPu4yRpDRleYnAC1hAiUzMqW5NhkBV05aQ6xzFuQaoJ2xwIDAQAB" + "SigningKeyId": "51c88cf9-e753-44ad-b9cf-00c1c11b6727", + "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxu5nBIn8lmIOiH1DUmQHKbv9Ss6KB\u002BtJDivCrKPh0n3os2SODkmdzR/dnwZdZUYMU21sxrLWTe3AoPzMHZ/sm2CGnDTYugo6X893Y8AJGbQWzLhR4BDQpEhMLTdBPMz3F1z6GqgYXth5k5LHBcg8avejIjb63smJZoqcuhEgRNx9sjJBvVBTagYRJYoiAK3esod1BtTewlZf1LZITLVec4DlIk2tWtVMus9JC\u002BehCYz1rXwL0hMT0subZrXhNsTPjN1lAT\u002BX3\u002BvwhScpvNcCI89qZx2WJbEA4okoKpxKsK0lkUfPeqVjUMkbD/xXy0pIPgqAjiN/EEwO4VTfH7CB9wIDAQAB" }, "CloudWatchLogs": { "AccessKey": "test", diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json index a219566..61043f5 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json @@ -15,8 +15,8 @@ "SecretKey": "test", "RegionEndpoint": "eu-west-2", "LocalTestEndpoint": "http://localhost:52002", - "SigningKeyId": "40ab43d1-5352-4b4e-b9a7-11ed401d2411", - "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu\u002BFcI4zsZvOoCvdIVhMdqJihfwiGGb9BADR\u002BWPD9WifnrNZJYNb4iHQCSCxRmteUjLNqjGgq/dKupFbSOxA22Cx8A63a6SydFpE1eeGARC5LsxD2H37Y6wJJYKtGhpLEXB/mQfo/vTg34bCWVVlY\u002ByM50HpQNTglt4c61FMTtDljXEq4RmP0vRwiQlUB\u002BrPBrhCPNXCXHdSO4GNRYfqBeY41sR5Ezad5C6QCgRzFKIGllQvorLbLXCJ3m9\u002B4I69l3Mtk\u002BHSmu1rNNne2XPzg25XM4JUVC3tOzvaPZXs\u002BAPu4yRpDRleYnAC1hAiUzMqW5NhkBV05aQ6xzFuQaoJ2xwIDAQAB" + "SigningKeyId": "51c88cf9-e753-44ad-b9cf-00c1c11b6727", + "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxu5nBIn8lmIOiH1DUmQHKbv9Ss6KB\u002BtJDivCrKPh0n3os2SODkmdzR/dnwZdZUYMU21sxrLWTe3AoPzMHZ/sm2CGnDTYugo6X893Y8AJGbQWzLhR4BDQpEhMLTdBPMz3F1z6GqgYXth5k5LHBcg8avejIjb63smJZoqcuhEgRNx9sjJBvVBTagYRJYoiAK3esod1BtTewlZf1LZITLVec4DlIk2tWtVMus9JC\u002BehCYz1rXwL0hMT0subZrXhNsTPjN1lAT\u002BX3\u002BvwhScpvNcCI89qZx2WJbEA4okoKpxKsK0lkUfPeqVjUMkbD/xXy0pIPgqAjiN/EEwO4VTfH7CB9wIDAQAB" }, "CloudWatchLogs": { "AccessKey": "test", diff --git a/MicroserviceTemplate/src/ServiceTemplate.sln b/MicroserviceTemplate/src/ServiceTemplate.sln index 47b2151..4bffd9e 100644 --- a/MicroserviceTemplate/src/ServiceTemplate.sln +++ b/MicroserviceTemplate/src/ServiceTemplate.sln @@ -22,6 +22,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.Test", "..\test EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Authentication.API", "Authentication.API\Authentication.API.csproj", "{EEF86165-70C1-4ED1-9D67-F929B2B73455}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{04E0AED5-37A9-482D-9624-FB1F991C15DE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EnvironmentInitializer", "..\tools\EnvironmentInitializer\EnvironmentInitializer.csproj", "{48B3400D-3AA0-435E-BB68-B03A12D01217}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -48,6 +52,10 @@ Global {EEF86165-70C1-4ED1-9D67-F929B2B73455}.Debug|Any CPU.Build.0 = Debug|Any CPU {EEF86165-70C1-4ED1-9D67-F929B2B73455}.Release|Any CPU.ActiveCfg = Release|Any CPU {EEF86165-70C1-4ED1-9D67-F929B2B73455}.Release|Any CPU.Build.0 = Release|Any CPU + {48B3400D-3AA0-435E-BB68-B03A12D01217}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48B3400D-3AA0-435E-BB68-B03A12D01217}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48B3400D-3AA0-435E-BB68-B03A12D01217}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48B3400D-3AA0-435E-BB68-B03A12D01217}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -58,6 +66,7 @@ Global {3733AF37-1B4D-4178-B4A8-46702493BCEC} = {3FD41DDF-2B43-4372-9FB1-62597803431B} {5A8EB360-D518-4F2C-929C-3628C6DA9226} = {0750781A-30FA-4A91-B764-A5E89BD9CCB0} {EEF86165-70C1-4ED1-9D67-F929B2B73455} = {3FD41DDF-2B43-4372-9FB1-62597803431B} + {48B3400D-3AA0-435E-BB68-B03A12D01217} = {04E0AED5-37A9-482D-9624-FB1F991C15DE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C9A3A709-C3C6-4654-AC2B-A92420A60DF5} diff --git a/MicroserviceTemplate/src/tye.yaml b/MicroserviceTemplate/src/tye.yaml index 18e1c8c..0a3a861 100644 --- a/MicroserviceTemplate/src/tye.yaml +++ b/MicroserviceTemplate/src/tye.yaml @@ -17,16 +17,26 @@ ingress: - name: Ingress bindings: - port: 51000 + protocol: https + ip: '127.0.0.1' rules: - - path: /A - service: servicename-api - - path: /B + - host: servicename.domain.local service: servicename-api + - host: authentication.domain.local + service: authentication-api + services: + - name: authentication-api + project: Authentication.API/Authentication.API.csproj + bindings: + - port: 51001 + protocol: https + #replicas: 2 - name: servicename-api project: ServiceName.API/ServiceName.API.csproj bindings: - - port: 51001 + - port: 51002 + protocol: https #replicas: 2 - name: SqlServer @@ -70,8 +80,6 @@ services: - name: REGION value: ap-southeast-2 - #aws --endpoint-url=http://localhost:52002 kms --region ap-southeast-2 create-key --key-spec RSA_4096 --key-usage ENCRYPT_DECRYPT - #aws --endpoint-url=http://localhost:52002 kms --region ap-southeast-2 list-keys - name: KMS image: nsmithuk/local-kms volumes: @@ -88,4 +96,10 @@ services: - name: DEBUG value: "1" - name: SERVICES - value: "logs" \ No newline at end of file + value: "logs" + + - name: Zipkin + image: "openzipkin/zipkin" + bindings: + - port: 9411 + protocol: http diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/CmdRunner.csproj b/MicroserviceTemplate/tools/EnvironmentInitializer/CmdRunner.csproj new file mode 100644 index 0000000..4e919a6 --- /dev/null +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/CmdRunner.csproj @@ -0,0 +1,24 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + + + + Always + + + + diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/Helper.cs b/MicroserviceTemplate/tools/EnvironmentInitializer/Helper.cs new file mode 100644 index 0000000..f6dbec5 --- /dev/null +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/Helper.cs @@ -0,0 +1,64 @@ +using System.Diagnostics; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Xml; + +namespace CmdRunner +{ + public class Helper + { + public static void UpdateTargetPropertyJson(string targetJson, string newKmsKeyId, string newKmsPublicKey) + { + var targetConfiguration = File.ReadAllText(targetJson); + JsonNode jsonNode = JsonSerializer.Deserialize(targetConfiguration); + jsonNode["ModuleConfiguration"]["Infrastructure"]["Kms"]["SigningKeyId"] = newKmsKeyId; + jsonNode["ModuleConfiguration"]["Infrastructure"]["Kms"]["PublicKey"] = newKmsPublicKey; + File.WriteAllText(targetJson, JsonSerializer.Serialize(jsonNode, new JsonSerializerOptions() { WriteIndented = true })); + } + + public static void UpdateTargetPropertyXml(string targetXml, string newKmsKeyId, string newKmsPublicKey) + { + var xmlDocument = new XmlDocument + { + PreserveWhitespace = true //to preserve formatting this must be done before loading so that the whitespace doesn't get thrown away at load time + }; + + xmlDocument.Load(targetXml); + + XmlNode node = xmlDocument.SelectSingleNode("//add[@key='amazonKmsSigningKeyId']"); + if (node != null) + { + node.Attributes["value"].Value = newKmsKeyId; + } + + node = xmlDocument.SelectSingleNode("//add[@key='amazonKmsPublicKey']"); + if (node != null) + { + node.Attributes["value"].Value = newKmsPublicKey; + } + + xmlDocument.Save(targetXml); + } + + public static string RunCommand(string command) + { + var p = new Process(); + p.StartInfo.UseShellExecute = false; + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.FileName = "cmd.exe"; + p.StartInfo.Arguments = $"/C {command}"; + p.Start(); + var output = p.StandardOutput.ReadToEnd(); + p.WaitForExit(); + return output; + } + + public static string GetJsonPropertyValue(string jsonKey, string output) + { + //JsonDocumentPath package is required because SelectElement is not natively supported by .NET 6 (https://stackoverflow.com/questions/70678718/how-to-delete-and-update-based-on-a-path-in-system-text-json-net-6) + var jsonDocument = JsonDocument.Parse(output); + JsonElement? jsonElement = jsonDocument.RootElement.SelectElement($"$.{jsonKey}"); + return jsonElement.Value.ToString(); + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/Model/DynamoDbTable.cs b/MicroserviceTemplate/tools/EnvironmentInitializer/Model/DynamoDbTable.cs new file mode 100644 index 0000000..55752c8 --- /dev/null +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/Model/DynamoDbTable.cs @@ -0,0 +1,6 @@ +internal class DynamoDbTable +{ + public string TableName { get; set; } + public string AttributeDefinitions { get; set; } + public string KeySchema { get; set; } +} \ No newline at end of file diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/Model/KmsKey.cs b/MicroserviceTemplate/tools/EnvironmentInitializer/Model/KmsKey.cs new file mode 100644 index 0000000..9bdcedd --- /dev/null +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/Model/KmsKey.cs @@ -0,0 +1,8 @@ +namespace CmdRunner.Model +{ + public class KmsKey + { + public string KeyId { get; set; } + public string KeyArn { get; set; } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/Model/KmsKeys.cs b/MicroserviceTemplate/tools/EnvironmentInitializer/Model/KmsKeys.cs new file mode 100644 index 0000000..ece19e2 --- /dev/null +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/Model/KmsKeys.cs @@ -0,0 +1,7 @@ +namespace CmdRunner.Model +{ + internal class KmsKeys + { + public List Keys { get; set; } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs b/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs new file mode 100644 index 0000000..6782917 --- /dev/null +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs @@ -0,0 +1,104 @@ +using System.Text.Json; +using CmdRunner; +using CmdRunner.Model; +using Microsoft.Extensions.Configuration; + +var configuration = new ConfigurationBuilder() + .SetBasePath(Environment.CurrentDirectory) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile($"appsettings.Development.json", optional: true, reloadOnChange: true) + .Build(); + +var kmsLocalUrl = configuration["KmsLocalUrl"]; +var dynamoDbLocalUrl = configuration["DynamoDbLocalUrl"]; +var targetConfigurationFiles = configuration.GetSection("TargetConfigurationFiles").GetChildren().ToList(); +var dynamoDbTables = configuration.GetSection("DynamoDbTables").Get>(); + +/* + * TODO: Clean up environment to re-run Tye + * Kill Tye process + * docker kill $(docker ps -q) + * docker rm $(docker ps -a -q + * docker network prune + * tye run --port 10000 --dashboard + */ + +//KMS +var sourceJson = Helper.RunCommand(@"docker container ls --filter ""name = kms*"" --format=""{{json .}}"""); +var kmsDockerState = Helper.GetJsonPropertyValue("State", sourceJson); +if (kmsDockerState.Equals("running")) +{ + Console.WriteLine("KMS container is running"); + + var existingKeys = Helper.RunCommand($"aws --endpoint-url={kmsLocalUrl} kms --region ap-southeast-2 list-keys"); + var existingKeysList = JsonSerializer.Deserialize(existingKeys); + var KmsKeyId = existingKeysList.Keys.FirstOrDefault()?.KeyId; + + if (KmsKeyId == null) + { + //Create new KMS Key + sourceJson = Helper.RunCommand($"aws --endpoint-url={kmsLocalUrl} kms --region ap-southeast-2 create-key --key-spec RSA_2048 --key-usage SIGN_VERIFY"); + var newKmsKeyId = Helper.GetJsonPropertyValue("KeyMetadata.KeyId", sourceJson); + KmsKeyId = newKmsKeyId; + } + + Console.WriteLine($"KeyId is {KmsKeyId}"); + + //Get the new Public Key based on the newly created Key + sourceJson = Helper.RunCommand($"aws --endpoint-url={kmsLocalUrl} kms --region ap-southeast-2 get-public-key --key-id {KmsKeyId}"); + + var KmsPublicKey = Helper.GetJsonPropertyValue("PublicKey", sourceJson); + + Console.WriteLine($"PublicKey is {KmsPublicKey}"); + + //Update the target configuration files + foreach (var filename in targetConfigurationFiles.Select(t => t.Value)) + { + Console.WriteLine($"Updating {filename}"); + var fileInfo = new FileInfo(filename); + switch (fileInfo.Extension) + { + case ".json": + Helper.UpdateTargetPropertyJson(filename, KmsKeyId, KmsPublicKey); + break; + + case ".xml": + case ".config": + Helper.UpdateTargetPropertyXml(filename, KmsKeyId, KmsPublicKey); + break; + + default: + break; + } + Console.WriteLine($"{filename} has been updated"); + } +} +else +{ + Console.WriteLine("KMS container is not running"); +} + +//DynamoDB +sourceJson = Helper.RunCommand(@"docker container ls --filter ""name = dynamodb*"" --format=""{{json .}}"""); +var dynamoDbDockerState = Helper.GetJsonPropertyValue("State", sourceJson); +if (dynamoDbDockerState.Equals("running")) +{ + Console.WriteLine("DynamoDB container is running"); + + foreach (var dynamoDbTable in dynamoDbTables) + { + if (!string.IsNullOrEmpty(dynamoDbTable.TableName)) + { + Console.WriteLine($"Creating DynamoDB table ({dynamoDbTable.TableName})"); + Helper.RunCommand($"aws --endpoint-url={dynamoDbLocalUrl} dynamodb create-table --table-name {dynamoDbTable.TableName} --attribute-definitions {dynamoDbTable.AttributeDefinitions} --key-schema {dynamoDbTable.KeySchema} --billing-mode PAY_PER_REQUEST"); + Console.WriteLine($"DynamoDB table ({dynamoDbTable.TableName}) has been created."); + } + } +} +else +{ + Console.WriteLine("DynamoDB container is not running"); +} + +Console.WriteLine("Press any key to finish the application"); +Console.ReadLine(); \ No newline at end of file diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/appsettings.json b/MicroserviceTemplate/tools/EnvironmentInitializer/appsettings.json new file mode 100644 index 0000000..eea34c2 --- /dev/null +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/appsettings.json @@ -0,0 +1,26 @@ +{ + "KmsLocalUrl": "http://localhost:52002", + "DynamoDbLocalUrl": "http://localhost:8000", + "TargetConfigurationFiles": [ + "C:\\Dev\\GitHub\\Workbench\\MicroserviceTemplate\\src\\ServiceName.API\\appsettings.Development.json", + "C:\\Dev\\GitHub\\Workbench\\MicroserviceTemplate\\src\\Authentication.API\\appsettings.Development.json", + "C:\\Dev\\Web.config" + ], + "DynamoDbTables": [ + { + "TableName": "ServiceName_Setting", + "AttributeDefinitions": "AttributeName=TenantId,AttributeType=S", + "KeySchema": "AttributeName=TenantId,KeyType=HASH" + }, + { + "TableName": "ServiceName_Setting2", + "AttributeDefinitions": "AttributeName=TenantId,AttributeType=S", + "KeySchema": "AttributeName=TenantId,KeyType=HASH" + }, + { + "TableName": "ServiceName_Setting3", + "AttributeDefinitions": "AttributeName=TenantId,AttributeType=S", + "KeySchema": "AttributeName=TenantId,KeyType=HASH" + } + ] +} \ No newline at end of file From 7d1fb22eb320387b22262e982b35ba901ef3bdb4 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Thu, 23 Jun 2022 11:38:19 +1000 Subject: [PATCH 25/56] [MicroservicesTemplate] Created All.sln Added CDK Added Nuke build Added version.json files for Nerdbank.GitVersioning Added Background Task for Long-Running jobs --- .../{src/ServiceTemplate.sln => All.sln} | 34 ++++- MicroserviceTemplate/aws/Cdk.csproj | 2 +- MicroserviceTemplate/build/.editorconfig | 11 ++ .../build/.nuke/build.schema.json | 123 +++++++++++++++ .../build/.nuke/parameters.json | 4 + MicroserviceTemplate/build/Build.cs | 141 ++++++++++++++++++ MicroserviceTemplate/build/Build.csproj | 32 ++++ MicroserviceTemplate/build/Build.sln | 25 ++++ MicroserviceTemplate/build/Configuration.cs | 14 ++ .../build/Directory.Build.props | 8 + .../build/Directory.Build.targets | 8 + MicroserviceTemplate/build/appsettings.json | 6 + MicroserviceTemplate/build/build.cmd | 7 + MicroserviceTemplate/build/build.ps1 | 69 +++++++++ MicroserviceTemplate/build/build.sh | 62 ++++++++ .../build/pipeline.csproj.DotSettings | 26 ++++ .../src/Authentication.API/version.json | 3 + .../BackgroundTasks/RepeatingTask.cs | 36 +++++ .../src/ServiceName.API/Program.cs | 7 +- .../src/ServiceName.API/version.json | 3 + ...r.csproj => EnvironmentInitializer.csproj} | 0 .../tools/EnvironmentInitializer/Program.cs | 1 + 22 files changed, 609 insertions(+), 13 deletions(-) rename MicroserviceTemplate/{src/ServiceTemplate.sln => All.sln} (65%) create mode 100644 MicroserviceTemplate/build/.editorconfig create mode 100644 MicroserviceTemplate/build/.nuke/build.schema.json create mode 100644 MicroserviceTemplate/build/.nuke/parameters.json create mode 100644 MicroserviceTemplate/build/Build.cs create mode 100644 MicroserviceTemplate/build/Build.csproj create mode 100644 MicroserviceTemplate/build/Build.sln create mode 100644 MicroserviceTemplate/build/Configuration.cs create mode 100644 MicroserviceTemplate/build/Directory.Build.props create mode 100644 MicroserviceTemplate/build/Directory.Build.targets create mode 100644 MicroserviceTemplate/build/appsettings.json create mode 100644 MicroserviceTemplate/build/build.cmd create mode 100644 MicroserviceTemplate/build/build.ps1 create mode 100644 MicroserviceTemplate/build/build.sh create mode 100644 MicroserviceTemplate/build/pipeline.csproj.DotSettings create mode 100644 MicroserviceTemplate/src/Authentication.API/version.json create mode 100644 MicroserviceTemplate/src/ServiceName.API/BackgroundTasks/RepeatingTask.cs create mode 100644 MicroserviceTemplate/src/ServiceName.API/version.json rename MicroserviceTemplate/tools/EnvironmentInitializer/{CmdRunner.csproj => EnvironmentInitializer.csproj} (100%) diff --git a/MicroserviceTemplate/src/ServiceTemplate.sln b/MicroserviceTemplate/All.sln similarity index 65% rename from MicroserviceTemplate/src/ServiceTemplate.sln rename to MicroserviceTemplate/All.sln index 4bffd9e..6d6e5ed 100644 --- a/MicroserviceTemplate/src/ServiceTemplate.sln +++ b/MicroserviceTemplate/All.sln @@ -3,28 +3,36 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.2.32505.173 MinimumVisualStudioVersion = 15.0.26124.0 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.API", "ServiceName.API\ServiceName.API.csproj", "{DD4C16FE-1C12-4B91-92D1-184329282E65}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.API", "src\ServiceName.API\ServiceName.API.csproj", "{DD4C16FE-1C12-4B91-92D1-184329282E65}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.Infrastructure", "ServiceName.Infrastructure\ServiceName.Infrastructure.csproj", "{9B43FACF-50A2-4332-9642-3F723488DE40}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.Infrastructure", "src\ServiceName.Infrastructure\ServiceName.Infrastructure.csproj", "{9B43FACF-50A2-4332-9642-3F723488DE40}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{99A702BB-D8C1-4F89-9B4E-EB891693FD7A}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tye", "tye", "{99A702BB-D8C1-4F89-9B4E-EB891693FD7A}" ProjectSection(SolutionItems) = preProject - tye.yaml = tye.yaml + src\tye.yaml = src\tye.yaml EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.Core", "ServiceName.Core\ServiceName.Core.csproj", "{3733AF37-1B4D-4178-B4A8-46702493BCEC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.Core", "src\ServiceName.Core\ServiceName.Core.csproj", "{3733AF37-1B4D-4178-B4A8-46702493BCEC}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0750781A-30FA-4A91-B764-A5E89BD9CCB0}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3FD41DDF-2B43-4372-9FB1-62597803431B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.Test", "..\tests\ServiceName.Test\ServiceName.Test.csproj", "{5A8EB360-D518-4F2C-929C-3628C6DA9226}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceName.Test", "tests\ServiceName.Test\ServiceName.Test.csproj", "{5A8EB360-D518-4F2C-929C-3628C6DA9226}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Authentication.API", "Authentication.API\Authentication.API.csproj", "{EEF86165-70C1-4ED1-9D67-F929B2B73455}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Authentication.API", "src\Authentication.API\Authentication.API.csproj", "{EEF86165-70C1-4ED1-9D67-F929B2B73455}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{04E0AED5-37A9-482D-9624-FB1F991C15DE}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EnvironmentInitializer", "..\tools\EnvironmentInitializer\EnvironmentInitializer.csproj", "{48B3400D-3AA0-435E-BB68-B03A12D01217}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EnvironmentInitializer", "tools\EnvironmentInitializer\EnvironmentInitializer.csproj", "{48B3400D-3AA0-435E-BB68-B03A12D01217}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{39870B6D-6682-4B08-8208-6C05B41930EC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Build", "build\Build.csproj", "{A9FA5380-D9B1-4AA1-93FB-F805E22FC9B2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "aws", "aws", "{462D80EC-4E6B-4B0F-8062-779007AB3A90}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cdk", "aws\Cdk.csproj", "{F9885267-A919-4711-B9AF-9C6ADEC4DA21}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -56,6 +64,14 @@ Global {48B3400D-3AA0-435E-BB68-B03A12D01217}.Debug|Any CPU.Build.0 = Debug|Any CPU {48B3400D-3AA0-435E-BB68-B03A12D01217}.Release|Any CPU.ActiveCfg = Release|Any CPU {48B3400D-3AA0-435E-BB68-B03A12D01217}.Release|Any CPU.Build.0 = Release|Any CPU + {A9FA5380-D9B1-4AA1-93FB-F805E22FC9B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9FA5380-D9B1-4AA1-93FB-F805E22FC9B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9FA5380-D9B1-4AA1-93FB-F805E22FC9B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9FA5380-D9B1-4AA1-93FB-F805E22FC9B2}.Release|Any CPU.Build.0 = Release|Any CPU + {F9885267-A919-4711-B9AF-9C6ADEC4DA21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F9885267-A919-4711-B9AF-9C6ADEC4DA21}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F9885267-A919-4711-B9AF-9C6ADEC4DA21}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F9885267-A919-4711-B9AF-9C6ADEC4DA21}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -67,6 +83,8 @@ Global {5A8EB360-D518-4F2C-929C-3628C6DA9226} = {0750781A-30FA-4A91-B764-A5E89BD9CCB0} {EEF86165-70C1-4ED1-9D67-F929B2B73455} = {3FD41DDF-2B43-4372-9FB1-62597803431B} {48B3400D-3AA0-435E-BB68-B03A12D01217} = {04E0AED5-37A9-482D-9624-FB1F991C15DE} + {A9FA5380-D9B1-4AA1-93FB-F805E22FC9B2} = {39870B6D-6682-4B08-8208-6C05B41930EC} + {F9885267-A919-4711-B9AF-9C6ADEC4DA21} = {462D80EC-4E6B-4B0F-8062-779007AB3A90} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C9A3A709-C3C6-4654-AC2B-A92420A60DF5} diff --git a/MicroserviceTemplate/aws/Cdk.csproj b/MicroserviceTemplate/aws/Cdk.csproj index 91517af..338b323 100644 --- a/MicroserviceTemplate/aws/Cdk.csproj +++ b/MicroserviceTemplate/aws/Cdk.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 Major diff --git a/MicroserviceTemplate/build/.editorconfig b/MicroserviceTemplate/build/.editorconfig new file mode 100644 index 0000000..31e43dc --- /dev/null +++ b/MicroserviceTemplate/build/.editorconfig @@ -0,0 +1,11 @@ +[*.cs] +dotnet_style_qualification_for_field = false:warning +dotnet_style_qualification_for_property = false:warning +dotnet_style_qualification_for_method = false:warning +dotnet_style_qualification_for_event = false:warning +dotnet_style_require_accessibility_modifiers = never:warning + +csharp_style_expression_bodied_methods = true:silent +csharp_style_expression_bodied_properties = true:warning +csharp_style_expression_bodied_indexers = true:warning +csharp_style_expression_bodied_accessors = true:warning diff --git a/MicroserviceTemplate/build/.nuke/build.schema.json b/MicroserviceTemplate/build/.nuke/build.schema.json new file mode 100644 index 0000000..0202870 --- /dev/null +++ b/MicroserviceTemplate/build/.nuke/build.schema.json @@ -0,0 +1,123 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Build Schema", + "$ref": "#/definitions/build", + "definitions": { + "build": { + "type": "object", + "properties": { + "Configuration": { + "type": "string", + "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)", + "enum": [ + "Debug", + "Release" + ] + }, + "Continue": { + "type": "boolean", + "description": "Indicates to continue a previously failed build attempt" + }, + "Help": { + "type": "boolean", + "description": "Shows the help text for this build assembly" + }, + "Host": { + "type": "string", + "description": "Host for execution. Default is 'automatic'", + "enum": [ + "AppVeyor", + "AzurePipelines", + "Bamboo", + "Bitbucket", + "Bitrise", + "GitHubActions", + "GitLab", + "Jenkins", + "Rider", + "SpaceAutomation", + "TeamCity", + "Terminal", + "TravisCI", + "VisualStudio", + "VSCode" + ] + }, + "NoLogo": { + "type": "boolean", + "description": "Disables displaying the NUKE logo" + }, + "OctopusApiKey": { + "type": "string" + }, + "OctopusServerUrl": { + "type": "string" + }, + "OctopusSpaceId": { + "type": "string" + }, + "Partition": { + "type": "string", + "description": "Partition to use on CI" + }, + "Plan": { + "type": "boolean", + "description": "Shows the execution plan (HTML)" + }, + "Profile": { + "type": "array", + "description": "Defines the profiles to load", + "items": { + "type": "string" + } + }, + "Root": { + "type": "string", + "description": "Root directory during build execution" + }, + "Skip": { + "type": "array", + "description": "List of targets to be skipped. Empty list skips all dependencies", + "items": { + "type": "string", + "enum": [ + "Clean", + "Compile", + "Pack", + "Restore", + "Versioning" + ] + } + }, + "Solution": { + "type": "string", + "description": "Path to a solution file that is automatically loaded" + }, + "Target": { + "type": "array", + "description": "List of targets to be invoked. Default is '{default_target}'", + "items": { + "type": "string", + "enum": [ + "Clean", + "Compile", + "Pack", + "Restore", + "Versioning" + ] + } + }, + "Verbosity": { + "type": "string", + "description": "Logging verbosity during build execution. Default is 'Normal'", + "enum": [ + "Minimal", + "Normal", + "Quiet", + "Verbose" + ] + } + } + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/build/.nuke/parameters.json b/MicroserviceTemplate/build/.nuke/parameters.json new file mode 100644 index 0000000..8e9eacd --- /dev/null +++ b/MicroserviceTemplate/build/.nuke/parameters.json @@ -0,0 +1,4 @@ +{ + "$schema": "./build.schema.json", + "Solution": "../All.sln" +} diff --git a/MicroserviceTemplate/build/Build.cs b/MicroserviceTemplate/build/Build.cs new file mode 100644 index 0000000..1920064 --- /dev/null +++ b/MicroserviceTemplate/build/Build.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.Extensions.Configuration; +using Nuke.Common; +using Nuke.Common.CI; +using Nuke.Common.IO; +using Nuke.Common.ProjectModel; +using Nuke.Common.Tooling; +using Nuke.Common.Tools.DotNet; +using Nuke.Common.Tools.GitVersion; +using Nuke.Common.Tools.MSBuild; +using Nuke.Common.Tools.NerdbankGitVersioning; +using Nuke.Common.Tools.NuGet; +using Nuke.Common.Tools.Octopus; +using Nuke.Common.Utilities.Collections; +using static Nuke.Common.IO.FileSystemTasks; +using static Nuke.Common.IO.PathConstruction; + +[ShutdownDotNetAfterServerBuild] +class Build : NukeBuild +{ + readonly IConfiguration _configuration; + readonly Dictionary _versions; + readonly List _csProjects; + readonly List _releaseableProjects; + + + public static int Main() => Execute(x => x.Pack); + + [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] + readonly Configuration Configuration = Configuration.Release; + + [Parameter] string OctopusServerUrl; + [Parameter] string OctopusApiKey; + [Parameter] string OctopusSpaceId; + + [Solution] readonly Solution Solution; + AbsolutePath SolutionDirectory => RootDirectory.Parent; + AbsolutePath SourceDirectory => SolutionDirectory / "src"; + AbsolutePath TestDirectory => SolutionDirectory / "test"; + AbsolutePath ToolDirectory => SolutionDirectory / "tools"; + AbsolutePath OutputDirectory => SolutionDirectory / "output"; + + public Build() + { + _configuration = new ConfigurationBuilder() + .SetBasePath(Environment.CurrentDirectory) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .Build(); + + _versions = new Dictionary(); + _releaseableProjects = _configuration.GetSection("ReleaseableProjects").Get>(); + _csProjects = SolutionDirectory.GlobFiles("**/*.csproj").Where(p => _releaseableProjects.Contains(new FileInfo(p).Name)).Select(p => new FileInfo(p)).ToList(); + } + + Target Clean => _ => _ + .Executes(() => + { + SourceDirectory.GlobDirectories("**/bin", "**/obj").ForEach(DeleteDirectory); + TestDirectory.GlobDirectories("**/bin", "**/obj").ForEach(DeleteDirectory); + ToolDirectory.GlobDirectories("**/bin", "**/obj").ForEach(DeleteDirectory); + EnsureCleanDirectory(OutputDirectory); + }); + + Target Restore => _ => _ + .DependsOn(Clean) + .Executes(() => + { + NuGetTasks.NuGetRestore(s => s.SetTargetPath(SolutionDirectory)); + }); + + Target Versioning => _ => _ + .DependsOn(Restore) + .Executes(() => + { + foreach (var csProject in _csProjects) + { + var versionResult = NerdbankGitVersioningTasks.NerdbankGitVersioningGetVersion(v => v.SetProcessWorkingDirectory(csProject.DirectoryName).SetProcessArgumentConfigurator(a => a.Add("-f json"))).Result; + NerdbankGitVersioningTasks.NerdbankGitVersioningSetVersion(v => v.SetProject(csProject.DirectoryName) + .SetVersion(versionResult.Version) + ); + _versions.Add(csProject.Name, versionResult.Version); + } + }); + + Target Compile => _ => _ + .DependsOn(Versioning) + .Executes(() => + { + MSBuildTasks.MSBuild(s => s + .SetTargetPath(SolutionDirectory) + .SetConfiguration(Configuration) + ); + }); + + Target Pack => _ => _ + .DependsOn(Compile) + .Executes(() => + { + foreach (var csProject in _csProjects) + { + var packageId = csProject.Name.Replace(".csproj", string.Empty); + var sourcePath = $"{csProject.DirectoryName}\\bin"; + var targetPath = $"{OutputDirectory}\\{packageId}"; + + var directories = Directory.GetDirectories(sourcePath, "*net*", SearchOption.AllDirectories).ToList(); + + if (directories.Count > 0) + { + sourcePath = directories.FirstOrDefault(); + } + + CopyDirectoryRecursively(sourcePath, targetPath); + + OctopusTasks.OctopusPack(o => o.SetBasePath(targetPath) + .SetOutputFolder(OutputDirectory) + .SetId(packageId) + .SetVersion(_versions.GetValueOrDefault(csProject.Name))); + + DeleteDirectory(targetPath); + } + }); + + //Target Push => _ => _ + // .DependsOn(Pack) + // .Executes(() => + // { + // var packages = SolutionDirectory.GlobFiles("**/output/*.nupkg"); + // foreach (var package in packages) + // { + // //if the package exists the default behaviour is to reject the package + // OctopusTasks.OctopusPush(o => o.SetServer(OctopusServerUrl) + // .SetApiKey(OctopusApiKey) + // .SetSpace(OctopusSpaceId) + // .SetPackage(package)); + // } + + // }); +} diff --git a/MicroserviceTemplate/build/Build.csproj b/MicroserviceTemplate/build/Build.csproj new file mode 100644 index 0000000..6842ec0 --- /dev/null +++ b/MicroserviceTemplate/build/Build.csproj @@ -0,0 +1,32 @@ + + + + Exe + net6.0 + + CS0649;CS0169 + ../build + ../build + 1 + + + + + + + + + + + + + + + + Always + + + + + + diff --git a/MicroserviceTemplate/build/Build.sln b/MicroserviceTemplate/build/Build.sln new file mode 100644 index 0000000..1b07ef4 --- /dev/null +++ b/MicroserviceTemplate/build/Build.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31605.320 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pipeline", "pipeline\Pipeline.csproj", "{8CF29A37-BFDC-4258-9DCC-B9C7EA15D2FF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8CF29A37-BFDC-4258-9DCC-B9C7EA15D2FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8CF29A37-BFDC-4258-9DCC-B9C7EA15D2FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8CF29A37-BFDC-4258-9DCC-B9C7EA15D2FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8CF29A37-BFDC-4258-9DCC-B9C7EA15D2FF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4798EAE1-2496-446B-B71B-6A5E69C5C3BA} + EndGlobalSection +EndGlobal diff --git a/MicroserviceTemplate/build/Configuration.cs b/MicroserviceTemplate/build/Configuration.cs new file mode 100644 index 0000000..9b22a3b --- /dev/null +++ b/MicroserviceTemplate/build/Configuration.cs @@ -0,0 +1,14 @@ +using System.ComponentModel; +using Nuke.Common.Tooling; + +[TypeConverter(typeof(TypeConverter))] +public class Configuration : Enumeration +{ + public static Configuration Debug = new Configuration { Value = nameof(Debug) }; + public static Configuration Release = new Configuration { Value = nameof(Release) }; + + public static implicit operator string(Configuration configuration) + { + return configuration.Value; + } +} diff --git a/MicroserviceTemplate/build/Directory.Build.props b/MicroserviceTemplate/build/Directory.Build.props new file mode 100644 index 0000000..e147d63 --- /dev/null +++ b/MicroserviceTemplate/build/Directory.Build.props @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/MicroserviceTemplate/build/Directory.Build.targets b/MicroserviceTemplate/build/Directory.Build.targets new file mode 100644 index 0000000..2532609 --- /dev/null +++ b/MicroserviceTemplate/build/Directory.Build.targets @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/MicroserviceTemplate/build/appsettings.json b/MicroserviceTemplate/build/appsettings.json new file mode 100644 index 0000000..2ec8b36 --- /dev/null +++ b/MicroserviceTemplate/build/appsettings.json @@ -0,0 +1,6 @@ +{ + "ReleaseableProjects": [ + "Authentication.API.csproj", + "ServiceName.API.csproj" + ] +} \ No newline at end of file diff --git a/MicroserviceTemplate/build/build.cmd b/MicroserviceTemplate/build/build.cmd new file mode 100644 index 0000000..b08cc59 --- /dev/null +++ b/MicroserviceTemplate/build/build.cmd @@ -0,0 +1,7 @@ +:; set -eo pipefail +:; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) +:; ${SCRIPT_DIR}/build.sh "$@" +:; exit $? + +@ECHO OFF +powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %* diff --git a/MicroserviceTemplate/build/build.ps1 b/MicroserviceTemplate/build/build.ps1 new file mode 100644 index 0000000..d4781b1 --- /dev/null +++ b/MicroserviceTemplate/build/build.ps1 @@ -0,0 +1,69 @@ +[CmdletBinding()] +Param( + [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] + [string[]]$BuildArguments +) + +Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)" + +Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 } +$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent + +########################################################################### +# CONFIGURATION +########################################################################### + +$BuildProjectFile = "$PSScriptRoot\pipeline\pipeline.csproj" +$TempDirectory = "$PSScriptRoot\\.nuke\temp" + +$DotNetGlobalFile = "$PSScriptRoot\\global.json" +$DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1" +$DotNetChannel = "Current" + +$env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1 +$env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 +$env:DOTNET_MULTILEVEL_LOOKUP = 0 + +########################################################################### +# EXECUTION +########################################################################### + +function ExecSafe([scriptblock] $cmd) { + & $cmd + if ($LASTEXITCODE) { exit $LASTEXITCODE } +} + +# If dotnet CLI is installed globally and it matches requested version, use for execution +if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` + $(dotnet --version) -and $LASTEXITCODE -eq 0) { + $env:DOTNET_EXE = (Get-Command "dotnet").Path +} +else { + # Download install script + $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" + New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) + + # If global.json exists, load expected version + if (Test-Path $DotNetGlobalFile) { + $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) + if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { + $DotNetVersion = $DotNetGlobal.sdk.version + } + } + + # Install by channel or version + $DotNetDirectory = "$TempDirectory\dotnet-win" + if (!(Test-Path variable:DotNetVersion)) { + ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } + } else { + ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } + } + $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" +} + +Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)" + +ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet } +ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } diff --git a/MicroserviceTemplate/build/build.sh b/MicroserviceTemplate/build/build.sh new file mode 100644 index 0000000..8afa5f9 --- /dev/null +++ b/MicroserviceTemplate/build/build.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash + +bash --version 2>&1 | head -n 1 + +set -eo pipefail +SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) + +########################################################################### +# CONFIGURATION +########################################################################### + +BUILD_PROJECT_FILE="$SCRIPT_DIR/pipeline/pipeline.csproj" +TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp" + +DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json" +DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh" +DOTNET_CHANNEL="Current" + +export DOTNET_CLI_TELEMETRY_OPTOUT=1 +export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 +export DOTNET_MULTILEVEL_LOOKUP=0 + +########################################################################### +# EXECUTION +########################################################################### + +function FirstJsonValue { + perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}" +} + +# If dotnet CLI is installed globally and it matches requested version, use for execution +if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then + export DOTNET_EXE="$(command -v dotnet)" +else + # Download install script + DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh" + mkdir -p "$TEMP_DIRECTORY" + curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL" + chmod +x "$DOTNET_INSTALL_FILE" + + # If global.json exists, load expected version + if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then + DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")") + if [[ "$DOTNET_VERSION" == "" ]]; then + unset DOTNET_VERSION + fi + fi + + # Install by channel or version + DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" + if [[ -z ${DOTNET_VERSION+x} ]]; then + "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path + else + "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path + fi + export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet" +fi + +echo "Microsoft (R) .NET Core SDK version $("$DOTNET_EXE" --version)" + +"$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet +"$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" diff --git a/MicroserviceTemplate/build/pipeline.csproj.DotSettings b/MicroserviceTemplate/build/pipeline.csproj.DotSettings new file mode 100644 index 0000000..c8947fc --- /dev/null +++ b/MicroserviceTemplate/build/pipeline.csproj.DotSettings @@ -0,0 +1,26 @@ + + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + Implicit + Implicit + ExpressionBody + 0 + NEXT_LINE + True + False + 120 + IF_OWNER_IS_SINGLE_LINE + WRAP_IF_LONG + False + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True + True + True + True + True + True + True + True diff --git a/MicroserviceTemplate/src/Authentication.API/version.json b/MicroserviceTemplate/src/Authentication.API/version.json new file mode 100644 index 0000000..90fa5e8 --- /dev/null +++ b/MicroserviceTemplate/src/Authentication.API/version.json @@ -0,0 +1,3 @@ +{ + "version": "1.0" +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/BackgroundTasks/RepeatingTask.cs b/MicroserviceTemplate/src/ServiceName.API/BackgroundTasks/RepeatingTask.cs new file mode 100644 index 0000000..ff6a232 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.API/BackgroundTasks/RepeatingTask.cs @@ -0,0 +1,36 @@ +namespace ServiceName.API.BackgroundTasks +{ + /// + /// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-6.0 + /// https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/BackgroundServiceTests.cs + /// https://docs.microsoft.com/en-us/dotnet/architecture/microservices/multi-container-microservice-net-applications/background-tasks-with-ihostedservice + /// + public class RepeatingTask : BackgroundService + { + ILogger _logger; + PeriodicTimer _timer; + + //public RepeatingTask(ILogger logger) + public RepeatingTask() + { + //_logger = logger; + _timer = new PeriodicTimer(TimeSpan.FromSeconds(2)); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (await _timer.WaitForNextTickAsync(stoppingToken) + && !stoppingToken.IsCancellationRequested) + { + await DoWork(); + } + } + + private async Task DoWork() + { + //SQS + //https://www.youtube.com/watch?v=7OfUi3h-wmM + Console.WriteLine(DateTime.Now.ToString("O")); + } + } +} diff --git a/MicroserviceTemplate/src/ServiceName.API/Program.cs b/MicroserviceTemplate/src/ServiceName.API/Program.cs index b78b970..4bee51c 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Program.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Program.cs @@ -1,9 +1,6 @@ -using System.Text; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.IdentityModel.Tokens; +using ServiceName.API.BackgroundTasks; using ServiceName.API.Extensions; using ServiceName.Core; -using ServiceName.Core.Common.Security; using ServiceName.Infrastructure; var builder = WebApplication.CreateBuilder(args); @@ -31,6 +28,8 @@ builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddHostedService(); + var app = builder.Build(); app.MapControllers(); diff --git a/MicroserviceTemplate/src/ServiceName.API/version.json b/MicroserviceTemplate/src/ServiceName.API/version.json new file mode 100644 index 0000000..90fa5e8 --- /dev/null +++ b/MicroserviceTemplate/src/ServiceName.API/version.json @@ -0,0 +1,3 @@ +{ + "version": "1.0" +} \ No newline at end of file diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/CmdRunner.csproj b/MicroserviceTemplate/tools/EnvironmentInitializer/EnvironmentInitializer.csproj similarity index 100% rename from MicroserviceTemplate/tools/EnvironmentInitializer/CmdRunner.csproj rename to MicroserviceTemplate/tools/EnvironmentInitializer/EnvironmentInitializer.csproj diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs b/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs index 6782917..030b5d9 100644 --- a/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs @@ -17,6 +17,7 @@ /* * TODO: Clean up environment to re-run Tye * Kill Tye process + * wsl --shutdown * docker kill $(docker ps -q) * docker rm $(docker ps -a -q * docker network prune From d28a43ca40df2c6d2a32bee9bfb6edaa83de1fc9 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Thu, 23 Jun 2022 15:42:56 +1000 Subject: [PATCH 26/56] [MicroserviceTemplate] Enabled Push step in Build.cs Ran code clean-up with codemaid --- MicroserviceTemplate/aws/CdkStack.cs | 8 +- .../aws/GlobalSuppressions.cs | 2 +- MicroserviceTemplate/aws/Program.cs | 7 +- .../aws/Properties/launchSettings.json | 7 + MicroserviceTemplate/aws/cdk.json | 2 +- MicroserviceTemplate/aws/response.json | 6 +- .../build/.nuke/build.schema.json | 4 + .../build/.nuke/parameters.json | 2 +- MicroserviceTemplate/build/Build.cs | 109 ++++++----- MicroserviceTemplate/build/Configuration.cs | 2 +- .../build/Properties/launchSettings.json | 8 + MicroserviceTemplate/build/appsettings.json | 2 +- MicroserviceTemplate/build/build.cmd | 7 - MicroserviceTemplate/build/build.ps1 | 69 ------- MicroserviceTemplate/build/build.sh | 62 ------- .../Extensions/AuthExtension.cs | 6 +- .../HealthCheck/DynamoDbHealthCheck.cs | 16 +- .../Extensions/HealthCheckExtensions.cs | 4 +- .../Swagger/ConfigureSwaggerOptions.cs | 6 +- .../Swagger/SwaggerDefaultValues.cs | 2 +- .../Extensions/SwaggerExtensions.cs | 10 +- .../Extensions/VersionExtension.cs | 4 +- .../src/Authentication.API/Program.cs | 7 +- .../Properties/launchSettings.json | 30 +-- .../appsettings.Development.json | 104 +++++------ .../aws-lambda-tools-defaults.json | 24 +-- .../BackgroundTasks/RepeatingTask.cs | 6 +- .../Extensions/AuthExtension.cs | 6 +- .../Extensions/EndpointExtensions.cs | 2 - .../HealthCheck/DynamoDbHealthCheck.cs | 16 +- .../Extensions/HealthCheckExtensions.cs | 4 +- .../Swagger/ConfigureSwaggerOptions.cs | 6 +- .../Swagger/SwaggerDefaultValues.cs | 2 +- .../Extensions/SwaggerExtensions.cs | 10 +- .../Extensions/VersionExtension.cs | 4 +- .../src/ServiceName.API/Program.cs | 3 +- .../Properties/launchSettings.json | 30 +-- .../appsettings.Development.json | 104 +++++------ .../aws-lambda-tools-defaults.json | 24 +-- .../CQRS/Commands/SaveSettingsCommand.cs | 8 +- .../CQRS/Queries/GetSettingsQuery.cs | 15 +- .../Common/Behaviours/LoggingBehaviour.cs | 8 +- .../Interfaces/IJwtAuthenticationService.cs | 1 + .../Common/Interfaces/INotificationService.cs | 3 +- .../Common/Interfaces/IRepositoryService.cs | 5 +- .../Common/Security/SecurityHelper.cs | 4 +- .../src/ServiceName.Core/ConfigureServices.cs | 2 +- .../ServiceName.Core/Model/ModuleOptions.cs | 2 +- .../ServiceName.Core/Model/SettingGroup.cs | 1 + .../Authentication/AssymetricKmsJwtService.cs | 15 +- .../Authentication/JWT/CustomJwtHeader.cs | 3 +- .../Authentication/JWT/CustomJwtPayload.cs | 17 +- .../SymmetricJwtAuthenticationService.cs | 4 +- .../ConfigureServices.cs | 27 ++- .../DynamoDBModel/SettingDbRecord.cs | 3 +- .../Repositories/SettingsRepositoryService.cs | 9 +- .../ServiceName.Test/Helpers/TestHelper.cs | 12 +- .../tests/ServiceName.Test/JwtTests.cs | 171 +++++++++--------- .../tests/ServiceName.Test/RepositoryTests.cs | 19 +- .../tools/EnvironmentInitializer/Program.cs | 1 - 60 files changed, 468 insertions(+), 589 deletions(-) create mode 100644 MicroserviceTemplate/aws/Properties/launchSettings.json create mode 100644 MicroserviceTemplate/build/Properties/launchSettings.json delete mode 100644 MicroserviceTemplate/build/build.cmd delete mode 100644 MicroserviceTemplate/build/build.ps1 delete mode 100644 MicroserviceTemplate/build/build.sh diff --git a/MicroserviceTemplate/aws/CdkStack.cs b/MicroserviceTemplate/aws/CdkStack.cs index a1ce3b6..bb986b5 100644 --- a/MicroserviceTemplate/aws/CdkStack.cs +++ b/MicroserviceTemplate/aws/CdkStack.cs @@ -1,10 +1,9 @@ using Amazon.CDK; using Amazon.CDK.AWS.APIGateway; +using Amazon.CDK.AWS.DynamoDB; using Amazon.CDK.AWS.Lambda; using Amazon.CDK.AWS.Logs; -using Amazon.CDK.AWS.DynamoDB; using Constructs; -using Amazon.CDK.AWS.CertificateManager; namespace Cdk { @@ -13,7 +12,7 @@ public class CdkStack : Stack internal CdkStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { //https://www.alexdebrie.com/posts/api-gateway-access-logs/ - + // logGroup.set = "/cloudwatchlogs/dotnet6-app/"; //Bucket logBucket = new Bucket(this, "S3 Bucket"); @@ -96,7 +95,6 @@ internal CdkStack(Construct scope, string id, IStackProps props = null) : base(s }); dynamoTable.GrantFullAccess(lambda); - } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/aws/GlobalSuppressions.cs b/MicroserviceTemplate/aws/GlobalSuppressions.cs index 26233fc..d0baa81 100644 --- a/MicroserviceTemplate/aws/GlobalSuppressions.cs +++ b/MicroserviceTemplate/aws/GlobalSuppressions.cs @@ -1 +1 @@ -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Potential Code Quality Issues", "RECS0026:Possible unassigned object created by 'new'", Justification = "Constructs add themselves to the scope in which they are created")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Potential Code Quality Issues", "RECS0026:Possible unassigned object created by 'new'", Justification = "Constructs add themselves to the scope in which they are created")] \ No newline at end of file diff --git a/MicroserviceTemplate/aws/Program.cs b/MicroserviceTemplate/aws/Program.cs index 460a9e2..5841124 100644 --- a/MicroserviceTemplate/aws/Program.cs +++ b/MicroserviceTemplate/aws/Program.cs @@ -1,11 +1,8 @@ using Amazon.CDK; -using System; -using System.Collections.Generic; -using System.Linq; namespace Cdk { - sealed class Program + internal sealed class Program { public static void Main(string[] args) { @@ -41,4 +38,4 @@ public static void Main(string[] args) app.Synth(); } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/aws/Properties/launchSettings.json b/MicroserviceTemplate/aws/Properties/launchSettings.json new file mode 100644 index 0000000..fb403a6 --- /dev/null +++ b/MicroserviceTemplate/aws/Properties/launchSettings.json @@ -0,0 +1,7 @@ +{ + "profiles": { + "Cdk": { + "commandName": "Project" + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/aws/cdk.json b/MicroserviceTemplate/aws/cdk.json index 0b9e580..a2c2d33 100644 --- a/MicroserviceTemplate/aws/cdk.json +++ b/MicroserviceTemplate/aws/cdk.json @@ -29,4 +29,4 @@ "aws-cn" ] } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/aws/response.json b/MicroserviceTemplate/aws/response.json index 20f591f..bb506e6 100644 --- a/MicroserviceTemplate/aws/response.json +++ b/MicroserviceTemplate/aws/response.json @@ -1 +1,5 @@ -{"errorMessage":"Object reference not set to an instance of an object.","errorType":"NullReferenceException","stackTrace":["at Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction.MarshallRequest(InvokeFeatures features, APIGatewayProxyRequest apiGatewayRequest, ILambdaContext lambdaContext)","at Amazon.Lambda.AspNetCoreServer.AbstractAspNetCoreFunction`2.FunctionHandlerAsync(TREQUEST request, ILambdaContext lambdaContext)","at Amazon.Lambda.RuntimeSupport.HandlerWrapper.<>c__DisplayClass26_0`2.<b__0>d.MoveNext()","--- End of stack trace from previous location ---","at Amazon.Lambda.RuntimeSupport.LambdaBootstrap.InvokeOnceAsync(CancellationToken cancellationToken)"]} +{ + "errorMessage": "Object reference not set to an instance of an object.", + "errorType": "NullReferenceException", + "stackTrace": [ "at Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction.MarshallRequest(InvokeFeatures features, APIGatewayProxyRequest apiGatewayRequest, ILambdaContext lambdaContext)", "at Amazon.Lambda.AspNetCoreServer.AbstractAspNetCoreFunction`2.FunctionHandlerAsync(TREQUEST request, ILambdaContext lambdaContext)", "at Amazon.Lambda.RuntimeSupport.HandlerWrapper.<>c__DisplayClass26_0`2.<b__0>d.MoveNext()", "--- End of stack trace from previous location ---", "at Amazon.Lambda.RuntimeSupport.LambdaBootstrap.InvokeOnceAsync(CancellationToken cancellationToken)" ] +} \ No newline at end of file diff --git a/MicroserviceTemplate/build/.nuke/build.schema.json b/MicroserviceTemplate/build/.nuke/build.schema.json index 0202870..a42063a 100644 --- a/MicroserviceTemplate/build/.nuke/build.schema.json +++ b/MicroserviceTemplate/build/.nuke/build.schema.json @@ -84,7 +84,9 @@ "Clean", "Compile", "Pack", + "Push", "Restore", + "Test", "Versioning" ] } @@ -102,7 +104,9 @@ "Clean", "Compile", "Pack", + "Push", "Restore", + "Test", "Versioning" ] } diff --git a/MicroserviceTemplate/build/.nuke/parameters.json b/MicroserviceTemplate/build/.nuke/parameters.json index 8e9eacd..e670b09 100644 --- a/MicroserviceTemplate/build/.nuke/parameters.json +++ b/MicroserviceTemplate/build/.nuke/parameters.json @@ -1,4 +1,4 @@ { "$schema": "./build.schema.json", "Solution": "../All.sln" -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/build/Build.cs b/MicroserviceTemplate/build/Build.cs index 1920064..56bbde5 100644 --- a/MicroserviceTemplate/build/Build.cs +++ b/MicroserviceTemplate/build/Build.cs @@ -19,29 +19,30 @@ using static Nuke.Common.IO.PathConstruction; [ShutdownDotNetAfterServerBuild] -class Build : NukeBuild +internal class Build : NukeBuild { - readonly IConfiguration _configuration; - readonly Dictionary _versions; - readonly List _csProjects; - readonly List _releaseableProjects; + private readonly IConfiguration _configuration; + private readonly Dictionary _versions; + private readonly List _srcProjects; + private readonly List _testProjects; + private readonly List _outputProjects; + private readonly List _releaseableProjects; - - public static int Main() => Execute(x => x.Pack); + public static int Main() => Execute(x => x.Push); [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] - readonly Configuration Configuration = Configuration.Release; + private readonly Configuration Configuration = Configuration.Release; - [Parameter] string OctopusServerUrl; - [Parameter] string OctopusApiKey; - [Parameter] string OctopusSpaceId; + [Parameter] private string OctopusServerUrl; + [Parameter] private string OctopusApiKey; + [Parameter] private string OctopusSpaceId; - [Solution] readonly Solution Solution; - AbsolutePath SolutionDirectory => RootDirectory.Parent; - AbsolutePath SourceDirectory => SolutionDirectory / "src"; - AbsolutePath TestDirectory => SolutionDirectory / "test"; - AbsolutePath ToolDirectory => SolutionDirectory / "tools"; - AbsolutePath OutputDirectory => SolutionDirectory / "output"; + [Solution] private readonly Solution Solution; + private AbsolutePath SolutionDirectory => RootDirectory.Parent; + private AbsolutePath SourceDirectory => SolutionDirectory / "src"; + private AbsolutePath TestDirectory => SolutionDirectory / "tests"; + private AbsolutePath ToolDirectory => SolutionDirectory / "tools"; + private AbsolutePath OutputDirectory => SolutionDirectory / "output"; public Build() { @@ -52,10 +53,12 @@ public Build() _versions = new Dictionary(); _releaseableProjects = _configuration.GetSection("ReleaseableProjects").Get>(); - _csProjects = SolutionDirectory.GlobFiles("**/*.csproj").Where(p => _releaseableProjects.Contains(new FileInfo(p).Name)).Select(p => new FileInfo(p)).ToList(); + _srcProjects = SourceDirectory.GlobFiles("**/*.csproj").Select(p => new FileInfo(p)).ToList(); + _testProjects = TestDirectory.GlobFiles("**/*.csproj").Select(p => new FileInfo(p)).ToList(); + _outputProjects = _srcProjects.Where(p => _releaseableProjects.Contains(p.Name)).ToList(); } - Target Clean => _ => _ + private Target Clean => _ => _ .Executes(() => { SourceDirectory.GlobDirectories("**/bin", "**/obj").ForEach(DeleteDirectory); @@ -64,18 +67,21 @@ public Build() EnsureCleanDirectory(OutputDirectory); }); - Target Restore => _ => _ + private Target Restore => _ => _ .DependsOn(Clean) .Executes(() => { - NuGetTasks.NuGetRestore(s => s.SetTargetPath(SolutionDirectory)); + foreach (var project in _srcProjects) + { + DotNetTasks.DotNetRestore(o => o.SetProjectFile(project.FullName)); + } }); - Target Versioning => _ => _ + private Target Versioning => _ => _ .DependsOn(Restore) .Executes(() => { - foreach (var csProject in _csProjects) + foreach (var csProject in _outputProjects) { var versionResult = NerdbankGitVersioningTasks.NerdbankGitVersioningGetVersion(v => v.SetProcessWorkingDirectory(csProject.DirectoryName).SetProcessArgumentConfigurator(a => a.Add("-f json"))).Result; NerdbankGitVersioningTasks.NerdbankGitVersioningSetVersion(v => v.SetProject(csProject.DirectoryName) @@ -85,21 +91,31 @@ public Build() } }); - Target Compile => _ => _ + private Target Compile => _ => _ .DependsOn(Versioning) .Executes(() => { - MSBuildTasks.MSBuild(s => s - .SetTargetPath(SolutionDirectory) - .SetConfiguration(Configuration) - ); + foreach (var project in _srcProjects) + { + DotNetTasks.DotNetBuild(o => o.SetProjectFile(project.FullName)); + } }); - Target Pack => _ => _ - .DependsOn(Compile) + private Target Test => _ => _ + .DependsOn(Compile) + .Executes(() => + { + foreach (var project in _testProjects) + { + DotNetTasks.DotNetTest(o => o.SetProjectFile(project.FullName)); + } + }); + + private Target Pack => _ => _ + .DependsOn(Test) .Executes(() => { - foreach (var csProject in _csProjects) + foreach (var csProject in _outputProjects) { var packageId = csProject.Name.Replace(".csproj", string.Empty); var sourcePath = $"{csProject.DirectoryName}\\bin"; @@ -123,19 +139,18 @@ public Build() } }); - //Target Push => _ => _ - // .DependsOn(Pack) - // .Executes(() => - // { - // var packages = SolutionDirectory.GlobFiles("**/output/*.nupkg"); - // foreach (var package in packages) - // { - // //if the package exists the default behaviour is to reject the package - // OctopusTasks.OctopusPush(o => o.SetServer(OctopusServerUrl) - // .SetApiKey(OctopusApiKey) - // .SetSpace(OctopusSpaceId) - // .SetPackage(package)); - // } - - // }); -} + private Target Push => _ => _ + .DependsOn(Pack) + .Executes(() => + { + var outputPackages = OutputDirectory.GlobFiles("**/*.nupkg").ToList(); + foreach (var package in outputPackages) + { + //if the package exists the default behaviour is to reject the package + OctopusTasks.OctopusPush(o => o.SetServer(OctopusServerUrl) + .SetApiKey(OctopusApiKey) + .SetSpace(OctopusSpaceId) + .SetPackage(package)); + } + }); +} \ No newline at end of file diff --git a/MicroserviceTemplate/build/Configuration.cs b/MicroserviceTemplate/build/Configuration.cs index 9b22a3b..9749190 100644 --- a/MicroserviceTemplate/build/Configuration.cs +++ b/MicroserviceTemplate/build/Configuration.cs @@ -11,4 +11,4 @@ public static implicit operator string(Configuration configuration) { return configuration.Value; } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/build/Properties/launchSettings.json b/MicroserviceTemplate/build/Properties/launchSettings.json new file mode 100644 index 0000000..912cf43 --- /dev/null +++ b/MicroserviceTemplate/build/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Build": { + "commandName": "Project", + "commandLineArgs": "--OctopusServerUrl \"https://octoserver\" --OctopusApiKey \"abc\" --OctopusSpaceId \"pae\"" + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/build/appsettings.json b/MicroserviceTemplate/build/appsettings.json index 2ec8b36..a191034 100644 --- a/MicroserviceTemplate/build/appsettings.json +++ b/MicroserviceTemplate/build/appsettings.json @@ -2,5 +2,5 @@ "ReleaseableProjects": [ "Authentication.API.csproj", "ServiceName.API.csproj" - ] + ] } \ No newline at end of file diff --git a/MicroserviceTemplate/build/build.cmd b/MicroserviceTemplate/build/build.cmd deleted file mode 100644 index b08cc59..0000000 --- a/MicroserviceTemplate/build/build.cmd +++ /dev/null @@ -1,7 +0,0 @@ -:; set -eo pipefail -:; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) -:; ${SCRIPT_DIR}/build.sh "$@" -:; exit $? - -@ECHO OFF -powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %* diff --git a/MicroserviceTemplate/build/build.ps1 b/MicroserviceTemplate/build/build.ps1 deleted file mode 100644 index d4781b1..0000000 --- a/MicroserviceTemplate/build/build.ps1 +++ /dev/null @@ -1,69 +0,0 @@ -[CmdletBinding()] -Param( - [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] - [string[]]$BuildArguments -) - -Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)" - -Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 } -$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent - -########################################################################### -# CONFIGURATION -########################################################################### - -$BuildProjectFile = "$PSScriptRoot\pipeline\pipeline.csproj" -$TempDirectory = "$PSScriptRoot\\.nuke\temp" - -$DotNetGlobalFile = "$PSScriptRoot\\global.json" -$DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1" -$DotNetChannel = "Current" - -$env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1 -$env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 -$env:DOTNET_MULTILEVEL_LOOKUP = 0 - -########################################################################### -# EXECUTION -########################################################################### - -function ExecSafe([scriptblock] $cmd) { - & $cmd - if ($LASTEXITCODE) { exit $LASTEXITCODE } -} - -# If dotnet CLI is installed globally and it matches requested version, use for execution -if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` - $(dotnet --version) -and $LASTEXITCODE -eq 0) { - $env:DOTNET_EXE = (Get-Command "dotnet").Path -} -else { - # Download install script - $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" - New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null - [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) - - # If global.json exists, load expected version - if (Test-Path $DotNetGlobalFile) { - $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) - if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { - $DotNetVersion = $DotNetGlobal.sdk.version - } - } - - # Install by channel or version - $DotNetDirectory = "$TempDirectory\dotnet-win" - if (!(Test-Path variable:DotNetVersion)) { - ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } - } else { - ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } - } - $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" -} - -Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)" - -ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet } -ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } diff --git a/MicroserviceTemplate/build/build.sh b/MicroserviceTemplate/build/build.sh deleted file mode 100644 index 8afa5f9..0000000 --- a/MicroserviceTemplate/build/build.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash - -bash --version 2>&1 | head -n 1 - -set -eo pipefail -SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) - -########################################################################### -# CONFIGURATION -########################################################################### - -BUILD_PROJECT_FILE="$SCRIPT_DIR/pipeline/pipeline.csproj" -TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp" - -DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json" -DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh" -DOTNET_CHANNEL="Current" - -export DOTNET_CLI_TELEMETRY_OPTOUT=1 -export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 -export DOTNET_MULTILEVEL_LOOKUP=0 - -########################################################################### -# EXECUTION -########################################################################### - -function FirstJsonValue { - perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}" -} - -# If dotnet CLI is installed globally and it matches requested version, use for execution -if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then - export DOTNET_EXE="$(command -v dotnet)" -else - # Download install script - DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh" - mkdir -p "$TEMP_DIRECTORY" - curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL" - chmod +x "$DOTNET_INSTALL_FILE" - - # If global.json exists, load expected version - if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then - DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")") - if [[ "$DOTNET_VERSION" == "" ]]; then - unset DOTNET_VERSION - fi - fi - - # Install by channel or version - DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" - if [[ -z ${DOTNET_VERSION+x} ]]; then - "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path - else - "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path - fi - export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet" -fi - -echo "Microsoft (R) .NET Core SDK version $("$DOTNET_EXE" --version)" - -"$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet -"$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/AuthExtension.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/AuthExtension.cs index ce2efc5..e73ccac 100644 --- a/MicroserviceTemplate/src/Authentication.API/Extensions/AuthExtension.cs +++ b/MicroserviceTemplate/src/Authentication.API/Extensions/AuthExtension.cs @@ -29,10 +29,10 @@ public static void AddAuthSupport(this IServiceCollection services, Configuratio services.AddAuthorization(); } - public static void UseAuth(this WebApplication app) { - + public static void UseAuth(this WebApplication app) + { app.UseAuthentication(); app.UseAuthorization(); } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs index a2e06a2..12e6296 100644 --- a/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs +++ b/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs @@ -7,13 +7,13 @@ namespace ServiceName.API.Extensions.HealthCheck { public class DynamoDbHealthCheck : IHealthCheck { - readonly IConfiguration _configuration; - + private readonly IConfiguration _configuration; + public DynamoDbHealthCheck(IConfiguration configuration) { _configuration = configuration; } - + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { try @@ -35,18 +35,16 @@ public Task CheckHealthAsync(HealthCheckContext context, Canc clientConfig.ServiceURL = localTestEndpoint; } - var amazonDynamoDBClient = new AmazonDynamoDBClient(accessKey, secretKey, clientConfig); - + var amazonDynamoDBClient = new AmazonDynamoDBClient(accessKey, secretKey, clientConfig); + Table.LoadTable(amazonDynamoDBClient, tableName); - + return Task.FromResult(HealthCheckResult.Healthy($"Table {tableName} exists.")); } catch (Exception ex) { return Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus, ex.Message)); } - - } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheckExtensions.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheckExtensions.cs index 993269b..615713a 100644 --- a/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheckExtensions.cs +++ b/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheckExtensions.cs @@ -17,7 +17,6 @@ namespace ServiceName.API.Extensions /// public static class HealthCheckExtensions { - public static void AddHealthCheckSupport(this IServiceCollection services, ConfigurationManager configurationManager) { var sqlServerName = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Server"]; @@ -28,7 +27,6 @@ public static void AddHealthCheckSupport(this IServiceCollection services, Confi var sqlConnectionString = $"Data Source={sqlServerName},{sqlPort};Initial Catalog={sqlDatabase};User ID={sqlUsername};Password={sqlPassword}"; - var redisServerName = configurationManager["ModuleConfiguration:Infrastructure:Redis:Server"]; var redisPort = configurationManager["ModuleConfiguration:Infrastructure:Redis:Port"]; @@ -59,4 +57,4 @@ public static void ConfigureHealthCheck(this WebApplication app) }); } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/ConfigureSwaggerOptions.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/ConfigureSwaggerOptions.cs index a3abfc9..ddf4849 100644 --- a/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/ConfigureSwaggerOptions.cs +++ b/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/ConfigureSwaggerOptions.cs @@ -1,11 +1,11 @@ namespace ServiceName.API.Extensions.Swagger { + using System; using Asp.Versioning.ApiExplorer; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; - using System; /// /// Configures the Swagger generation options. @@ -14,7 +14,7 @@ /// service has been resolved from the service container. public class ConfigureSwaggerOptions : IConfigureOptions { - readonly IApiVersionDescriptionProvider provider; + private readonly IApiVersionDescriptionProvider provider; /// /// Initializes a new instance of the class. @@ -33,7 +33,7 @@ public void Configure(SwaggerGenOptions options) } } - static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) + private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) { var info = new OpenApiInfo() { diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/SwaggerDefaultValues.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/SwaggerDefaultValues.cs index af07330..1a29ec6 100644 --- a/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/SwaggerDefaultValues.cs +++ b/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/SwaggerDefaultValues.cs @@ -1,10 +1,10 @@ namespace ServiceName.API.Extensions.Swagger { + using System.Linq; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; - using System.Linq; /// /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter. diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/SwaggerExtensions.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/SwaggerExtensions.cs index ea1e41a..d4bd87d 100644 --- a/MicroserviceTemplate/src/Authentication.API/Extensions/SwaggerExtensions.cs +++ b/MicroserviceTemplate/src/Authentication.API/Extensions/SwaggerExtensions.cs @@ -9,8 +9,8 @@ public static class SwaggerExtensions { public static void AddSwaggerSupport(this IServiceCollection services) { - services.AddTransient, ConfigureSwaggerOptions>(); - + services.AddTransient, ConfigureSwaggerOptions>(); + services.AddSwaggerGen(options => { options.OperationFilter(); @@ -44,14 +44,14 @@ public static void AddSwaggerSupport(this IServiceCollection services) options.AddSecurityRequirement(securityReq); }); } - + public static void ConfigureSwaggerUI(this WebApplication app) { app.UseSwagger(); app.UseSwaggerUI(options => { options.RoutePrefix = string.Empty; - + var descriptions = app.DescribeApiVersions(); // build a swagger endpoint for each discovered API version @@ -64,4 +64,4 @@ public static void ConfigureSwaggerUI(this WebApplication app) }); } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/VersionExtension.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/VersionExtension.cs index 3ceea13..8badf2e 100644 --- a/MicroserviceTemplate/src/Authentication.API/Extensions/VersionExtension.cs +++ b/MicroserviceTemplate/src/Authentication.API/Extensions/VersionExtension.cs @@ -3,7 +3,7 @@ public static class VersionExtension { public static void AddApiVersioningSupport(this IServiceCollection services) - { + { services.AddApiVersioning() .AddApiExplorer( options => @@ -18,4 +18,4 @@ public static void AddApiVersioningSupport(this IServiceCollection services) }); } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Program.cs b/MicroserviceTemplate/src/Authentication.API/Program.cs index b78b970..d4cbe61 100644 --- a/MicroserviceTemplate/src/Authentication.API/Program.cs +++ b/MicroserviceTemplate/src/Authentication.API/Program.cs @@ -1,9 +1,5 @@ -using System.Text; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.IdentityModel.Tokens; using ServiceName.API.Extensions; using ServiceName.Core; -using ServiceName.Core.Common.Security; using ServiceName.Infrastructure; var builder = WebApplication.CreateBuilder(args); @@ -48,5 +44,4 @@ app.UseHttpsRedirection(); -app.Run(); - +app.Run(); \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Properties/launchSettings.json b/MicroserviceTemplate/src/Authentication.API/Properties/launchSettings.json index fc4a0ac..ffdb25f 100644 --- a/MicroserviceTemplate/src/Authentication.API/Properties/launchSettings.json +++ b/MicroserviceTemplate/src/Authentication.API/Properties/launchSettings.json @@ -1,18 +1,18 @@ { - "profiles": { - "Authentication.API": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:62060;http://localhost:62061" - }, - "Mock Lambda Test Tool": { - "commandName": "Executable", - "commandLineArgs": "--port 5050", - "workingDirectory": ".\\bin\\$(Configuration)\\net6.0", - "executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe" + "profiles": { + "Authentication.API": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:62060;http://localhost:62061" + }, + "Mock Lambda Test Tool": { + "commandName": "Executable", + "commandLineArgs": "--port 5050", + "workingDirectory": ".\\bin\\$(Configuration)\\net6.0", + "executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe" + } } - } } \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/appsettings.Development.json b/MicroserviceTemplate/src/Authentication.API/appsettings.Development.json index 61043f5..19cb35b 100644 --- a/MicroserviceTemplate/src/Authentication.API/appsettings.Development.json +++ b/MicroserviceTemplate/src/Authentication.API/appsettings.Development.json @@ -1,56 +1,56 @@ { - "Logging": { - "Sink": "CloudWatchLogs", - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*", - "ModuleConfiguration": { - "IsSwaggerUIEnabled": true, - "Infrastructure": { - "Kms": { - "AccessKey": "test", - "SecretKey": "test", - "RegionEndpoint": "eu-west-2", - "LocalTestEndpoint": "http://localhost:52002", - "SigningKeyId": "51c88cf9-e753-44ad-b9cf-00c1c11b6727", - "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxu5nBIn8lmIOiH1DUmQHKbv9Ss6KB\u002BtJDivCrKPh0n3os2SODkmdzR/dnwZdZUYMU21sxrLWTe3AoPzMHZ/sm2CGnDTYugo6X893Y8AJGbQWzLhR4BDQpEhMLTdBPMz3F1z6GqgYXth5k5LHBcg8avejIjb63smJZoqcuhEgRNx9sjJBvVBTagYRJYoiAK3esod1BtTewlZf1LZITLVec4DlIk2tWtVMus9JC\u002BehCYz1rXwL0hMT0subZrXhNsTPjN1lAT\u002BX3\u002BvwhScpvNcCI89qZx2WJbEA4okoKpxKsK0lkUfPeqVjUMkbD/xXy0pIPgqAjiN/EEwO4VTfH7CB9wIDAQAB" - }, - "CloudWatchLogs": { - "AccessKey": "test", - "SecretKey": "test", - "RegionEndpoint": "ap-southeast-2", - "LocalTestEndpoint": "http://host.docker.internal:4566", - "LogGroupName": "/LocalStack/Microservice/Logs" - }, - "DynamoDb": { - "AccessKey": "test", - "SecretKey": "test", - "RegionEndpoint": "ap-southeast-2", - "LocalTestEndpoint": "http://host.docker.internal:8000", - "TableName": "ServiceName_Setting" - }, - "Seq": { - "ServerUrl": "http://localhost:5341", - "ApiKey": "" - }, - "SqlServer": { - "Server": "host.docker.internal", - "Port": "1433", - "Database": "DBName", - "Username": "sa", - "Password": "M3rz0ug4!!!!" - }, - "Redis": { - "Server": "host.docker.internal", - "Port": "6379" - } + "Logging": { + "Sink": "CloudWatchLogs", + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } }, - "Jwt": { - "Issuer": "issuer", - "Audience": "audience" + "AllowedHosts": "*", + "ModuleConfiguration": { + "IsSwaggerUIEnabled": true, + "Infrastructure": { + "Kms": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "eu-west-2", + "LocalTestEndpoint": "http://localhost:52002", + "SigningKeyId": "51c88cf9-e753-44ad-b9cf-00c1c11b6727", + "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxu5nBIn8lmIOiH1DUmQHKbv9Ss6KB\u002BtJDivCrKPh0n3os2SODkmdzR/dnwZdZUYMU21sxrLWTe3AoPzMHZ/sm2CGnDTYugo6X893Y8AJGbQWzLhR4BDQpEhMLTdBPMz3F1z6GqgYXth5k5LHBcg8avejIjb63smJZoqcuhEgRNx9sjJBvVBTagYRJYoiAK3esod1BtTewlZf1LZITLVec4DlIk2tWtVMus9JC\u002BehCYz1rXwL0hMT0subZrXhNsTPjN1lAT\u002BX3\u002BvwhScpvNcCI89qZx2WJbEA4okoKpxKsK0lkUfPeqVjUMkbD/xXy0pIPgqAjiN/EEwO4VTfH7CB9wIDAQAB" + }, + "CloudWatchLogs": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "LocalTestEndpoint": "http://host.docker.internal:4566", + "LogGroupName": "/LocalStack/Microservice/Logs" + }, + "DynamoDb": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "LocalTestEndpoint": "http://host.docker.internal:8000", + "TableName": "ServiceName_Setting" + }, + "Seq": { + "ServerUrl": "http://localhost:5341", + "ApiKey": "" + }, + "SqlServer": { + "Server": "host.docker.internal", + "Port": "1433", + "Database": "DBName", + "Username": "sa", + "Password": "M3rz0ug4!!!!" + }, + "Redis": { + "Server": "host.docker.internal", + "Port": "6379" + } + }, + "Jwt": { + "Issuer": "issuer", + "Audience": "audience" + } } - } } \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/aws-lambda-tools-defaults.json b/MicroserviceTemplate/src/Authentication.API/aws-lambda-tools-defaults.json index 950200c..73f3049 100644 --- a/MicroserviceTemplate/src/Authentication.API/aws-lambda-tools-defaults.json +++ b/MicroserviceTemplate/src/Authentication.API/aws-lambda-tools-defaults.json @@ -1,14 +1,14 @@ { - "Information": [ - "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", - "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", - "dotnet lambda help", - "All the command line options for the Lambda command can be specified in this file." - ], - "profile": "", - "region": "", - "configuration": "Release", - "s3-prefix": "MinimalApi/", - "template": "serverless.template", - "template-parameters": "" + "Information": [ + "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", + "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", + "dotnet lambda help", + "All the command line options for the Lambda command can be specified in this file." + ], + "profile": "", + "region": "", + "configuration": "Release", + "s3-prefix": "MinimalApi/", + "template": "serverless.template", + "template-parameters": "" } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/BackgroundTasks/RepeatingTask.cs b/MicroserviceTemplate/src/ServiceName.API/BackgroundTasks/RepeatingTask.cs index ff6a232..7fff04b 100644 --- a/MicroserviceTemplate/src/ServiceName.API/BackgroundTasks/RepeatingTask.cs +++ b/MicroserviceTemplate/src/ServiceName.API/BackgroundTasks/RepeatingTask.cs @@ -7,8 +7,8 @@ /// public class RepeatingTask : BackgroundService { - ILogger _logger; - PeriodicTimer _timer; + private ILogger _logger; + private PeriodicTimer _timer; //public RepeatingTask(ILogger logger) public RepeatingTask() @@ -33,4 +33,4 @@ private async Task DoWork() Console.WriteLine(DateTime.Now.ToString("O")); } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthExtension.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthExtension.cs index ce2efc5..e73ccac 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthExtension.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthExtension.cs @@ -29,10 +29,10 @@ public static void AddAuthSupport(this IServiceCollection services, Configuratio services.AddAuthorization(); } - public static void UseAuth(this WebApplication app) { - + public static void UseAuth(this WebApplication app) + { app.UseAuthentication(); app.UseAuthorization(); } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs index 17a0871..98bd9dd 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs @@ -3,7 +3,6 @@ using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using ServiceName.Core.Common.Interfaces; using ServiceName.Core.CQRS.Commands; using ServiceName.Core.CQRS.Queries; using ServiceName.Core.Model; @@ -21,7 +20,6 @@ public static void MapEndpoints(this WebApplication app, ConfigurationManager co .ReportApiVersions() .Build(); - app.MapGet("/settings", [Authorize] async (IMediator mediator, [FromHeader] string authorization) => await mediator.Send(new GetSettingsQueryRequest() { TenantId = GetTenantIdFromJwt(authorization) })) .WithApiVersionSet(versionSet) .MapToApiVersion(1.0); diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs index a2e06a2..12e6296 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs @@ -7,13 +7,13 @@ namespace ServiceName.API.Extensions.HealthCheck { public class DynamoDbHealthCheck : IHealthCheck { - readonly IConfiguration _configuration; - + private readonly IConfiguration _configuration; + public DynamoDbHealthCheck(IConfiguration configuration) { _configuration = configuration; } - + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { try @@ -35,18 +35,16 @@ public Task CheckHealthAsync(HealthCheckContext context, Canc clientConfig.ServiceURL = localTestEndpoint; } - var amazonDynamoDBClient = new AmazonDynamoDBClient(accessKey, secretKey, clientConfig); - + var amazonDynamoDBClient = new AmazonDynamoDBClient(accessKey, secretKey, clientConfig); + Table.LoadTable(amazonDynamoDBClient, tableName); - + return Task.FromResult(HealthCheckResult.Healthy($"Table {tableName} exists.")); } catch (Exception ex) { return Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus, ex.Message)); } - - } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs index 993269b..615713a 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs @@ -17,7 +17,6 @@ namespace ServiceName.API.Extensions /// public static class HealthCheckExtensions { - public static void AddHealthCheckSupport(this IServiceCollection services, ConfigurationManager configurationManager) { var sqlServerName = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Server"]; @@ -28,7 +27,6 @@ public static void AddHealthCheckSupport(this IServiceCollection services, Confi var sqlConnectionString = $"Data Source={sqlServerName},{sqlPort};Initial Catalog={sqlDatabase};User ID={sqlUsername};Password={sqlPassword}"; - var redisServerName = configurationManager["ModuleConfiguration:Infrastructure:Redis:Server"]; var redisPort = configurationManager["ModuleConfiguration:Infrastructure:Redis:Port"]; @@ -59,4 +57,4 @@ public static void ConfigureHealthCheck(this WebApplication app) }); } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs index c53576f..8674968 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs @@ -1,11 +1,11 @@ namespace ServiceName.API.Extensions.Swagger { + using System; using Asp.Versioning.ApiExplorer; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; - using System; /// /// Configures the Swagger generation options. @@ -14,7 +14,7 @@ /// service has been resolved from the service container. public class ConfigureSwaggerOptions : IConfigureOptions { - readonly IApiVersionDescriptionProvider provider; + private readonly IApiVersionDescriptionProvider provider; /// /// Initializes a new instance of the class. @@ -33,7 +33,7 @@ public void Configure(SwaggerGenOptions options) } } - static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) + private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) { var info = new OpenApiInfo() { diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/SwaggerDefaultValues.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/SwaggerDefaultValues.cs index 2cc1783..767bd28 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/SwaggerDefaultValues.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/SwaggerDefaultValues.cs @@ -1,10 +1,10 @@ namespace ServiceName.API.Extensions.Swagger { + using System.Linq; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; - using System.Linq; /// /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter. diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs index ea1e41a..d4bd87d 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs @@ -9,8 +9,8 @@ public static class SwaggerExtensions { public static void AddSwaggerSupport(this IServiceCollection services) { - services.AddTransient, ConfigureSwaggerOptions>(); - + services.AddTransient, ConfigureSwaggerOptions>(); + services.AddSwaggerGen(options => { options.OperationFilter(); @@ -44,14 +44,14 @@ public static void AddSwaggerSupport(this IServiceCollection services) options.AddSecurityRequirement(securityReq); }); } - + public static void ConfigureSwaggerUI(this WebApplication app) { app.UseSwagger(); app.UseSwaggerUI(options => { options.RoutePrefix = string.Empty; - + var descriptions = app.DescribeApiVersions(); // build a swagger endpoint for each discovered API version @@ -64,4 +64,4 @@ public static void ConfigureSwaggerUI(this WebApplication app) }); } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/VersionExtension.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/VersionExtension.cs index 3ceea13..8badf2e 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/VersionExtension.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/VersionExtension.cs @@ -3,7 +3,7 @@ public static class VersionExtension { public static void AddApiVersioningSupport(this IServiceCollection services) - { + { services.AddApiVersioning() .AddApiExplorer( options => @@ -18,4 +18,4 @@ public static void AddApiVersioningSupport(this IServiceCollection services) }); } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/Program.cs b/MicroserviceTemplate/src/ServiceName.API/Program.cs index 4bee51c..f20d878 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Program.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Program.cs @@ -47,5 +47,4 @@ app.UseHttpsRedirection(); -app.Run(); - +app.Run(); \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/Properties/launchSettings.json b/MicroserviceTemplate/src/ServiceName.API/Properties/launchSettings.json index 513c5c3..c378fd4 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Properties/launchSettings.json +++ b/MicroserviceTemplate/src/ServiceName.API/Properties/launchSettings.json @@ -1,18 +1,18 @@ { - "profiles": { - "ServiceName.API": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:62060;http://localhost:62061" - }, - "Mock Lambda Test Tool": { - "commandName": "Executable", - "commandLineArgs": "--port 5050", - "workingDirectory": ".\\bin\\$(Configuration)\\net6.0", - "executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe" + "profiles": { + "ServiceName.API": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:62060;http://localhost:62061" + }, + "Mock Lambda Test Tool": { + "commandName": "Executable", + "commandLineArgs": "--port 5050", + "workingDirectory": ".\\bin\\$(Configuration)\\net6.0", + "executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe" + } } - } } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json index 61043f5..19cb35b 100644 --- a/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json +++ b/MicroserviceTemplate/src/ServiceName.API/appsettings.Development.json @@ -1,56 +1,56 @@ { - "Logging": { - "Sink": "CloudWatchLogs", - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*", - "ModuleConfiguration": { - "IsSwaggerUIEnabled": true, - "Infrastructure": { - "Kms": { - "AccessKey": "test", - "SecretKey": "test", - "RegionEndpoint": "eu-west-2", - "LocalTestEndpoint": "http://localhost:52002", - "SigningKeyId": "51c88cf9-e753-44ad-b9cf-00c1c11b6727", - "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxu5nBIn8lmIOiH1DUmQHKbv9Ss6KB\u002BtJDivCrKPh0n3os2SODkmdzR/dnwZdZUYMU21sxrLWTe3AoPzMHZ/sm2CGnDTYugo6X893Y8AJGbQWzLhR4BDQpEhMLTdBPMz3F1z6GqgYXth5k5LHBcg8avejIjb63smJZoqcuhEgRNx9sjJBvVBTagYRJYoiAK3esod1BtTewlZf1LZITLVec4DlIk2tWtVMus9JC\u002BehCYz1rXwL0hMT0subZrXhNsTPjN1lAT\u002BX3\u002BvwhScpvNcCI89qZx2WJbEA4okoKpxKsK0lkUfPeqVjUMkbD/xXy0pIPgqAjiN/EEwO4VTfH7CB9wIDAQAB" - }, - "CloudWatchLogs": { - "AccessKey": "test", - "SecretKey": "test", - "RegionEndpoint": "ap-southeast-2", - "LocalTestEndpoint": "http://host.docker.internal:4566", - "LogGroupName": "/LocalStack/Microservice/Logs" - }, - "DynamoDb": { - "AccessKey": "test", - "SecretKey": "test", - "RegionEndpoint": "ap-southeast-2", - "LocalTestEndpoint": "http://host.docker.internal:8000", - "TableName": "ServiceName_Setting" - }, - "Seq": { - "ServerUrl": "http://localhost:5341", - "ApiKey": "" - }, - "SqlServer": { - "Server": "host.docker.internal", - "Port": "1433", - "Database": "DBName", - "Username": "sa", - "Password": "M3rz0ug4!!!!" - }, - "Redis": { - "Server": "host.docker.internal", - "Port": "6379" - } + "Logging": { + "Sink": "CloudWatchLogs", + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } }, - "Jwt": { - "Issuer": "issuer", - "Audience": "audience" + "AllowedHosts": "*", + "ModuleConfiguration": { + "IsSwaggerUIEnabled": true, + "Infrastructure": { + "Kms": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "eu-west-2", + "LocalTestEndpoint": "http://localhost:52002", + "SigningKeyId": "51c88cf9-e753-44ad-b9cf-00c1c11b6727", + "PublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxu5nBIn8lmIOiH1DUmQHKbv9Ss6KB\u002BtJDivCrKPh0n3os2SODkmdzR/dnwZdZUYMU21sxrLWTe3AoPzMHZ/sm2CGnDTYugo6X893Y8AJGbQWzLhR4BDQpEhMLTdBPMz3F1z6GqgYXth5k5LHBcg8avejIjb63smJZoqcuhEgRNx9sjJBvVBTagYRJYoiAK3esod1BtTewlZf1LZITLVec4DlIk2tWtVMus9JC\u002BehCYz1rXwL0hMT0subZrXhNsTPjN1lAT\u002BX3\u002BvwhScpvNcCI89qZx2WJbEA4okoKpxKsK0lkUfPeqVjUMkbD/xXy0pIPgqAjiN/EEwO4VTfH7CB9wIDAQAB" + }, + "CloudWatchLogs": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "LocalTestEndpoint": "http://host.docker.internal:4566", + "LogGroupName": "/LocalStack/Microservice/Logs" + }, + "DynamoDb": { + "AccessKey": "test", + "SecretKey": "test", + "RegionEndpoint": "ap-southeast-2", + "LocalTestEndpoint": "http://host.docker.internal:8000", + "TableName": "ServiceName_Setting" + }, + "Seq": { + "ServerUrl": "http://localhost:5341", + "ApiKey": "" + }, + "SqlServer": { + "Server": "host.docker.internal", + "Port": "1433", + "Database": "DBName", + "Username": "sa", + "Password": "M3rz0ug4!!!!" + }, + "Redis": { + "Server": "host.docker.internal", + "Port": "6379" + } + }, + "Jwt": { + "Issuer": "issuer", + "Audience": "audience" + } } - } } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/aws-lambda-tools-defaults.json b/MicroserviceTemplate/src/ServiceName.API/aws-lambda-tools-defaults.json index 950200c..73f3049 100644 --- a/MicroserviceTemplate/src/ServiceName.API/aws-lambda-tools-defaults.json +++ b/MicroserviceTemplate/src/ServiceName.API/aws-lambda-tools-defaults.json @@ -1,14 +1,14 @@ { - "Information": [ - "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", - "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", - "dotnet lambda help", - "All the command line options for the Lambda command can be specified in this file." - ], - "profile": "", - "region": "", - "configuration": "Release", - "s3-prefix": "MinimalApi/", - "template": "serverless.template", - "template-parameters": "" + "Information": [ + "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", + "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", + "dotnet lambda help", + "All the command line options for the Lambda command can be specified in this file." + ], + "profile": "", + "region": "", + "configuration": "Release", + "s3-prefix": "MinimalApi/", + "template": "serverless.template", + "template-parameters": "" } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs index 59dd138..4b4a46c 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Commands/SaveSettingsCommand.cs @@ -15,9 +15,9 @@ public record SaveSettingsCommandRequest : IRequest public class CreateTodoListCommandHandler : IRequestHandler { - IRepositoryService _settingsRepository; - IConfiguration _configuration; - IDistributedCache _cache; + private IRepositoryService _settingsRepository; + private IConfiguration _configuration; + private IDistributedCache _cache; public CreateTodoListCommandHandler(IRepositoryService settingsRepository, IConfiguration configuration, IDistributedCache cache) { @@ -33,4 +33,4 @@ public async Task Handle(SaveSettingsCommandRequest request, CancellationT return result; } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs index e1b5cda..66de7cc 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/CQRS/Queries/GetSettingsQuery.cs @@ -11,11 +11,12 @@ public record GetSettingsQueryRequest : IRequest { public Guid TenantId { get; set; } } + public class GetSettingsQueryHandler : IRequestHandler { - IRepositoryService _settingsRepository; - IConfiguration _configuration; - IDistributedCache _cache; + private IRepositoryService _settingsRepository; + private IConfiguration _configuration; + private IDistributedCache _cache; public GetSettingsQueryHandler(IRepositoryService settingsRepository, IConfiguration configuration, IDistributedCache cache) { @@ -27,9 +28,9 @@ public GetSettingsQueryHandler(IRepositoryService settingsRepository, public async Task Handle(GetSettingsQueryRequest request, CancellationToken cancellationToken) { Settings cachedSettings; - + var cachedSettingsJson = await _cache.GetStringAsync(request.TenantId.ToString()); - + if (string.IsNullOrEmpty(cachedSettingsJson)) { var settings = await _settingsRepository.GetByIdAsync(request.TenantId); @@ -40,8 +41,8 @@ public async Task Handle(GetSettingsQueryRequest request, Cancellation { cachedSettings = JsonSerializer.Deserialize(cachedSettingsJson); } - + return cachedSettings; } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs index 74b4715..74396d7 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Behaviours/LoggingBehaviour.cs @@ -7,7 +7,7 @@ namespace ServiceName.Core.Common.Behaviours { public class LoggingBehaviour : IPipelineBehavior where TRequest : IRequest { - readonly ILogger _logger; + private readonly ILogger _logger; private readonly Stopwatch _timer; public LoggingBehaviour(ILogger logger) @@ -21,7 +21,7 @@ public async Task Handle(TRequest request, CancellationToken cancella var correlationId = Guid.NewGuid(); var requestName = typeof(TRequest).Name; - _logger.Information(@"correlationId {correlationId} + _logger.Information(@"correlationId {correlationId} requestName {requestName} type {requestType} payload {requestPayload}", correlationId, requestName, "Request", JsonSerializer.Serialize(request)); @@ -38,6 +38,4 @@ public async Task Handle(TRequest request, CancellationToken cancella return response; } } - - -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IJwtAuthenticationService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IJwtAuthenticationService.cs index 7879e7f..e4de082 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IJwtAuthenticationService.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IJwtAuthenticationService.cs @@ -5,6 +5,7 @@ namespace ServiceName.Core.Common.Interfaces public interface IJwtAuthenticationService { Task GenerateTokenAsync(ModuleIdentity identity, int lifetimeSeconds, string issuer, string audience); + Task ValidateTokenAsync(string token); } } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/INotificationService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/INotificationService.cs index e75bc5b..75bdcfe 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/INotificationService.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/INotificationService.cs @@ -3,6 +3,7 @@ public interface INotificationService { void SendEmail(); + void SendSMS(); } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IRepositoryService.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IRepositoryService.cs index 740cf90..211ffe0 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IRepositoryService.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Interfaces/IRepositoryService.cs @@ -3,8 +3,11 @@ public interface IRepositoryService where T : class { Task GetByIdAsync(Guid id); + Task SaveAsync(Guid id, T obj); + Task> GetAllAsync(); + Task DeleteAsync(int id); } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Common/Security/SecurityHelper.cs b/MicroserviceTemplate/src/ServiceName.Core/Common/Security/SecurityHelper.cs index c04ac30..b3de5fe 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/Common/Security/SecurityHelper.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Common/Security/SecurityHelper.cs @@ -19,6 +19,6 @@ public static RsaSecurityKey GetRsaSecurityKey(string publicKey) }; var rsaSecurityKey = new RsaSecurityKey(rsaParameters); return rsaSecurityKey; - } + } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs index 02d58b3..a205c1f 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/ConfigureServices.cs @@ -14,4 +14,4 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection return services; } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Model/ModuleOptions.cs b/MicroserviceTemplate/src/ServiceName.Core/Model/ModuleOptions.cs index f34cbb8..53053be 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/Model/ModuleOptions.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Model/ModuleOptions.cs @@ -4,4 +4,4 @@ public class ModuleOptions { public bool IsDevelopment { get; set; } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Core/Model/SettingGroup.cs b/MicroserviceTemplate/src/ServiceName.Core/Model/SettingGroup.cs index 7b39db6..74a5265 100644 --- a/MicroserviceTemplate/src/ServiceName.Core/Model/SettingGroup.cs +++ b/MicroserviceTemplate/src/ServiceName.Core/Model/SettingGroup.cs @@ -6,6 +6,7 @@ public class SettingGroup { [JsonPropertyName("IsSettingAEnabled")] public bool IsSettingAEnabled { get; set; } + [JsonPropertyName("IsSettingBEnabled")] public bool IsSettingBEnabled { get; set; } } diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs index 2a69638..2d8cded 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/AssymetricKmsJwtService.cs @@ -1,13 +1,10 @@ using System.IdentityModel.Tokens.Jwt; -using System.Security.Cryptography; using System.Text; using System.Text.Json; using Amazon.KeyManagementService; using Amazon.KeyManagementService.Model; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; -using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.Security; using Serilog; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Common.Security; @@ -17,16 +14,16 @@ namespace ServiceName.Infrastructure.Authentication { /// - /// + /// /// aws --endpoint-url=http://localhost:52002 kms --region ap-southeast-2 create-key --key-spec RSA_2048 --key-usage SIGN_VERIFY /// aws --endpoint-url=http://localhost:52002 kms --region ap-southeast-2 list-keys /// aws --endpoint-url=http://localhost:52002 kms --region ap-southeast-2 get-public-key --key-id 6732c7ca-6ec9-4b96-9711-fd1c7d637c8e /// public class AssymetricKmsJwtService : IJwtAuthenticationService { - readonly IConfiguration _configuration; - readonly IAmazonKeyManagementService _amazonKms; - readonly ILogger _logger; + private readonly IConfiguration _configuration; + private readonly IAmazonKeyManagementService _amazonKms; + private readonly ILogger _logger; public AssymetricKmsJwtService(IConfiguration configuration, IAmazonKeyManagementService amazonKms, ILogger logger) { @@ -43,7 +40,7 @@ public async Task GenerateTokenAsync(ModuleIdentity identity, int lifeti { throw new InvalidOperationException("The amazonKmsSigningKeyId is not defined."); } - + var header = Base64UrlEncoder.Encode(JsonSerializer.Serialize(new CustomJwtHeader() { Algorithm = "RS256", @@ -107,4 +104,4 @@ public async Task ValidateTokenAsync(string token) return await Task.FromResult(true); } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/JWT/CustomJwtHeader.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/JWT/CustomJwtHeader.cs index dbc4c89..fd66a24 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/JWT/CustomJwtHeader.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/JWT/CustomJwtHeader.cs @@ -6,7 +6,8 @@ internal class CustomJwtHeader { [JsonPropertyName("kid")] public string KeyId { get; set; } + [JsonPropertyName("alg")] public string Algorithm { get; set; } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/JWT/CustomJwtPayload.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/JWT/CustomJwtPayload.cs index 8d8cc7b..5c9ebd4 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/JWT/CustomJwtPayload.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/JWT/CustomJwtPayload.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json.Serialization; -using System.Threading.Tasks; +using System.Text.Json.Serialization; namespace ServiceName.Infrastructure.Authentication.JWT { @@ -11,17 +6,23 @@ internal class CustomJwtPayload { [JsonPropertyName("exp")] public long ExpirationTime { get; set; } + [JsonPropertyName("iat")] public long IssuedAt { get; set; } + [JsonPropertyName("iss")] public string Issuer { get; set; } + [JsonPropertyName("aud")] public string Audience { get; set; } + [JsonPropertyName("sub")] public string Subject { get; set; } + [JsonPropertyName("custom:tenantId")] public string TenantId { get; set; } - [JsonPropertyName("custom:username")] + + [JsonPropertyName("custom:username")] public string Username { get; set; } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/SymmetricJwtAuthenticationService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/SymmetricJwtAuthenticationService.cs index e84649f..d52bc9a 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/SymmetricJwtAuthenticationService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Authentication/SymmetricJwtAuthenticationService.cs @@ -33,7 +33,7 @@ public Task GenerateTokenAsync(ModuleIdentity identity, int lifetimeSeco var token = tokenHandler.CreateToken(tokenDescriptor); return Task.FromResult(tokenHandler.WriteToken(token)); } - + public async Task ValidateTokenAsync(string token) { var mySecret = "asdv234235^&%&^%&^hjsdfb2%%%"; @@ -60,4 +60,4 @@ public async Task ValidateTokenAsync(string token) return await Task.FromResult(true); } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs index 648f30b..b8769ca 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/ConfigureServices.cs @@ -19,12 +19,12 @@ namespace ServiceName.Infrastructure { public static class ConfigureServices { - static ConfigurationManager _configurationManager; - + private static ConfigurationManager _configurationManager; + public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, ConfigurationManager configurationManager) { _configurationManager = configurationManager; - + services.AddSingleton(GetConfiguration(configurationManager)); services.AddSingleton(GetLogger()); services.AddSingleton(GetRedisCache()); @@ -42,7 +42,7 @@ private static IAmazonKeyManagementService GetAmazonKms() var secretKey = _configurationManager["ModuleConfiguration:Infrastructure:Kms:SecretKey"]; var regionEndpoint = RegionEndpoint.GetBySystemName(_configurationManager["ModuleConfiguration:Infrastructure:Kms:RegionEndpoint"]); var localTestEndpoint = _configurationManager["ModuleConfiguration:Infrastructure:Kms:LocalTestEndpoint"]; - + AmazonKeyManagementServiceConfig amazonKeyManagementServiceConfig = new() { RegionEndpoint = regionEndpoint, @@ -68,7 +68,7 @@ private static IDistributedCache GetRedisCache() var redisPort = _configurationManager["ModuleConfiguration:Infrastructure:Redis:Port"]; var redisConnectionString = $"{redisServerName}:{redisPort}"; - + var cache = new RedisCache(new RedisCacheOptions { Configuration = redisConnectionString @@ -77,16 +77,14 @@ private static IDistributedCache GetRedisCache() return cache; } - private static ILogger GetLogger() { - var loggingSink = _configurationManager["Logging:Sink"]; switch (loggingSink) { case "Seq": - + var serverUrl = _configurationManager["ModuleConfiguration:Infrastructure:Seq:ServerUrl"]; var apiKey = _configurationManager["ModuleConfiguration:Infrastructure:Seq:ApiKey"]; @@ -97,7 +95,7 @@ private static ILogger GetLogger() .CreateLogger(); case "CloudWatchLogs": - + var accessKey = _configurationManager["ModuleConfiguration:Infrastructure:CloudWatchLogs:AccessKey"]; var secretKey = _configurationManager["ModuleConfiguration:Infrastructure:CloudWatchLogs:SecretKey"]; var regionEndpoint = _configurationManager["ModuleConfiguration:Infrastructure:CloudWatchLogs:RegionEndpoint"]; @@ -129,11 +127,10 @@ private static ILogger GetLogger() return new LoggerConfiguration().WriteTo.AWSSeriLog() .WriteTo.Console() .CreateLogger(); - + default: throw new Exception("Logger Sink is not supported."); - - } + } } private static IConfiguration GetConfiguration(ConfigurationManager configurationManager) @@ -144,7 +141,7 @@ private static IConfiguration GetConfiguration(ConfigurationManager configuratio .AddJsonFile($"appsettings.Development.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables() .Build(); - + return configurationManager; } @@ -156,7 +153,7 @@ private static DynamoDBContext GetDynamoDBContext() var localTestEndpoint = _configurationManager["ModuleConfiguration:Infrastructure:DynamoDb:LocalTestEndpoint"]; var dynamoDBContextConfig = new DynamoDBContextConfig() { ConsistentRead = true }; - + AmazonDynamoDBConfig amazonDynamoDBConfig = new() { RegionEndpoint = regionEndpoint @@ -178,4 +175,4 @@ private static DynamoDBContext GetDynamoDBContext() return new DynamoDBContext(amazonDynamoDBClientWithoutCredentials, dynamoDBContextConfig); } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDBModel/SettingDbRecord.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDBModel/SettingDbRecord.cs index 80903a9..865d98e 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDBModel/SettingDbRecord.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/DynamoDBModel/SettingDbRecord.cs @@ -10,9 +10,10 @@ public string TenantId { get; set; } + public string Settings { get; set; } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs index 3a3d4ff..31e3985 100644 --- a/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs +++ b/MicroserviceTemplate/src/ServiceName.Infrastructure/Repositories/SettingsRepositoryService.cs @@ -1,6 +1,5 @@ using System.Text.Json; using Amazon.DynamoDBv2.DataModel; -using Microsoft.Extensions.Configuration; using Serilog; using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; @@ -11,14 +10,14 @@ namespace ServiceName.Infrastructure.Repositories /// //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.DotNet.html //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetItemCRUD.html - //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DotNetSDKHighLevel.html + //https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DotNetSDKHighLevel.html //aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST //aws dynamodb list-tables --endpoint-url http://localhost:8000 /// public class SettingsRepositoryService : IRepositoryService { - readonly IDynamoDBContext _dynamoDBContext; - readonly ILogger _logger; + private readonly IDynamoDBContext _dynamoDBContext; + private readonly ILogger _logger; public SettingsRepositoryService(IDynamoDBContext dynamoContext, ILogger logger) { @@ -88,4 +87,4 @@ private async Task SaveNewSettings(Guid id, Settings obj) return settingDbRecord; } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs b/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs index 549d22a..534fd56 100644 --- a/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs +++ b/MicroserviceTemplate/tests/ServiceName.Test/Helpers/TestHelper.cs @@ -15,20 +15,22 @@ public static SettingDbRecord GetDynamoDBRecord(string tenantId) TenantId = "53a13ec4-fde8-4087-8e2a-5fb6b1fbc062", Settings = @"{""CategoryA"":{""IsSettingAEnabled"":true,""IsSettingBEnabled"":true}}" }; + case "e2c92679-5309-438b-8efc-64054a7babc2": return new SettingDbRecord() { TenantId = "e2c92679-5309-438b-8efc-64054a7babc2", Settings = String.Empty - }; + }; + default: return null; - } + } } internal static IConfigurationRoot GetConfigurationMock() { - var configDictionary = new Dictionary + var configDictionary = new Dictionary { {"ModuleConfiguration:Infrastructure:Kms:AccessKey", "test"}, {"ModuleConfiguration:Infrastructure:Kms:SecretKey", "test"}, @@ -40,8 +42,6 @@ internal static IConfigurationRoot GetConfigurationMock() {"ModuleConfiguration:Jwt:Audience", "Audience"}, }; - - var configuration = new ConfigurationBuilder() .AddInMemoryCollection(configDictionary) .Build(); @@ -49,4 +49,4 @@ internal static IConfigurationRoot GetConfigurationMock() return configuration; } } -} +} \ No newline at end of file diff --git a/MicroserviceTemplate/tests/ServiceName.Test/JwtTests.cs b/MicroserviceTemplate/tests/ServiceName.Test/JwtTests.cs index 3b162c0..242fbbd 100644 --- a/MicroserviceTemplate/tests/ServiceName.Test/JwtTests.cs +++ b/MicroserviceTemplate/tests/ServiceName.Test/JwtTests.cs @@ -1,99 +1,100 @@ -using System.Text; -using System.Text.Json; -using Amazon; -using Amazon.KeyManagementService; -using Amazon.KeyManagementService.Model; -using Microsoft.Extensions.Configuration; -using Moq; -using Moq.AutoMock; -using Serilog; -using ServiceName.Core.Model; -using ServiceName.Infrastructure.Authentication; -using ServiceName.Test.Helpers; +//TODO: Make this UNIT TESTEABLE -namespace ServiceName.Test -{ - public class JwtTests - { - AssymetricKmsJwtService _assymetricKmsJwtService; - ModuleIdentity _moduleIdentity; - IConfiguration _configurationMock; +//using System.Text; +//using System.Text.Json; +//using Amazon; +//using Amazon.KeyManagementService; +//using Amazon.KeyManagementService.Model; +//using Microsoft.Extensions.Configuration; +//using Moq; +//using Moq.AutoMock; +//using Serilog; +//using ServiceName.Core.Model; +//using ServiceName.Infrastructure.Authentication; +//using ServiceName.Test.Helpers; - public JwtTests() - { - var _autoMocker = new AutoMocker(); - _configurationMock = TestHelper.GetConfigurationMock(); +//namespace ServiceName.Test +//{ +// public class JwtTests +// { +// AssymetricKmsJwtService _assymetricKmsJwtService; +// ModuleIdentity _moduleIdentity; +// IConfiguration _configurationMock; - _moduleIdentity = new() - { - UserGuid = "UserGuid", - InstanceGuid = "InstanceGuid", - UserName = "UserName" - }; +// public JwtTests() +// { +// var _autoMocker = new AutoMocker(); +// _configurationMock = TestHelper.GetConfigurationMock(); - //SignResponse mockSignResponse = JsonSerializer.Deserialize(@"{""KeyId"":""arn:aws:Kms:eu-west-2:111122223333:key/6732c7ca-6ec9-4b96-9711-fd1c7d637c8e"",""Signature"":null,""SigningAlgorithm"":{""Value"":""RSASSA_PKCS1_V1_5_SHA_256""},""ResponseMetadata"":{""RequestId"":"""",""Metadata"":{},""ChecksumAlgorithm"":0,""ChecksumValidationStatus"":0},""ContentLength"":493,""HttpStatusCode"":200}"); - //byte[] byteArray = Encoding.UTF8.GetBytes("hzxyCYR5Zse5pzb49qr9ydvusAiPkCCYlr961/orhNUYLo0oOyLeBcW6rIlaI8id7TeIHtENCOrPGc8aUXxLjsWW4KuKthPaU/1LC3lBBaEzA1gs2VpRZajzWbCCPHhwcI522dypVi4TwabMgmlRh8iPD6QOxPexvtPnibetIcBwTZx6viLdepyz1mdd9RKAQprjSvI4K9Lm84NRaUXs969qfXlfKSRUVpDpWxWQ2pDnPt847WbDQZM8AR2U3aEfVN+56gilzOSE4LAlXPqgfRmzdtJzZA3Lv3wULBS96Eq1LkPfaXovk2yzU/dQL6/T3X/azDenl5kyymDovuCmvw=="); - //mockSignResponse.Signature = new MemoryStream(byteArray); - //_autoMocker.GetMock().Setup(x => x.SignAsync(It.IsAny(), default)).ReturnsAsync(mockSignResponse); +// _moduleIdentity = new() +// { +// UserGuid = "UserGuid", +// InstanceGuid = "InstanceGuid", +// UserName = "UserName" +// }; - _autoMocker.Use(GetAmazonKms()); //This should be replaced with a SignResponse mock as response of the SignAsync method - _autoMocker.Use(_configurationMock); - _assymetricKmsJwtService = _autoMocker.CreateInstance(); - } +// //SignResponse mockSignResponse = JsonSerializer.Deserialize(@"{""KeyId"":""arn:aws:Kms:eu-west-2:111122223333:key/6732c7ca-6ec9-4b96-9711-fd1c7d637c8e"",""Signature"":null,""SigningAlgorithm"":{""Value"":""RSASSA_PKCS1_V1_5_SHA_256""},""ResponseMetadata"":{""RequestId"":"""",""Metadata"":{},""ChecksumAlgorithm"":0,""ChecksumValidationStatus"":0},""ContentLength"":493,""HttpStatusCode"":200}"); +// //byte[] byteArray = Encoding.UTF8.GetBytes("hzxyCYR5Zse5pzb49qr9ydvusAiPkCCYlr961/orhNUYLo0oOyLeBcW6rIlaI8id7TeIHtENCOrPGc8aUXxLjsWW4KuKthPaU/1LC3lBBaEzA1gs2VpRZajzWbCCPHhwcI522dypVi4TwabMgmlRh8iPD6QOxPexvtPnibetIcBwTZx6viLdepyz1mdd9RKAQprjSvI4K9Lm84NRaUXs969qfXlfKSRUVpDpWxWQ2pDnPt847WbDQZM8AR2U3aEfVN+56gilzOSE4LAlXPqgfRmzdtJzZA3Lv3wULBS96Eq1LkPfaXovk2yzU/dQL6/T3X/azDenl5kyymDovuCmvw=="); +// //mockSignResponse.Signature = new MemoryStream(byteArray); +// //_autoMocker.GetMock().Setup(x => x.SignAsync(It.IsAny(), default)).ReturnsAsync(mockSignResponse); - /// - /// TODO: Move to Helper class - /// - /// - private IAmazonKeyManagementService GetAmazonKms() - { - var accessKey = _configurationMock["ModuleConfiguration:Infrastructure:Kms:AccessKey"]; - var secretKey = _configurationMock["ModuleConfiguration:Infrastructure:Kms:SecretKey"]; - var regionEndpoint = RegionEndpoint.GetBySystemName(_configurationMock["ModuleConfiguration:Infrastructure:Kms:RegionEndpoint"]); - var localTestEndpoint = _configurationMock["ModuleConfiguration:Infrastructure:Kms:LocalTestEndpoint"]; +// _autoMocker.Use(GetAmazonKms()); //This should be replaced with a SignResponse mock as response of the SignAsync method +// _autoMocker.Use(_configurationMock); +// _assymetricKmsJwtService = _autoMocker.CreateInstance(); +// } - AmazonKeyManagementServiceConfig amazonKeyManagementServiceConfig = new() - { - RegionEndpoint = regionEndpoint, - }; +// /// +// /// TODO: Move to Helper class +// /// +// /// +// private IAmazonKeyManagementService GetAmazonKms() +// { +// var accessKey = _configurationMock["ModuleConfiguration:Infrastructure:Kms:AccessKey"]; +// var secretKey = _configurationMock["ModuleConfiguration:Infrastructure:Kms:SecretKey"]; +// var regionEndpoint = RegionEndpoint.GetBySystemName(_configurationMock["ModuleConfiguration:Infrastructure:Kms:RegionEndpoint"]); +// var localTestEndpoint = _configurationMock["ModuleConfiguration:Infrastructure:Kms:LocalTestEndpoint"]; - if (!string.IsNullOrEmpty(localTestEndpoint)) - { - amazonKeyManagementServiceConfig.UseHttp = true; - amazonKeyManagementServiceConfig.ServiceURL = localTestEndpoint; - } +// AmazonKeyManagementServiceConfig amazonKeyManagementServiceConfig = new() +// { +// RegionEndpoint = regionEndpoint, +// }; - if (!string.IsNullOrEmpty(accessKey) && !string.IsNullOrEmpty(secretKey)) - { - return new AmazonKeyManagementServiceClient(accessKey, secretKey, amazonKeyManagementServiceConfig); - } +// if (!string.IsNullOrEmpty(localTestEndpoint)) +// { +// amazonKeyManagementServiceConfig.UseHttp = true; +// amazonKeyManagementServiceConfig.ServiceURL = localTestEndpoint; +// } - return new AmazonKeyManagementServiceClient(amazonKeyManagementServiceConfig); - } +// if (!string.IsNullOrEmpty(accessKey) && !string.IsNullOrEmpty(secretKey)) +// { +// return new AmazonKeyManagementServiceClient(accessKey, secretKey, amazonKeyManagementServiceConfig); +// } +// return new AmazonKeyManagementServiceClient(amazonKeyManagementServiceConfig); +// } - [Fact] - public async Task JwtValidationOK() - { - var token = await _assymetricKmsJwtService.GenerateTokenAsync(_moduleIdentity, 60, "Issuer", "Audience"); - var isValid = await _assymetricKmsJwtService.ValidateTokenAsync(token); - Assert.True(isValid); - } +// [Fact] +// public async Task JwtValidationOK() +// { +// var token = await _assymetricKmsJwtService.GenerateTokenAsync(_moduleIdentity, 60, "Issuer", "Audience"); +// var isValid = await _assymetricKmsJwtService.ValidateTokenAsync(token); +// Assert.True(isValid); +// } - [Fact] - public async Task JwtWithIssuerMismatch() - { - var token = await _assymetricKmsJwtService.GenerateTokenAsync(_moduleIdentity, 60, "Issuer2", "Audience"); - var isValid = await _assymetricKmsJwtService.ValidateTokenAsync(token); - Assert.False(isValid); - } +// [Fact] +// public async Task JwtWithIssuerMismatch() +// { +// var token = await _assymetricKmsJwtService.GenerateTokenAsync(_moduleIdentity, 60, "Issuer2", "Audience"); +// var isValid = await _assymetricKmsJwtService.ValidateTokenAsync(token); +// Assert.False(isValid); +// } - [Fact] - public async Task JwtWithAudienceMismatch() - { - var token = await _assymetricKmsJwtService.GenerateTokenAsync(_moduleIdentity, 60, "Issuer", "Audience2"); - var isValid = await _assymetricKmsJwtService.ValidateTokenAsync(token); - Assert.False(isValid); - } - } -} +// [Fact] +// public async Task JwtWithAudienceMismatch() +// { +// var token = await _assymetricKmsJwtService.GenerateTokenAsync(_moduleIdentity, 60, "Issuer", "Audience2"); +// var isValid = await _assymetricKmsJwtService.ValidateTokenAsync(token); +// Assert.False(isValid); +// } +// } +//} \ No newline at end of file diff --git a/MicroserviceTemplate/tests/ServiceName.Test/RepositoryTests.cs b/MicroserviceTemplate/tests/ServiceName.Test/RepositoryTests.cs index ccb9d2d..5034cb1 100644 --- a/MicroserviceTemplate/tests/ServiceName.Test/RepositoryTests.cs +++ b/MicroserviceTemplate/tests/ServiceName.Test/RepositoryTests.cs @@ -9,22 +9,22 @@ namespace ServiceName.Test { public class RepositoryTests { - readonly AutoMocker _autoMocker; - readonly SettingsRepositoryService _mockSettingsRepository; + private readonly AutoMocker _autoMocker; + private readonly SettingsRepositoryService _mockSettingsRepository; public RepositoryTests() { _autoMocker = new AutoMocker(); _mockSettingsRepository = _autoMocker.CreateInstance(); } - + [Fact] public async Task SettingRepositoryTestWhenTenantIdExists() { var tenantId = "53a13ec4-fde8-4087-8e2a-5fb6b1fbc062"; - + _autoMocker.GetMock().Setup(x => x.LoadAsync(tenantId, default)).ReturnsAsync(TestHelper.GetDynamoDBRecord(tenantId)); - + var result = await _mockSettingsRepository.GetByIdAsync(Guid.Parse(tenantId)); Assert.NotNull(result.CategoryA); @@ -36,9 +36,9 @@ public async Task SettingRepositoryTestWhenTenantIdExists() public async Task SettingRepositoryTestWhenTenantIdDoesNotExist() { var tenantId = "296b73d9-692a-42bf-9ccb-ff41ca256722"; - + _autoMocker.GetMock().Setup(x => x.LoadAsync(tenantId, default)).ReturnsAsync(TestHelper.GetDynamoDBRecord(tenantId)); - + var result = await _mockSettingsRepository.GetByIdAsync(Guid.Parse(tenantId)); Assert.NotNull(result.CategoryA); @@ -46,12 +46,11 @@ public async Task SettingRepositoryTestWhenTenantIdDoesNotExist() Assert.False(result.CategoryA.IsSettingBEnabled); } - [Fact] public async Task SettingRepositoryTestWhenSettingsAreEmpty() { var tenantId = "e2c92679-5309-438b-8efc-64054a7babc2"; - + _autoMocker.GetMock().Setup(x => x.LoadAsync(tenantId, default)).ReturnsAsync(TestHelper.GetDynamoDBRecord(tenantId)); var result = await _mockSettingsRepository.GetByIdAsync(Guid.Parse(tenantId)); @@ -59,6 +58,6 @@ public async Task SettingRepositoryTestWhenSettingsAreEmpty() Assert.NotNull(result.CategoryA); Assert.False(result.CategoryA.IsSettingAEnabled); Assert.False(result.CategoryA.IsSettingBEnabled); - } + } } } \ No newline at end of file diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs b/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs index 030b5d9..335bdd9 100644 --- a/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs @@ -1,7 +1,6 @@ using System.Text.Json; using CmdRunner; using CmdRunner.Model; -using Microsoft.Extensions.Configuration; var configuration = new ConfigurationBuilder() .SetBasePath(Environment.CurrentDirectory) From 7a386fd46d2a77f5a4f614c8c384d4800e63cf64 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Thu, 23 Jun 2022 15:48:44 +1000 Subject: [PATCH 27/56] [MicroservicesTemplate] Fixed version.json files --- MicroserviceTemplate/src/Authentication.API/version.json | 3 ++- MicroserviceTemplate/src/ServiceName.API/version.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/MicroserviceTemplate/src/Authentication.API/version.json b/MicroserviceTemplate/src/Authentication.API/version.json index 90fa5e8..509976e 100644 --- a/MicroserviceTemplate/src/Authentication.API/version.json +++ b/MicroserviceTemplate/src/Authentication.API/version.json @@ -1,3 +1,4 @@ { - "version": "1.0" + "version": "1.0", + "pathFilters": [ "." ] } \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/version.json b/MicroserviceTemplate/src/ServiceName.API/version.json index 90fa5e8..509976e 100644 --- a/MicroserviceTemplate/src/ServiceName.API/version.json +++ b/MicroserviceTemplate/src/ServiceName.API/version.json @@ -1,3 +1,4 @@ { - "version": "1.0" + "version": "1.0", + "pathFilters": [ "." ] } \ No newline at end of file From 27d0a433c45c1efdb5b89a6920e9b5b6ee386568 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Fri, 24 Jun 2022 10:17:34 +1000 Subject: [PATCH 28/56] [MicroserviceTemplate] Build improvements (Pack step) Removed references to unused packages Upgraded packages version --- MicroserviceTemplate/aws/Cdk.csproj | 3 +- MicroserviceTemplate/build/Build.cs | 64 ++++++++++--------- MicroserviceTemplate/build/Build.csproj | 1 - MicroserviceTemplate/build/appsettings.json | 3 +- .../ServiceName.Core/ServiceName.Core.csproj | 4 +- .../ServiceName.Infrastructure.csproj | 9 ++- .../ServiceName.Test/ServiceName.Test.csproj | 1 - .../EnvironmentInitializer.csproj | 2 - .../tools/EnvironmentInitializer/Program.cs | 1 + 9 files changed, 43 insertions(+), 45 deletions(-) diff --git a/MicroserviceTemplate/aws/Cdk.csproj b/MicroserviceTemplate/aws/Cdk.csproj index 338b323..c59c65f 100644 --- a/MicroserviceTemplate/aws/Cdk.csproj +++ b/MicroserviceTemplate/aws/Cdk.csproj @@ -9,8 +9,7 @@ - - + + true + + true + + + + + + + Always + + + \ No newline at end of file diff --git a/MicroserviceTemplate/src/Mock.API/Model/MockFile.cs b/MicroserviceTemplate/src/Mock.API/Model/MockFile.cs new file mode 100644 index 0000000..ad24ede --- /dev/null +++ b/MicroserviceTemplate/src/Mock.API/Model/MockFile.cs @@ -0,0 +1,7 @@ +namespace Mock.API.Model +{ + public class MockFile + { + public List Resources { get; set; } + } +} diff --git a/MicroserviceTemplate/src/Mock.API/Model/ResourceMock.cs b/MicroserviceTemplate/src/Mock.API/Model/ResourceMock.cs new file mode 100644 index 0000000..4010b61 --- /dev/null +++ b/MicroserviceTemplate/src/Mock.API/Model/ResourceMock.cs @@ -0,0 +1,8 @@ +namespace Mock.API.Model +{ + public class ResourceMock + { + public string Id { get; set; } + public string Value { get; set; } + } +} diff --git a/MicroserviceTemplate/src/Mock.API/Program.cs b/MicroserviceTemplate/src/Mock.API/Program.cs new file mode 100644 index 0000000..221dfa8 --- /dev/null +++ b/MicroserviceTemplate/src/Mock.API/Program.cs @@ -0,0 +1,39 @@ +using System.Text.Json; +using Microsoft.AspNetCore.Mvc; +using Mock.API.Model; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddControllers(); + +// Add AWS Lambda support. When application is run in Lambda Kestrel is swapped out as the web server with Amazon.Lambda.AspNetCoreServer. This +// package will act as the webserver translating request and responses between the Lambda event source and ASP.NET Core. +builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi); + +var app = builder.Build(); + + +app.UseHttpsRedirection(); +app.UseAuthorization(); +app.MapControllers(); + +app.MapGet("/resources", () => GetMocks()); +app.MapGet("/resources/{id}", (string id) => GetMock(id)); + +app.Run(); + + +ResourceMock? GetMock(string id) +{ + var mockJson = File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), "Files", "Mock.json")); + var mockFile = JsonSerializer.Deserialize(mockJson); + return mockFile?.Resources?.FirstOrDefault(m => m.Id.Equals(id)); +} + +List GetMocks() +{ + var mockJson = File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), "Files", "Mock.json")); + var mockFile = JsonSerializer.Deserialize(mockJson); + return mockFile?.Resources!; +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Mock.API/Properties/launchSettings.json b/MicroserviceTemplate/src/Mock.API/Properties/launchSettings.json new file mode 100644 index 0000000..cb1dce3 --- /dev/null +++ b/MicroserviceTemplate/src/Mock.API/Properties/launchSettings.json @@ -0,0 +1,18 @@ +{ + "profiles": { + "Mock.API": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:64916;http://localhost:64917" + }, + "Mock Lambda Test Tool": { + "commandName": "Executable", + "commandLineArgs": "--port 5050", + "workingDirectory": ".\\bin\\$(Configuration)\\net6.0", + "executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe" + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Mock.API/Readme.md b/MicroserviceTemplate/src/Mock.API/Readme.md new file mode 100644 index 0000000..9e1d3e0 --- /dev/null +++ b/MicroserviceTemplate/src/Mock.API/Readme.md @@ -0,0 +1,51 @@ +# ASP.NET Core Minimal API Serverless Application + +This project shows how to run an ASP.NET Core Web API project as an AWS Lambda exposed through Amazon API Gateway. The NuGet package [Amazon.Lambda.AspNetCoreServer](https://www.nuget.org/packages/Amazon.Lambda.AspNetCoreServer) contains a Lambda function that is used to translate requests from API Gateway into the ASP.NET Core framework and then the responses from ASP.NET Core back to API Gateway. + + +For more information about how the Amazon.Lambda.AspNetCoreServer package works and how to extend its behavior view its [README](https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.AspNetCoreServer/README.md) file in GitHub. + +## Executable Assembly ## + +.NET Lambda projects that use C# top level statements like this project must be deployed as an executable assembly instead of a class library. To indicate to Lambda that the .NET function is an executable assembly the +Lambda function handler value is set to the .NET Assembly name. This is different then deploying as a class library where the function handler string includes the assembly, type and method name. + +To deploy as an executable assembly the Lambda runtime client must be started to listen for incoming events to process. For an ASP.NET Core application the Lambda runtime client is started by included the +`Amazon.Lambda.AspNetCoreServer.Hosting` NuGet package and calling `AddAWSLambdaHosting(LambdaEventSource.HttpApi)` passing in the event source while configuring the services of the application. The +event source can be API Gateway REST API and HTTP API or Application Load Balancer. + +### Project Files ### + +* serverless.template - an AWS CloudFormation Serverless Application Model template file for declaring your Serverless functions and other AWS resources +* aws-lambda-tools-defaults.json - default argument settings for use with Visual Studio and command line deployment tools for AWS +* Program.cs - entry point to the application that contains all of the top level statements initializing the ASP.NET Core application. +The call to `AddAWSLambdaHosting` configures the application to work in Lambda when it detects Lambda is the executing environment. +* Controllers\CalculatorController - example Web API controller + +You may also have a test project depending on the options selected. + +## Here are some steps to follow from Visual Studio: + +To deploy your Serverless application, right click the project in Solution Explorer and select *Publish to AWS Lambda*. + +To view your deployed application open the Stack View window by double-clicking the stack name shown beneath the AWS CloudFormation node in the AWS Explorer tree. The Stack View also displays the root URL to your published application. + +## Here are some steps to follow to get started from the command line: + +Once you have edited your template and code you can deploy your application using the [Amazon.Lambda.Tools Global Tool](https://github.com/aws/aws-extensions-for-dotnet-cli#aws-lambda-amazonlambdatools) from the command line. + +Install Amazon.Lambda.Tools Global Tools if not already installed. +``` + dotnet tool install -g Amazon.Lambda.Tools +``` + +If already installed check if new version is available. +``` + dotnet tool update -g Amazon.Lambda.Tools +``` + +Deploy application +``` + cd "Mock.API/src/Mock.API" + dotnet lambda deploy-serverless +``` diff --git a/MicroserviceTemplate/src/Mock.API/appsettings.Development.json b/MicroserviceTemplate/src/Mock.API/appsettings.Development.json new file mode 100644 index 0000000..1b2d3ba --- /dev/null +++ b/MicroserviceTemplate/src/Mock.API/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Mock.API/appsettings.json b/MicroserviceTemplate/src/Mock.API/appsettings.json new file mode 100644 index 0000000..ec04bc1 --- /dev/null +++ b/MicroserviceTemplate/src/Mock.API/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Mock.API/aws-lambda-tools-defaults.json b/MicroserviceTemplate/src/Mock.API/aws-lambda-tools-defaults.json new file mode 100644 index 0000000..a13d212 --- /dev/null +++ b/MicroserviceTemplate/src/Mock.API/aws-lambda-tools-defaults.json @@ -0,0 +1,14 @@ +{ + "Information": [ + "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", + "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", + "dotnet lambda help", + "All the command line options for the Lambda command can be specified in this file." + ], + "profile": "", + "region": "", + "configuration": "Release", + "s3-prefix": "Mock.API/", + "template": "serverless.template", + "template-parameters": "" +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Mock.API/serverless.template b/MicroserviceTemplate/src/Mock.API/serverless.template new file mode 100644 index 0000000..a967b14 --- /dev/null +++ b/MicroserviceTemplate/src/Mock.API/serverless.template @@ -0,0 +1,47 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Transform": "AWS::Serverless-2016-10-31", + "Description": "An AWS Serverless Application that uses the ASP.NET Core framework running in Amazon Lambda.", + "Parameters": {}, + "Conditions": {}, + "Resources": { + "AspNetCoreFunction": { + "Type": "AWS::Serverless::Function", + "Properties": { + "Handler": "Mock.API", + "Runtime": "dotnet6", + "CodeUri": "", + "MemorySize": 256, + "Timeout": 30, + "Role": null, + "Policies": [ + "AWSLambda_FullAccess" + ], + "Events": { + "ProxyResource": { + "Type": "Api", + "Properties": { + "Path": "/{proxy+}", + "Method": "ANY" + } + }, + "RootResource": { + "Type": "Api", + "Properties": { + "Path": "/", + "Method": "ANY" + } + } + } + } + } + }, + "Outputs": { + "ApiURL": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthExtension.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthenticationExtension.cs similarity index 96% rename from MicroserviceTemplate/src/ServiceName.API/Extensions/AuthExtension.cs rename to MicroserviceTemplate/src/ServiceName.API/Extensions/AuthenticationExtension.cs index e73ccac..090b464 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthExtension.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthenticationExtension.cs @@ -4,7 +4,7 @@ namespace ServiceName.API.Extensions { - public static class SecurityExtension + public static class AuthenticationExtension { public static void AddAuthSupport(this IServiceCollection services, ConfigurationManager configurationManager) { diff --git a/MicroserviceTemplate/src/ServiceName.API/Properties/launchSettings.json b/MicroserviceTemplate/src/ServiceName.API/Properties/launchSettings.json index c378fd4..513c5c3 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Properties/launchSettings.json +++ b/MicroserviceTemplate/src/ServiceName.API/Properties/launchSettings.json @@ -1,18 +1,18 @@ { - "profiles": { - "ServiceName.API": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:62060;http://localhost:62061" - }, - "Mock Lambda Test Tool": { - "commandName": "Executable", - "commandLineArgs": "--port 5050", - "workingDirectory": ".\\bin\\$(Configuration)\\net6.0", - "executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe" - } + "profiles": { + "ServiceName.API": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:62060;http://localhost:62061" + }, + "Mock Lambda Test Tool": { + "commandName": "Executable", + "commandLineArgs": "--port 5050", + "workingDirectory": ".\\bin\\$(Configuration)\\net6.0", + "executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe" } + } } \ No newline at end of file diff --git a/MicroserviceTemplate/tye.yaml b/MicroserviceTemplate/tye.yaml index 280ae86..6518b98 100644 --- a/MicroserviceTemplate/tye.yaml +++ b/MicroserviceTemplate/tye.yaml @@ -38,6 +38,12 @@ services: - port: 51002 protocol: https #replicas: 2 + - name: mock-api + project: src/Mock.API/Mock.API.csproj + bindings: + - port: 51003 + protocol: https + #replicas: 2 - name: SqlServer image: mcr.microsoft.com/mssql/server:2019-latest From 71a738ad1e372ef92d9029729d6f4cc2dcf5fe5c Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Thu, 30 Jun 2022 10:12:28 +1000 Subject: [PATCH 35/56] [Connectors] Added BitBucket Connector --- .../BitbucketConnector.cs | 41 +++++++++++++++++++ .../IntegrationConnectors.Bitbucket.csproj | 13 ++++++ .../Model/Author.cs | 24 +++++++++++ .../Model/BitBucketPullRequests.cs | 6 +++ .../Model/BitbucketBranch.cs | 10 +++++ .../Model/BitbucketBranches.cs | 9 ++++ .../Model/Target.cs | 8 ++++ .../Model/User.cs | 7 ++++ 8 files changed, 118 insertions(+) create mode 100644 IntegrationConnectors/src/IntegrationConnectors.Bitbucket/BitbucketConnector.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.Bitbucket/IntegrationConnectors.Bitbucket.csproj create mode 100644 IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/Author.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketPullRequests.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitbucketBranch.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitbucketBranches.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/Target.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/User.cs diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/BitbucketConnector.cs b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/BitbucketConnector.cs new file mode 100644 index 0000000..26f9525 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/BitbucketConnector.cs @@ -0,0 +1,41 @@ +using System.Text.Json; +using IntegrationConnectors.Common; + +namespace IntegrationConnectors.Bitbucket +{ + public class BitbucketConnector: HttpConnector + { + public BitbucketConnector(string baseUrl, string apiKey, AuthenticationType authType) : base(baseUrl, apiKey, authType) + { + + } + + public async Task> GetBranchesAsync(string company, string repository) + { + var pageNumber = 1; + var allBranches = new List(); + + var response = await GetAsync($"{_url}/2.0/repositories/{company}/{repository}/refs/branches?page={pageNumber}&pagelen=100"); + var branches = JsonSerializer.Deserialize(response, _jsonSerializerOptions); + allBranches.AddRange(branches.Values); + + while (branches.Values.Count > 0) + { + pageNumber++; + + response = await GetAsync($"{_url}/2.0/repositories/{company}/{repository}/refs/branches?page={pageNumber}&pagelen=100"); + branches = JsonSerializer.Deserialize(response, _jsonSerializerOptions); + allBranches.AddRange(branches.Values); + } + + return allBranches.OrderByDescending(b => b.Target.Date).ToList(); + } + + public async Task GetPullRequestsAsync(string company, string repository) + { + var response = await GetAsync($"{_url}/2.0/repositories/{company}/{repository}/pullrequests?page=1"); + var pullRequests = JsonSerializer.Deserialize(response, _jsonSerializerOptions); + return pullRequests; + } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/IntegrationConnectors.Bitbucket.csproj b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/IntegrationConnectors.Bitbucket.csproj new file mode 100644 index 0000000..c614b1e --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/IntegrationConnectors.Bitbucket.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/Author.cs b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/Author.cs new file mode 100644 index 0000000..d884798 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/Author.cs @@ -0,0 +1,24 @@ +using System.Text.RegularExpressions; + +namespace IntegrationConnectors.Bitbucket +{ + public class Author + { + public string Raw { get; set; } + + public User User { get; set; } + + public string UserEmail + { + get + { + // Create a pattern for a word that starts with letter "M" + string pattern = @"<.*>"; + // Create a Regex + Regex rg = new Regex(pattern); + MatchCollection matchedAuthors = rg.Matches(Raw); + return matchedAuthors[0].Value; + } + } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketPullRequests.cs b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketPullRequests.cs new file mode 100644 index 0000000..fa21361 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketPullRequests.cs @@ -0,0 +1,6 @@ +namespace IntegrationConnectors.Bitbucket +{ + public class BitBucketPullRequests + { + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitbucketBranch.cs b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitbucketBranch.cs new file mode 100644 index 0000000..deec867 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitbucketBranch.cs @@ -0,0 +1,10 @@ +namespace IntegrationConnectors.Bitbucket +{ + public class BitbucketBranch + { + public string Name { get; set; } + + public Target Target { get; set; } + + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitbucketBranches.cs b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitbucketBranches.cs new file mode 100644 index 0000000..7ac82f3 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitbucketBranches.cs @@ -0,0 +1,9 @@ + + +namespace IntegrationConnectors.Bitbucket +{ + public class BitbucketBranches + { + public List Values { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/Target.cs b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/Target.cs new file mode 100644 index 0000000..ba77e55 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/Target.cs @@ -0,0 +1,8 @@ +namespace IntegrationConnectors.Bitbucket +{ + public class Target + { + public Author Author { get; set; } + public DateTime Date { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/User.cs b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/User.cs new file mode 100644 index 0000000..923c8f9 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/User.cs @@ -0,0 +1,7 @@ +namespace IntegrationConnectors.Bitbucket +{ + public class User + { + public string Uuid { get; set; } + } +} \ No newline at end of file From d58d6c6cd3a14d6f6768af03b1a8f09fb42dfb4d Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Thu, 30 Jun 2022 10:13:27 +1000 Subject: [PATCH 36/56] [CodeKata] Added Readme.md TODO --- CodeKata/CodeKata.sln | 9 +++++++-- CodeKata/Readme.md | 6 ++++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 CodeKata/Readme.md diff --git a/CodeKata/CodeKata.sln b/CodeKata/CodeKata.sln index 842f15d..2bc1e50 100644 --- a/CodeKata/CodeKata.sln +++ b/CodeKata/CodeKata.sln @@ -1,10 +1,15 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30717.126 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32505.173 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeKata.CSharp", "CSharp\CodeKata.CSharp.csproj", "{19C1553D-2D8D-4B63-8DD4-DA2A7A8EF3AD}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7D70EF4E-11A4-4C8A-93D2-EFD448BFE1F6}" + ProjectSection(SolutionItems) = preProject + Readme.md = Readme.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/CodeKata/Readme.md b/CodeKata/Readme.md new file mode 100644 index 0000000..0a7b52a --- /dev/null +++ b/CodeKata/Readme.md @@ -0,0 +1,6 @@ +Fluent API: +https://www.c-sharpcorner.com/article/creating-a-fluent-api-in-c-sharp-net/ +https://www.youtube.com/watch?v=1JAdZul-aRQ + +Object Mapping: +https://gist.github.com/leandromonaco/6d05108810efb48c0c27f7c6f8d26d44 \ No newline at end of file From e202eeb8025acacb4837ec81c3cbbdb2767c64f6 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Thu, 30 Jun 2022 11:31:26 +1000 Subject: [PATCH 37/56] [MicroserviceTemplate] Added Delay configuration to Mock.API for Circuit Breaking Testing --- MicroserviceTemplate/src/Mock.API/Program.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/MicroserviceTemplate/src/Mock.API/Program.cs b/MicroserviceTemplate/src/Mock.API/Program.cs index 221dfa8..67e2187 100644 --- a/MicroserviceTemplate/src/Mock.API/Program.cs +++ b/MicroserviceTemplate/src/Mock.API/Program.cs @@ -18,22 +18,28 @@ app.UseAuthorization(); app.MapControllers(); -app.MapGet("/resources", () => GetMocks()); -app.MapGet("/resources/{id}", (string id) => GetMock(id)); +app.MapGet("/resources", ([FromHeader] int delayInMilliseconds) => GetMocks(delayInMilliseconds)); +app.MapGet("/resources/{id}", (string id, [FromHeader] int delayInMilliseconds) => GetMock(id, delayInMilliseconds)); app.Run(); -ResourceMock? GetMock(string id) +ResourceMock? GetMock(string id, int delayInMilliseconds) { var mockJson = File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), "Files", "Mock.json")); var mockFile = JsonSerializer.Deserialize(mockJson); + + Thread.Sleep(delayInMilliseconds); + return mockFile?.Resources?.FirstOrDefault(m => m.Id.Equals(id)); } -List GetMocks() +List GetMocks(int delayInMilliseconds) { var mockJson = File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), "Files", "Mock.json")); var mockFile = JsonSerializer.Deserialize(mockJson); + + Thread.Sleep(delayInMilliseconds); + return mockFile?.Resources!; } \ No newline at end of file From db24098c579e63a702edc7f99067e058bfb7b186 Mon Sep 17 00:00:00 2001 From: Leandro Monaco Date: Wed, 13 Jul 2022 11:23:46 +1000 Subject: [PATCH 38/56] Update tye.yaml --- MicroserviceTemplate/tye.yaml | 68 ++++++++++++++--------------------- 1 file changed, 26 insertions(+), 42 deletions(-) diff --git a/MicroserviceTemplate/tye.yaml b/MicroserviceTemplate/tye.yaml index 6518b98..cb91eec 100644 --- a/MicroserviceTemplate/tye.yaml +++ b/MicroserviceTemplate/tye.yaml @@ -1,54 +1,36 @@ -# tye application configuration file -# read all about it at https://github.com/dotnet/tye -# -# when you've given us a try, we'd love to know what you think: -# https://aka.ms/AA7q20u -# - extensions: - name: seq logPath: ./.logs name: servicetemplate -#registry: exampleuser (required for Kubernetes) -#namespace: examplenamespace network: tye-network ingress: - name: Ingress bindings: - - port: 51000 + - port: 50000 protocol: https ip: '127.0.0.1' rules: - - host: servicename.domain.local - service: servicename-api - - host: authentication.domain.local - service: authentication-api + - host: notification-api.dev.timetarget.com + service: notification-api services: - - name: authentication-api - project: src/Authentication.API/Authentication.API.csproj + - name: notification-api + project: Notification/src/Notification.API/Notification.API.csproj bindings: - port: 51001 protocol: https - #replicas: 2 - - name: servicename-api - project: src/ServiceName.API/ServiceName.API.csproj - bindings: - - port: 51002 - protocol: https - #replicas: 2 + - name: mock-api - project: src/Mock.API/Mock.API.csproj + project: Notification/tests/Mock.API/Mock.API.csproj bindings: - - port: 51003 - protocol: https - #replicas: 2 + - port: 51000 + protocol: http - name: SqlServer image: mcr.microsoft.com/mssql/server:2019-latest bindings: - - connectionString: Data Source=host.docker.internal,1433;Initial Catalog=ServiceDB;Persist Security Info=True;User ID=sa;Password=${env:SA_PASSWORD} + - connectionString: Data Source=localhost,1433;Initial Catalog=ServiceDB;Persist Security Info=True;User ID=sa;Password=${env:SA_PASSWORD} port: 1433 env: - name: SA_PASSWORD @@ -61,21 +43,10 @@ services: bindings: - port: 6379 connectionString: "${host}:${port}" - #args: "redis-cli -h redis MONITOR" - # https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html (Docker Tab) - # https://docs.amazonaws.cn/en_us/amazondynamodb/latest/developerguide/DynamoDBLocal.UsageNotes.html - # aws --endpoint-url=http://localhost:8000 dynamodb list-tables - # aws --endpoint-url=http://localhost:8000 dynamodb create-table --table-name ServiceName_Setting --attribute-definitions AttributeName=TenantId,AttributeType=S --key-schema AttributeName=TenantId,KeyType=HASH --billing-mode PAY_PER_REQUEST - # -inMemory and -dbPath cannot be set at the same time - # Local DynamoDB has serious performance issues when not using -inMemory parameter - name: DynamoDB image: "amazon/dynamodb-local:latest" args: -jar DynamoDBLocal.jar -inMemory -sharedDb - #args: -jar DynamoDBLocal.jar -sharedDb -dbPath /mnt/c/Temp/DynamoDB - #volumes: - # - source: "./" - # target: "/mnt/c/Temp/DynamoDB" bindings: - port: 8000 env: @@ -89,10 +60,23 @@ services: - name: KMS image: nsmithuk/local-kms volumes: - - source: "./" - target: "/mnt/c/Temp/Kms" + - source: "C:/" + target: "/mnt/c" bindings: - port: 52002 + env: + - name: KMS_REGION + value: "ap-southeast-2" + - name: KMS_SEED_PATH + value: "/mnt/c/Dev/HF-Services/local-kms-seed.yaml" + + - name: Cognito + image: jagregory/cognito-local:latest + volumes: + - source: "C:/mnt/c/.cognito" + target: "/app/.cognito" + bindings: + - port: 9229 - name: LocalStack image: "localstack/localstack:latest" @@ -108,4 +92,4 @@ services: image: "openzipkin/zipkin" bindings: - port: 9411 - protocol: http \ No newline at end of file + protocol: http From dcae587c38de4ee8f42665c205f33ab25f440150 Mon Sep 17 00:00:00 2001 From: Leandro Monaco Date: Mon, 18 Jul 2022 17:11:13 +1000 Subject: [PATCH 39/56] Update tye.yaml --- MicroserviceTemplate/tye.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/MicroserviceTemplate/tye.yaml b/MicroserviceTemplate/tye.yaml index cb91eec..4897e5f 100644 --- a/MicroserviceTemplate/tye.yaml +++ b/MicroserviceTemplate/tye.yaml @@ -34,7 +34,7 @@ services: port: 1433 env: - name: SA_PASSWORD - value: M3rz0ug4!!!! + value: secret - name: ACCEPT_EULA value: "Y" @@ -43,6 +43,7 @@ services: bindings: - port: 6379 connectionString: "${host}:${port}" + args: "--requirepass secret" - name: DynamoDB image: "amazon/dynamodb-local:latest" @@ -73,10 +74,13 @@ services: - name: Cognito image: jagregory/cognito-local:latest volumes: - - source: "C:/mnt/c/.cognito" + - source: "C:/Dev/HF-Services/.cognito" target: "/app/.cognito" bindings: - port: 9229 + env: + - name: NODE_TLS_REJECT_UNAUTHORIZED + value: "0" - name: LocalStack image: "localstack/localstack:latest" From 349273a1911dddbc2401d44a886783743da3b4d1 Mon Sep 17 00:00:00 2001 From: Leandro Monaco Date: Mon, 18 Jul 2022 17:12:39 +1000 Subject: [PATCH 40/56] Update tye.yaml --- MicroserviceTemplate/tye.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MicroserviceTemplate/tye.yaml b/MicroserviceTemplate/tye.yaml index 4897e5f..e8c9f44 100644 --- a/MicroserviceTemplate/tye.yaml +++ b/MicroserviceTemplate/tye.yaml @@ -74,7 +74,7 @@ services: - name: Cognito image: jagregory/cognito-local:latest volumes: - - source: "C:/Dev/HF-Services/.cognito" + - source: "C:/Dev/.cognito" target: "/app/.cognito" bindings: - port: 9229 From 74867440d7fb0d85a65ef9d6d820bf2cd97dac0d Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Wed, 20 Jul 2022 13:47:19 +1000 Subject: [PATCH 41/56] [MicroserviceTemplate] Added FeatureFlag Service --- MicroserviceTemplate/All.sln | 7 + .../FeatureFlag.API/FeatureFlag.API.csproj | 15 ++ .../src/FeatureFlag.API/Program.cs | 37 +++++ .../Properties/launchSettings.json | 31 ++++ .../src/FeatureFlag.API/appsettings.json | 15 ++ .../src/FeatureFlag.API/libman.json | 5 + .../tools/EnvironmentInitializer/Helper.cs | 111 +++++++++----- .../tools/EnvironmentInitializer/Program.cs | 139 ++++++++---------- .../EnvironmentInitializer/appsettings.json | 22 +-- MicroserviceTemplate/tye.yaml | 7 +- 10 files changed, 250 insertions(+), 139 deletions(-) create mode 100644 MicroserviceTemplate/src/FeatureFlag.API/FeatureFlag.API.csproj create mode 100644 MicroserviceTemplate/src/FeatureFlag.API/Program.cs create mode 100644 MicroserviceTemplate/src/FeatureFlag.API/Properties/launchSettings.json create mode 100644 MicroserviceTemplate/src/FeatureFlag.API/appsettings.json create mode 100644 MicroserviceTemplate/src/FeatureFlag.API/libman.json diff --git a/MicroserviceTemplate/All.sln b/MicroserviceTemplate/All.sln index 8b82fcc..0e634d9 100644 --- a/MicroserviceTemplate/All.sln +++ b/MicroserviceTemplate/All.sln @@ -36,6 +36,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cdk", "aws\Cdk.csproj", "{F EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mock.API", "src\Mock.API\Mock.API.csproj", "{FBEEAEA7-7A4E-4D97-9947-C3375A1F8217}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FeatureFlag.API", "src\FeatureFlag.API\FeatureFlag.API.csproj", "{02C79D99-4EFD-45E0-AB01-37B2A43FC2E5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -78,6 +80,10 @@ Global {FBEEAEA7-7A4E-4D97-9947-C3375A1F8217}.Debug|Any CPU.Build.0 = Debug|Any CPU {FBEEAEA7-7A4E-4D97-9947-C3375A1F8217}.Release|Any CPU.ActiveCfg = Release|Any CPU {FBEEAEA7-7A4E-4D97-9947-C3375A1F8217}.Release|Any CPU.Build.0 = Release|Any CPU + {02C79D99-4EFD-45E0-AB01-37B2A43FC2E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {02C79D99-4EFD-45E0-AB01-37B2A43FC2E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {02C79D99-4EFD-45E0-AB01-37B2A43FC2E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {02C79D99-4EFD-45E0-AB01-37B2A43FC2E5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -92,6 +98,7 @@ Global {A9FA5380-D9B1-4AA1-93FB-F805E22FC9B2} = {39870B6D-6682-4B08-8208-6C05B41930EC} {F9885267-A919-4711-B9AF-9C6ADEC4DA21} = {462D80EC-4E6B-4B0F-8062-779007AB3A90} {FBEEAEA7-7A4E-4D97-9947-C3375A1F8217} = {3FD41DDF-2B43-4372-9FB1-62597803431B} + {02C79D99-4EFD-45E0-AB01-37B2A43FC2E5} = {3FD41DDF-2B43-4372-9FB1-62597803431B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C9A3A709-C3C6-4654-AC2B-A92420A60DF5} diff --git a/MicroserviceTemplate/src/FeatureFlag.API/FeatureFlag.API.csproj b/MicroserviceTemplate/src/FeatureFlag.API/FeatureFlag.API.csproj new file mode 100644 index 0000000..a559ecb --- /dev/null +++ b/MicroserviceTemplate/src/FeatureFlag.API/FeatureFlag.API.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + enable + enable + + + + + + + + + diff --git a/MicroserviceTemplate/src/FeatureFlag.API/Program.cs b/MicroserviceTemplate/src/FeatureFlag.API/Program.cs new file mode 100644 index 0000000..cdfcfc7 --- /dev/null +++ b/MicroserviceTemplate/src/FeatureFlag.API/Program.cs @@ -0,0 +1,37 @@ +using Microsoft.FeatureManagement; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +builder.Configuration.SetBasePath(Environment.CurrentDirectory) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddSystemsManager("/AppConfigApplicationId/", TimeSpan.FromMinutes(5)) + //.AddSystemsManager("/kbt546s/", TimeSpan.FromMinutes(5)) + //.AddAppConfigUsingLambdaExtension("AppConfigApplicationId", "AppConfigEnvironmentId", "AppConfigConfigurationProfileId") + .AddAppConfigUsingLambdaExtension("kbt546s", "bcre59f", "gdx1prc") + .Build(); + +builder.Services.AddFeatureManagement(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.MapGet("/feature/{featureName}", async (IFeatureManager featureManager, string featureName) => +{ + return await featureManager.IsEnabledAsync(featureName); +}); + +app.Run(); + diff --git a/MicroserviceTemplate/src/FeatureFlag.API/Properties/launchSettings.json b/MicroserviceTemplate/src/FeatureFlag.API/Properties/launchSettings.json new file mode 100644 index 0000000..72ecca6 --- /dev/null +++ b/MicroserviceTemplate/src/FeatureFlag.API/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:27281", + "sslPort": 44340 + } + }, + "profiles": { + "FeatureFlag.API": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7122;http://localhost:5171", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/MicroserviceTemplate/src/FeatureFlag.API/appsettings.json b/MicroserviceTemplate/src/FeatureFlag.API/appsettings.json new file mode 100644 index 0000000..1a91984 --- /dev/null +++ b/MicroserviceTemplate/src/FeatureFlag.API/appsettings.json @@ -0,0 +1,15 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "FeatureManagement": { + "FeatureFlags": { + "FeatureT": true, + "FeatureX": false + } + } +} diff --git a/MicroserviceTemplate/src/FeatureFlag.API/libman.json b/MicroserviceTemplate/src/FeatureFlag.API/libman.json new file mode 100644 index 0000000..ceee271 --- /dev/null +++ b/MicroserviceTemplate/src/FeatureFlag.API/libman.json @@ -0,0 +1,5 @@ +{ + "version": "1.0", + "defaultProvider": "cdnjs", + "libraries": [] +} \ No newline at end of file diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/Helper.cs b/MicroserviceTemplate/tools/EnvironmentInitializer/Helper.cs index f6dbec5..b3f0879 100644 --- a/MicroserviceTemplate/tools/EnvironmentInitializer/Helper.cs +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/Helper.cs @@ -3,62 +3,97 @@ using System.Text.Json.Nodes; using System.Xml; -namespace CmdRunner +namespace EnvironmentInitializer { - public class Helper + public static class Helper { - public static void UpdateTargetPropertyJson(string targetJson, string newKmsKeyId, string newKmsPublicKey) + public static string RunCmdCommand(string command, string? directory = null, bool waitForExit = true) { - var targetConfiguration = File.ReadAllText(targetJson); - JsonNode jsonNode = JsonSerializer.Deserialize(targetConfiguration); - jsonNode["ModuleConfiguration"]["Infrastructure"]["Kms"]["SigningKeyId"] = newKmsKeyId; - jsonNode["ModuleConfiguration"]["Infrastructure"]["Kms"]["PublicKey"] = newKmsPublicKey; - File.WriteAllText(targetJson, JsonSerializer.Serialize(jsonNode, new JsonSerializerOptions() { WriteIndented = true })); + try + { + var p = new Process(); + if (!string.IsNullOrEmpty(directory)) + { + p.StartInfo.WorkingDirectory = directory; + } + p.StartInfo.UseShellExecute = false; + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.FileName = "cmd.exe"; + p.StartInfo.Arguments = $"/C {command}"; + p.Start(); + if (waitForExit) + { + var output = p.StandardOutput.ReadToEnd(); + p.WaitForExit(); + return output; + } + + return string.Empty; + } + catch + { + return string.Empty; + } } - public static void UpdateTargetPropertyXml(string targetXml, string newKmsKeyId, string newKmsPublicKey) + public static string RunPowerShellCommand(string command, string? directory = null, bool waitForExit = true) { - var xmlDocument = new XmlDocument + try { - PreserveWhitespace = true //to preserve formatting this must be done before loading so that the whitespace doesn't get thrown away at load time - }; + var p = new Process(); + if (!string.IsNullOrEmpty(directory)) + { + p.StartInfo.WorkingDirectory = directory; + } + p.StartInfo.UseShellExecute = false; + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.FileName = "powershell.exe"; + p.StartInfo.Arguments = $" -c {command}"; + p.Start(); - xmlDocument.Load(targetXml); + if (waitForExit) + { + var output = p.StandardOutput.ReadToEnd(); + p.WaitForExit(); + return output; + } - XmlNode node = xmlDocument.SelectSingleNode("//add[@key='amazonKmsSigningKeyId']"); - if (node != null) - { - node.Attributes["value"].Value = newKmsKeyId; + return string.Empty; } - - node = xmlDocument.SelectSingleNode("//add[@key='amazonKmsPublicKey']"); - if (node != null) + catch { - node.Attributes["value"].Value = newKmsPublicKey; + return string.Empty; } - - xmlDocument.Save(targetXml); } - public static string RunCommand(string command) + public static string GetJsonPropertyValue(string jsonKey, string output) { - var p = new Process(); - p.StartInfo.UseShellExecute = false; - p.StartInfo.RedirectStandardOutput = true; - p.StartInfo.FileName = "cmd.exe"; - p.StartInfo.Arguments = $"/C {command}"; - p.Start(); - var output = p.StandardOutput.ReadToEnd(); - p.WaitForExit(); - return output; + try + { + //JsonDocumentPath package is required because SelectElement is not natively supported by .NET 6 (https://stackoverflow.com/questions/70678718/how-to-delete-and-update-based-on-a-path-in-system-text-json-net-6) + var jsonDocument = JsonDocument.Parse(output); + JsonElement? jsonElement = jsonDocument.RootElement.SelectElement($"$.{jsonKey}"); + if (jsonElement == null) + { + return string.Empty; + } + return jsonElement.Value.ToString(); + } + catch + { + return string.Empty; + } } - public static string GetJsonPropertyValue(string jsonKey, string output) + public static void KillProcess(string name) { - //JsonDocumentPath package is required because SelectElement is not natively supported by .NET 6 (https://stackoverflow.com/questions/70678718/how-to-delete-and-update-based-on-a-path-in-system-text-json-net-6) - var jsonDocument = JsonDocument.Parse(output); - JsonElement? jsonElement = jsonDocument.RootElement.SelectElement($"$.{jsonKey}"); - return jsonElement.Value.ToString(); + Process[] workers = Process.GetProcessesByName(name); + foreach (var worker in workers) + { + worker.Kill(); + worker.WaitForExit(); + worker.Dispose(); + } } } } \ No newline at end of file diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs b/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs index 2aac3e4..584e5fd 100644 --- a/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs @@ -1,105 +1,82 @@ -using System.Text.Json; -using CmdRunner; -using CmdRunner.Model; +using EnvironmentInitializer; using Microsoft.Extensions.Configuration; +var debugEnabled = args.Length > 0 && !string.IsNullOrEmpty(args[0]) && args[0].ToLower().Equals("--debug"); + +var exitEnabled = args.Length > 0 && !string.IsNullOrEmpty(args[0]) && args[0].ToLower().Equals("--exit"); + var configuration = new ConfigurationBuilder() .SetBasePath(Environment.CurrentDirectory) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile($"appsettings.Development.json", optional: true, reloadOnChange: true) .Build(); -var kmsLocalUrl = configuration["KmsLocalUrl"]; var dynamoDbLocalUrl = configuration["DynamoDbLocalUrl"]; -var targetConfigurationFiles = configuration.GetSection("TargetConfigurationFiles").GetChildren().ToList(); var dynamoDbTables = configuration.GetSection("DynamoDbTables").Get>(); - -/* - * TODO: Clean up environment to re-run Tye - * Kill Tye process - * wsl --shutdown - * docker kill $(docker ps -q) - * docker rm $(docker ps -a -q) - * docker network prune - * tye run --port 10000 --dashboard - */ - -//KMS -var sourceJson = Helper.RunCommand(@"docker container ls --filter ""name = kms*"" --format=""{{json .}}"""); -var kmsDockerState = Helper.GetJsonPropertyValue("State", sourceJson); -if (kmsDockerState.Equals("running")) +var cognitoLocalDbFolder = configuration["CognitoLocalDbFolder"]; +var cognitoContainerId = string.Empty; + +Console.WriteLine("Cleaning up docker containers..."); +Helper.KillProcess("tye"); +//TODO: Add all processes here +Helper.KillProcess("Mock.API"); +Helper.RunPowerShellCommand(@"docker kill $(docker ps -q)"); +Helper.RunPowerShellCommand(@"docker rm --force $(docker ps -a -q)"); +Helper.RunPowerShellCommand(@"docker network prune --force"); + +if (exitEnabled) { - Console.WriteLine("KMS container is running"); - - var existingKeys = Helper.RunCommand($"aws --endpoint-url={kmsLocalUrl} kms --region ap-southeast-2 list-keys"); - var existingKeysList = JsonSerializer.Deserialize(existingKeys); - var KmsKeyId = existingKeysList.Keys.FirstOrDefault()?.KeyId; - - if (KmsKeyId == null) - { - //Create new KMS Key - sourceJson = Helper.RunCommand($"aws --endpoint-url={kmsLocalUrl} kms --region ap-southeast-2 create-key --key-spec RSA_2048 --key-usage SIGN_VERIFY"); - var newKmsKeyId = Helper.GetJsonPropertyValue("KeyMetadata.KeyId", sourceJson); - KmsKeyId = newKmsKeyId; - } - - Console.WriteLine($"KeyId is {KmsKeyId}"); - - //Get the new Public Key based on the newly created Key - sourceJson = Helper.RunCommand($"aws --endpoint-url={kmsLocalUrl} kms --region ap-southeast-2 get-public-key --key-id {KmsKeyId}"); - - var KmsPublicKey = Helper.GetJsonPropertyValue("PublicKey", sourceJson); - - Console.WriteLine($"PublicKey is {KmsPublicKey}"); - - //Update the target configuration files - foreach (var filename in targetConfigurationFiles.Select(t => t.Value)) - { - Console.WriteLine($"Updating {filename}"); - var fileInfo = new FileInfo(filename); - switch (fileInfo.Extension) - { - case ".json": - Helper.UpdateTargetPropertyJson(filename, KmsKeyId, KmsPublicKey); - break; + Console.WriteLine("Environment is no longer running."); + return; +} - case ".xml": - case ".config": - Helper.UpdateTargetPropertyXml(filename, KmsKeyId, KmsPublicKey); - break; +var tyeYmlFolder = @"C:\Dev\GitHub\Workbench\MicroserviceTemplate"; - default: - break; - } - Console.WriteLine($"{filename} has been updated"); - } +if (debugEnabled) +{ + Console.WriteLine("Running Tye in debug mode... do not forget to attach the debugger!"); + Helper.RunPowerShellCommand(@"tye run --port 10000 --dashboard --debug *", tyeYmlFolder, false); } else { - Console.WriteLine("KMS container is not running"); + Console.WriteLine("Running Tye..."); + Helper.RunPowerShellCommand(@"tye run --port 10000 --dashboard", tyeYmlFolder, false); } -//DynamoDB -sourceJson = Helper.RunCommand(@"docker container ls --filter ""name = dynamodb*"" --format=""{{json .}}"""); -var dynamoDbDockerState = Helper.GetJsonPropertyValue("State", sourceJson); -if (dynamoDbDockerState.Equals("running")) +Console.WriteLine("Spining up new docker containers..."); +var sourceJson = string.Empty; +var cognitoDockerState = string.Empty; +var dynamoDbDockerState = string.Empty; +while (!cognitoDockerState.Equals("running") && + !dynamoDbDockerState.Equals("running")) { - Console.WriteLine("DynamoDB container is running"); + Thread.Sleep(60000); + sourceJson = Helper.RunCmdCommand(@"docker container ls --filter ""name = cognito*"" --format=""{{json .}}"""); + cognitoDockerState = Helper.GetJsonPropertyValue("State", sourceJson); + cognitoContainerId = Helper.GetJsonPropertyValue("ID", sourceJson); + sourceJson = Helper.RunCmdCommand(@"docker container ls --filter ""name = dynamodb*"" --format=""{{json .}}"""); + dynamoDbDockerState = Helper.GetJsonPropertyValue("State", sourceJson); +} + +Console.WriteLine("Starting local environment configuration..."); - foreach (var dynamoDbTable in dynamoDbTables) +//DynamoDB +Console.WriteLine("Configuring DynamoDB..."); +foreach (var dynamoDbTable in dynamoDbTables) +{ + if (!string.IsNullOrEmpty(dynamoDbTable.TableName)) { - if (!string.IsNullOrEmpty(dynamoDbTable.TableName)) - { - Console.WriteLine($"Creating DynamoDB table ({dynamoDbTable.TableName})"); - Helper.RunCommand($"aws --endpoint-url={dynamoDbLocalUrl} dynamodb create-table --table-name {dynamoDbTable.TableName} --attribute-definitions {dynamoDbTable.AttributeDefinitions} --key-schema {dynamoDbTable.KeySchema} --billing-mode PAY_PER_REQUEST"); - Console.WriteLine($"DynamoDB table ({dynamoDbTable.TableName}) has been created."); - } + Console.WriteLine($"Creating DynamoDB table ({dynamoDbTable.TableName})"); + Helper.RunCmdCommand($"aws --endpoint-url={dynamoDbLocalUrl} dynamodb create-table --table-name {dynamoDbTable.TableName} --attribute-definitions {dynamoDbTable.AttributeDefinitions} --key-schema {dynamoDbTable.KeySchema} --billing-mode PAY_PER_REQUEST"); + Console.WriteLine($"DynamoDB table ({dynamoDbTable.TableName}) has been created."); } } -else -{ - Console.WriteLine("DynamoDB container is not running"); -} -Console.WriteLine("Press any key to finish the application"); -Console.ReadLine(); \ No newline at end of file +//Cognito +Console.WriteLine("Configuring Cognito..."); +Directory.CreateDirectory(cognitoLocalDbFolder); +File.Copy($@"{Directory.GetCurrentDirectory()}\CognitoLocalDb\clients.json", $@"{cognitoLocalDbFolder}\clients.json", true); +File.Copy($@"{Directory.GetCurrentDirectory()}\CognitoLocalDb\user-pool-test.json", $@"{cognitoLocalDbFolder}\user-pool-test.json", true); +Helper.RunCmdCommand($@"docker container restart {cognitoContainerId}"); + +Console.WriteLine("Environment configuration is done."); \ No newline at end of file diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/appsettings.json b/MicroserviceTemplate/tools/EnvironmentInitializer/appsettings.json index 4c8dd3d..d6a9659 100644 --- a/MicroserviceTemplate/tools/EnvironmentInitializer/appsettings.json +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/appsettings.json @@ -1,26 +1,10 @@ { - "KmsLocalUrl": "http://localhost:52002", "DynamoDbLocalUrl": "http://localhost:8000", - "TargetConfigurationFiles": [ - "C:\\Dev\\GitHub\\Workbench\\MicroserviceTemplate\\src\\ServiceName.API\\appsettings.Development.json", - "C:\\Dev\\GitHub\\Workbench\\MicroserviceTemplate\\src\\Authentication.API\\appsettings.Development.json", - "C:\\Dev\\GitHub\\Workbench\\MicroserviceTemplate\\tests\\ServiceName.Test\\appsettings.test.json", - "C:\\Dev\\Web.config" - ], + "CognitoLocalDbFolder": "C:\\Dev\\GitHub\\Workbench\\MicroserviceTemplate\\.cognito\\\\db", "DynamoDbTables": [ { - "TableName": "ServiceName_Setting", - "AttributeDefinitions": "AttributeName=TenantId,AttributeType=S", - "KeySchema": "AttributeName=TenantId,KeyType=HASH" - }, - { - "TableName": "ServiceName_Setting2", - "AttributeDefinitions": "AttributeName=TenantId,AttributeType=S", - "KeySchema": "AttributeName=TenantId,KeyType=HASH" - }, - { - "TableName": "ServiceName_Setting3", - "AttributeDefinitions": "AttributeName=TenantId,AttributeType=S", + "TableName": "SystemSettings", + "AttributeDefinitions": "AttributeName=InstanceId,AttributeType=S", "KeySchema": "AttributeName=TenantId,KeyType=HASH" } ] diff --git a/MicroserviceTemplate/tye.yaml b/MicroserviceTemplate/tye.yaml index 6518b98..bbc1e96 100644 --- a/MicroserviceTemplate/tye.yaml +++ b/MicroserviceTemplate/tye.yaml @@ -44,7 +44,12 @@ services: - port: 51003 protocol: https #replicas: 2 - + - name: featureflag-api + project: src/FeatureFlag.API/FeatureFlag.API.csproj + bindings: + - port: 51004 + protocol: https + #replicas: 2 - name: SqlServer image: mcr.microsoft.com/mssql/server:2019-latest bindings: From d5a9f6b44ae3283fa46e17978d13519e9a53d4c5 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Wed, 20 Jul 2022 13:57:34 +1000 Subject: [PATCH 42/56] [MicroserviceTemplate] Fixed Tye.yaml --- MicroserviceTemplate/tye.yaml | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/MicroserviceTemplate/tye.yaml b/MicroserviceTemplate/tye.yaml index 28a5bf3..6838ff0 100644 --- a/MicroserviceTemplate/tye.yaml +++ b/MicroserviceTemplate/tye.yaml @@ -11,28 +11,38 @@ ingress: protocol: https ip: '127.0.0.1' rules: - - host: notification-api.dev.timetarget.com - service: notification-api - + - host: authentication-api.domain.com + service: authentication-api services: - - name: notification-api - project: Notification/src/Notification.API/Notification.API.csproj + + - name: authentication-api + project: src/Authentication.API/Authentication.API.csproj bindings: - port: 51001 protocol: https + #replicas: 2 + + - name: servicename-api + project: src/ServiceName.API/ServiceName.API.csproj + bindings: + - port: 51002 + protocol: https + #replicas: 2 - name: mock-api - project: Notification/tests/Mock.API/Mock.API.csproj + project: src/Mock.API/Mock.API.csproj bindings: - port: 51003 protocol: https #replicas: 2 + - name: featureflag-api project: src/FeatureFlag.API/FeatureFlag.API.csproj bindings: - port: 51004 protocol: https #replicas: 2 + - name: SqlServer image: mcr.microsoft.com/mssql/server:2019-latest bindings: From 79cd9d82466c7528d71550affab547519c90f8d5 Mon Sep 17 00:00:00 2001 From: Leandro Monaco Date: Wed, 20 Jul 2022 17:04:08 +1000 Subject: [PATCH 43/56] [MicroserviceTemplate] TODO: Get data from config file --- MicroserviceTemplate/src/FeatureFlag.API/Program.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/MicroserviceTemplate/src/FeatureFlag.API/Program.cs b/MicroserviceTemplate/src/FeatureFlag.API/Program.cs index cdfcfc7..8e8cc6e 100644 --- a/MicroserviceTemplate/src/FeatureFlag.API/Program.cs +++ b/MicroserviceTemplate/src/FeatureFlag.API/Program.cs @@ -7,13 +7,12 @@ builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); +//TODO: Get AppConfigApplicationId, AppConfigEnvironmentId and AppConfigConfigurationProfileId from configuration file builder.Configuration.SetBasePath(Environment.CurrentDirectory) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddSystemsManager("/AppConfigApplicationId/", TimeSpan.FromMinutes(5)) - //.AddSystemsManager("/kbt546s/", TimeSpan.FromMinutes(5)) - //.AddAppConfigUsingLambdaExtension("AppConfigApplicationId", "AppConfigEnvironmentId", "AppConfigConfigurationProfileId") - .AddAppConfigUsingLambdaExtension("kbt546s", "bcre59f", "gdx1prc") - .Build(); + .AddAppConfigUsingLambdaExtension("AppConfigApplicationId", "AppConfigEnvironmentId", "AppConfigConfigurationProfileId") + .Build(); builder.Services.AddFeatureManagement(); From c3debf4fa93b49981fce96f63fe5fa179b510edf Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Thu, 21 Jul 2022 10:15:14 +1000 Subject: [PATCH 44/56] [MicroserviceTemplate] Added AmazonFeatureFlags Extension to FeatureFlag.API Fixed EnvironmentInitializer Tool --- .../AmazonAppConfigSettings.cs | 30 ++ .../AmazonFeatureFlagConfigSource.cs | 20 ++ .../AmazonFeatureFlagExtension.cs | 50 ++++ .../AmazonFeatureFlagProvider.cs | 91 ++++++ .../FeatureFlag.API/FeatureFlag.API.csproj | 9 +- .../src/FeatureFlag.API/Program.cs | 22 +- .../src/FeatureFlag.API/appsettings.json | 13 +- .../src/FeatureFlag.API/libman.json | 5 - .../CognitoLocalDb/clients.json | 24 ++ .../CognitoLocalDb/user-pool-test.json | 271 ++++++++++++++++++ .../tools/EnvironmentInitializer/Program.cs | 1 + 11 files changed, 521 insertions(+), 15 deletions(-) create mode 100644 MicroserviceTemplate/src/FeatureFlag.API/AmazonAppConfigSettings.cs create mode 100644 MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagConfigSource.cs create mode 100644 MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagExtension.cs create mode 100644 MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagProvider.cs delete mode 100644 MicroserviceTemplate/src/FeatureFlag.API/libman.json create mode 100644 MicroserviceTemplate/tools/EnvironmentInitializer/CognitoLocalDb/clients.json create mode 100644 MicroserviceTemplate/tools/EnvironmentInitializer/CognitoLocalDb/user-pool-test.json diff --git a/MicroserviceTemplate/src/FeatureFlag.API/AmazonAppConfigSettings.cs b/MicroserviceTemplate/src/FeatureFlag.API/AmazonAppConfigSettings.cs new file mode 100644 index 0000000..f4e5672 --- /dev/null +++ b/MicroserviceTemplate/src/FeatureFlag.API/AmazonAppConfigSettings.cs @@ -0,0 +1,30 @@ +namespace FeatureFlag.API +{ + public class AmazonAppConfigSettings + { + /// + /// Name or Id + /// + public string ApplicationId { get; set; } = String.Empty; + + /// + /// Name or Id + /// + public string EnvironmentId { get; set; } = String.Empty; + + /// + /// Name or Id + /// + public string ConfigurationProfileId { get; set; } = String.Empty; + + /// + /// Data poll frequency, default is 10 minutes + /// + public TimeSpan? DataPollFrequency { get; set; } = TimeSpan.FromMinutes(10); + + /// + /// e.g.: FeatureManagement or MyApp:AWSFlags + /// + public string ConfigSectionNaming { get; set; } = String.Empty; + } +} diff --git a/MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagConfigSource.cs b/MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagConfigSource.cs new file mode 100644 index 0000000..2be5511 --- /dev/null +++ b/MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagConfigSource.cs @@ -0,0 +1,20 @@ +using Amazon.AppConfigData; + +namespace FeatureFlag.API +{ + public class AmazonFeatureFlagConfigSource : IConfigurationSource + { + private readonly AmazonAppConfigDataClient _client; + private readonly AmazonAppConfigSettings _cfg; + + public AmazonFeatureFlagConfigSource(AmazonAppConfigDataClient client, AmazonAppConfigSettings cfg) + { + _client = client; + _cfg = cfg; + } + public IConfigurationProvider Build(IConfigurationBuilder builder) + { + return new AmazonFeatureFlagProvider(_client, _cfg); + } + } +} diff --git a/MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagExtension.cs b/MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagExtension.cs new file mode 100644 index 0000000..b658d3c --- /dev/null +++ b/MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagExtension.cs @@ -0,0 +1,50 @@ +using Amazon.AppConfigData; + +namespace FeatureFlag.API +{ + public static class AmazonFeatureFlagExtension + { + public static IConfigurationBuilder AddAmazonFeatureFlags( + this IConfigurationBuilder builder, + AmazonAppConfigDataClient client, + AmazonAppConfigSettings config) + { + var cfg = config ?? throw new ArgumentNullException(); + if (string.IsNullOrEmpty(cfg.EnvironmentId)) + { + throw new ArgumentException("Environment id (or name) missing from Amazon AppConfig settings"); + } + if (string.IsNullOrEmpty(cfg.ApplicationId)) + { + throw new ArgumentException("Application id (or name) missing from Amazon AppConfig settings"); + } + if (string.IsNullOrEmpty(cfg.ConfigurationProfileId)) + { + throw new ArgumentException("Configuration id (or name) missing from Amazon AppConfig settings"); + } + + builder.Add(new AmazonFeatureFlagConfigSource(client, cfg)); + + return builder; + } + + + public static IConfigurationBuilder AddAmazonFeatureFlags(this IConfigurationBuilder builder, AmazonAppConfigSettings config) + { + return builder.AddAmazonFeatureFlags(new AmazonAppConfigDataClient(), config); + } + + public static IConfigurationBuilder AddAmazonFeatureFlags(this IConfigurationBuilder builder, Action configAction) + { + var settings = new AmazonAppConfigSettings(); + configAction(settings); + return builder.AddAmazonFeatureFlags(new AmazonAppConfigDataClient(), settings); + } + + public static IConfigurationBuilder AddAmazonFeatureFlags(this IConfigurationBuilder builder, AmazonAppConfigSettings config, AmazonAppConfigDataClient client) + { + return builder.AddAmazonFeatureFlags(client, config); + } + } +} + diff --git a/MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagProvider.cs b/MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagProvider.cs new file mode 100644 index 0000000..c07a734 --- /dev/null +++ b/MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagProvider.cs @@ -0,0 +1,91 @@ +using System.Text; +using System.Text.Json; +using Amazon.AppConfigData; +using Amazon.AppConfigData.Model; + +namespace FeatureFlag.API +{ + public class AmazonFeatureFlagProvider: ConfigurationProvider + { + private readonly AmazonAppConfigSettings _cfg; + private readonly StartConfigurationSessionRequest _startRequest; + private readonly AmazonAppConfigDataClient _client; + private readonly TimeSpan? _frequency; + private CancellationToken _cancellationToken = new CancellationToken(); + private Task _task; + + private string _token = string.Empty; + private readonly string _configSections = string.Empty; + + public AmazonFeatureFlagProvider(AmazonAppConfigDataClient client, AmazonAppConfigSettings cfg) + { + + _cfg = cfg; + _client = client; + _frequency = _cfg.DataPollFrequency; + if (!string.IsNullOrWhiteSpace(_cfg.ConfigSectionNaming)) + { + _configSections = _cfg.ConfigSectionNaming.Trim().Trim(':') + ':'; + } + _startRequest = new StartConfigurationSessionRequest + { + ApplicationIdentifier = _cfg.ApplicationId, + EnvironmentIdentifier = _cfg.EnvironmentId, + ConfigurationProfileIdentifier = _cfg.ConfigurationProfileId + }; + } + + public override void Load() + { + RefreshData(); + + if (_task == null && _frequency.HasValue) + { + _task = new Task(async () => + { + while (true && !_cancellationToken.IsCancellationRequested) + { + await Task.Delay(_frequency.Value); + RefreshData(); + } + }); + _task.Start(); + } + } + + internal void RefreshData() + { + if (string.IsNullOrWhiteSpace(_token)) + { + var initial = _client.StartConfigurationSessionAsync(_startRequest).ConfigureAwait(false).GetAwaiter().GetResult(); + _token = initial.InitialConfigurationToken; + } + + var latestResponse = _client.GetLatestConfigurationAsync( + new GetLatestConfigurationRequest { ConfigurationToken = _token }).ConfigureAwait(false).GetAwaiter().GetResult(); + _token = latestResponse.NextPollConfigurationToken; + + var configBytes = latestResponse.Configuration.ToArray(); + if (configBytes != null && configBytes.Length != 0) // when the length == 0 it means the config didn't change. + { + UpdateData(configBytes); + } + } + + private void UpdateData(byte[] configBytes) + { + var newConfig = Encoding.UTF8.GetString(configBytes); + var doc = JsonDocument.Parse(newConfig); + var newData = new Dictionary(); + foreach (var item in doc.RootElement.EnumerateObject()) + { + var featureName = item.Name; + var isEnabled = item.Value.GetProperty("enabled").GetBoolean().ToString(); + newData[_configSections + featureName] = isEnabled; + } + Data = newData; + OnReload(); + } + } +} + diff --git a/MicroserviceTemplate/src/FeatureFlag.API/FeatureFlag.API.csproj b/MicroserviceTemplate/src/FeatureFlag.API/FeatureFlag.API.csproj index a559ecb..01ad984 100644 --- a/MicroserviceTemplate/src/FeatureFlag.API/FeatureFlag.API.csproj +++ b/MicroserviceTemplate/src/FeatureFlag.API/FeatureFlag.API.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -8,8 +8,15 @@ + + + + Always + + + diff --git a/MicroserviceTemplate/src/FeatureFlag.API/Program.cs b/MicroserviceTemplate/src/FeatureFlag.API/Program.cs index 8e8cc6e..25603a3 100644 --- a/MicroserviceTemplate/src/FeatureFlag.API/Program.cs +++ b/MicroserviceTemplate/src/FeatureFlag.API/Program.cs @@ -1,3 +1,6 @@ +using Amazon.AppConfigData; +using Amazon.AppConfigData.Model; +using FeatureFlag.API; using Microsoft.FeatureManagement; var builder = WebApplication.CreateBuilder(args); @@ -7,14 +10,25 @@ builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); -//TODO: Get AppConfigApplicationId, AppConfigEnvironmentId and AppConfigConfigurationProfileId from configuration file +var dataPollFrequencyInMinutes = 5; + builder.Configuration.SetBasePath(Environment.CurrentDirectory) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddSystemsManager("/AppConfigApplicationId/", TimeSpan.FromMinutes(5)) - .AddAppConfigUsingLambdaExtension("AppConfigApplicationId", "AppConfigEnvironmentId", "AppConfigConfigurationProfileId") + .AddSystemsManager($"/{builder.Configuration["AwsAppConfig:ApplicationId"]}/", TimeSpan.FromMinutes(dataPollFrequencyInMinutes)) + .AddAppConfigUsingLambdaExtension(builder.Configuration["AwsAppConfig:ApplicationId"], builder.Configuration["AwsAppConfig:EnvironmentId"], builder.Configuration["AwsAppConfig:FreeformConfigurationProfileId"]) + .AddAmazonFeatureFlags(c => + { + //these can be the id and the name as well... + c.ApplicationId = builder.Configuration["AwsAppConfig:ApplicationId"]; + c.EnvironmentId = builder.Configuration["AwsAppConfig:EnvironmentId"]; + c.ConfigurationProfileId = builder.Configuration["AwsAppConfig:FeatureFlagConfigurationProfileId"]; + c.DataPollFrequency = TimeSpan.FromMinutes(dataPollFrequencyInMinutes); + c.ConfigSectionNaming = "FeatureFlags"; //this is what you should use when u add the feature management service (next line), also can be empty. + }) .Build(); -builder.Services.AddFeatureManagement(); + +builder.Services.AddFeatureManagement(builder.Configuration.GetSection("FeatureFlags")); var app = builder.Build(); diff --git a/MicroserviceTemplate/src/FeatureFlag.API/appsettings.json b/MicroserviceTemplate/src/FeatureFlag.API/appsettings.json index 1a91984..f3c399b 100644 --- a/MicroserviceTemplate/src/FeatureFlag.API/appsettings.json +++ b/MicroserviceTemplate/src/FeatureFlag.API/appsettings.json @@ -6,10 +6,13 @@ } }, "AllowedHosts": "*", - "FeatureManagement": { - "FeatureFlags": { - "FeatureT": true, - "FeatureX": false - } + "AwsAppConfig": { + "ApplicationId": "kat546s", + "EnvironmentId": "bcte59f", + "FeatureFlagConfigurationProfileId": "b97qhdo", + "FreeformConfigurationProfileId": "gdz1prc" + }, + "FeatureFlags": { + "featureA": false } } diff --git a/MicroserviceTemplate/src/FeatureFlag.API/libman.json b/MicroserviceTemplate/src/FeatureFlag.API/libman.json deleted file mode 100644 index ceee271..0000000 --- a/MicroserviceTemplate/src/FeatureFlag.API/libman.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "version": "1.0", - "defaultProvider": "cdnjs", - "libraries": [] -} \ No newline at end of file diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/CognitoLocalDb/clients.json b/MicroserviceTemplate/tools/EnvironmentInitializer/CognitoLocalDb/clients.json new file mode 100644 index 0000000..29b2761 --- /dev/null +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/CognitoLocalDb/clients.json @@ -0,0 +1,24 @@ +{ + "Clients": { + "39rt3ewwjkp3cu9xfwtut211e": { + "AllowedOAuthFlows": [ + "client_credentials" + ], + "AllowedOAuthFlowsUserPoolClient": true, + "ClientId": "39rt3ewwjkp3cu9xfwtut211e", + "ClientName": "client-test2222", + "ClientSecret": "10qmi449suk91407f6xi1pkhr", + "CreationDate": "2022-07-14T00:32:32.311Z", + "ExplicitAuthFlows": [ + "ALLOW_USER_PASSWORD_AUTH" + ], + "LastModifiedDate": "2022-07-14T00:32:32.311Z", + "TokenValidityUnits": { + "AccessToken": "hours", + "IdToken": "minutes", + "RefreshToken": "days" + }, + "UserPoolId": "user-pool-test" + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/CognitoLocalDb/user-pool-test.json b/MicroserviceTemplate/tools/EnvironmentInitializer/CognitoLocalDb/user-pool-test.json new file mode 100644 index 0000000..beff5e7 --- /dev/null +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/CognitoLocalDb/user-pool-test.json @@ -0,0 +1,271 @@ +{ + "Users": { + "testuser": { + "Username": "testuser", + "Password": "testpassword", + "Attributes": [ + { + "Name": "sub", + "Value": "0278febb-429d-4137-ac69-ab6a6ea6a41f" + }, + { + "Name": "email", + "Value": "testuser@domain.com" + }, + { + "Name": "phone_number", + "Value": "+61455587898" + } + ], + "Enabled": true, + "UserStatus": "CONFIRMED", + "UserCreateDate": "2022-07-14T00:30:54.047Z", + "UserLastModifiedDate": "2022-07-14T00:31:18.168Z", + "RefreshTokens": [ + "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2duaXRvOnVzZXJuYW1lIjoidGVzdHVzZXIiLCJlbWFpbCI6InRlc3R1c2VyQGh1bWFuZm9yY2UuY29tIiwiaWF0IjoxNjU3NzU4Nzk0LCJqdGkiOiI2MGZmNzczNC0wOTJhLTQzYjYtYTViYy01MTk2ZjEzNDI1MTIiLCJleHAiOjE2NTgzNjM1OTQsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6OTIyOS91c2VyLXBvb2wtdGVzdCJ9.XXOn9m7XyEZyZ5v9YLq-mVAcajVLNNDOXEntGE9Ywkl2yiFgVQclUE4o7S7gzDH26_Sq7ARJiJfFREC_94UJrB6O5B8uVuSqb8cK9rB1dku9o6S1q-IHK2DsM3nCeSNHzXSiTx-cFMKJ1npGHs6KTRBMLIMe6EuNE2a2Ivi6ObalJKO-HyVOz8KXTyVCaTAcHvZ0IFgv2qvKsD0AmZhLoZ0MS_5jvedvjlsreAiyfhihPNtFC94VHfr6Xt-Ah5slJ9SsDcfZcVFHgyUbb-r7zjoLm3T0F8PRHJtyGjzJphV1fUSoaaib8SFE8by29YO1UJ5IVHrwNpDZDOdWXGeaXA" + ] + } + }, + "Options": { + "Policies": { + "PasswordPolicy": { + "MinimumLength": 8, + "RequireUppercase": true, + "RequireLowercase": true, + "RequireNumbers": true, + "RequireSymbols": true, + "TemporaryPasswordValidityDays": 7 + } + }, + "LambdaConfig": {}, + "SchemaAttributes": [ + { + "Name": "sub", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": false, + "Required": true, + "StringAttributeConstraints": { + "MinLength": "1", + "MaxLength": "2048" + } + }, + { + "Name": "name", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "StringAttributeConstraints": { + "MinLength": "0", + "MaxLength": "2048" + } + }, + { + "Name": "given_name", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "StringAttributeConstraints": { + "MinLength": "0", + "MaxLength": "2048" + } + }, + { + "Name": "family_name", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "StringAttributeConstraints": { + "MinLength": "0", + "MaxLength": "2048" + } + }, + { + "Name": "middle_name", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "StringAttributeConstraints": { + "MinLength": "0", + "MaxLength": "2048" + } + }, + { + "Name": "nickname", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "StringAttributeConstraints": { + "MinLength": "0", + "MaxLength": "2048" + } + }, + { + "Name": "preferred_username", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "StringAttributeConstraints": { + "MinLength": "0", + "MaxLength": "2048" + } + }, + { + "Name": "profile", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "StringAttributeConstraints": { + "MinLength": "0", + "MaxLength": "2048" + } + }, + { + "Name": "picture", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "StringAttributeConstraints": { + "MinLength": "0", + "MaxLength": "2048" + } + }, + { + "Name": "website", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "StringAttributeConstraints": { + "MinLength": "0", + "MaxLength": "2048" + } + }, + { + "Name": "email", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "StringAttributeConstraints": { + "MinLength": "0", + "MaxLength": "2048" + } + }, + { + "Name": "email_verified", + "AttributeDataType": "Boolean", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false + }, + { + "Name": "gender", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "StringAttributeConstraints": { + "MinLength": "0", + "MaxLength": "2048" + } + }, + { + "Name": "birthdate", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "StringAttributeConstraints": { + "MinLength": "10", + "MaxLength": "10" + } + }, + { + "Name": "zoneinfo", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "StringAttributeConstraints": { + "MinLength": "0", + "MaxLength": "2048" + } + }, + { + "Name": "locale", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "StringAttributeConstraints": { + "MinLength": "0", + "MaxLength": "2048" + } + }, + { + "Name": "phone_number", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "StringAttributeConstraints": { + "MinLength": "0", + "MaxLength": "2048" + } + }, + { + "Name": "phone_number_verified", + "AttributeDataType": "Boolean", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false + }, + { + "Name": "address", + "AttributeDataType": "String", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "StringAttributeConstraints": { + "MinLength": "0", + "MaxLength": "2048" + } + }, + { + "Name": "updated_at", + "AttributeDataType": "Number", + "DeveloperOnlyAttribute": false, + "Mutable": true, + "Required": false, + "NumberAttributeConstraints": { + "MinValue": "0" + } + } + ], + "VerificationMessageTemplate": { + "DefaultEmailOption": "CONFIRM_WITH_CODE" + }, + "MfaConfiguration": "OFF", + "EstimatedNumberOfUsers": 0, + "EmailConfiguration": { + "EmailSendingAccount": "COGNITO_DEFAULT" + }, + "AdminCreateUserConfig": { + "AllowAdminCreateUserOnly": false, + "UnusedAccountValidityDays": 7 + }, + "UsernameAttributes": [ + "email" + ], + "Id": "user-pool-test" + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs b/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs index 584e5fd..b7d54ad 100644 --- a/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs @@ -19,6 +19,7 @@ Console.WriteLine("Cleaning up docker containers..."); Helper.KillProcess("tye"); //TODO: Add all processes here +Helper.KillProcess("FeatureFlag.API"); Helper.KillProcess("Mock.API"); Helper.RunPowerShellCommand(@"docker kill $(docker ps -q)"); Helper.RunPowerShellCommand(@"docker rm --force $(docker ps -a -q)"); From 1f20f3a0c4ec13af8244986a0edf428302792e8b Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Thu, 18 Aug 2022 09:23:29 +1000 Subject: [PATCH 45/56] [Microservices] added FeatureManagement API Consolidated Swagger Documentation --- MicroserviceTemplate/All.sln | 17 +++-- MicroserviceTemplate/docs/README.md | 7 ++ .../Authentication.API.csproj | 2 +- .../Swagger/ConfigureSwaggerOptions.cs | 2 +- .../src/Authentication.API/Program.cs | 2 +- .../AmazonAppConfigSettings.cs | 30 -------- .../AmazonFeatureFlagConfigSource.cs | 20 ----- .../src/FeatureFlag.API/Program.cs | 50 ------------- .../src/FeatureFlag.API/appsettings.json | 18 ----- .../AwsConfigurationBuilderExtension.cs} | 16 ++-- .../AwsFeatureFlagConfigurationProvider.cs} | 8 +- .../AwsFeatureFlagConfigurationSource.cs | 20 +++++ .../Configuration/AwsSettings.cs | 15 ++++ .../Swagger/ConfigureSwaggerOptions.cs | 55 ++++++++++++++ .../Swagger/SwaggerDefaultValues.cs | 48 ++++++++++++ .../Extensions/SwaggerExtensions.cs | 67 +++++++++++++++++ .../Extensions/VersionExtension.cs | 21 ++++++ .../FeatureManagement.API.csproj} | 3 +- .../src/FeatureManagement.API/Program.cs | 75 +++++++++++++++++++ .../Properties/launchSettings.json | 0 .../FeatureManagement.API/appsettings.json | 54 +++++++++++++ .../Swagger/ConfigureSwaggerOptions.cs | 55 ++++++++++++++ .../Swagger/SwaggerDefaultValues.cs | 48 ++++++++++++ .../Mock.API/Extensions/SwaggerExtensions.cs | 67 +++++++++++++++++ .../Mock.API/Extensions/VersionExtension.cs | 21 ++++++ .../src/Mock.API/Mock.API.csproj | 5 +- MicroserviceTemplate/src/Mock.API/Program.cs | 11 ++- .../Swagger/ConfigureSwaggerOptions.cs | 2 +- .../src/ServiceName.API/Program.cs | 4 +- .../ServiceName.API/ServiceName.API.csproj | 2 +- .../tools/EnvironmentInitializer/Program.cs | 4 +- MicroserviceTemplate/tye.yaml | 8 +- 32 files changed, 606 insertions(+), 151 deletions(-) create mode 100644 MicroserviceTemplate/docs/README.md delete mode 100644 MicroserviceTemplate/src/FeatureFlag.API/AmazonAppConfigSettings.cs delete mode 100644 MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagConfigSource.cs delete mode 100644 MicroserviceTemplate/src/FeatureFlag.API/Program.cs delete mode 100644 MicroserviceTemplate/src/FeatureFlag.API/appsettings.json rename MicroserviceTemplate/src/{FeatureFlag.API/AmazonFeatureFlagExtension.cs => FeatureManagement.API/Configuration/AwsConfigurationBuilderExtension.cs} (74%) rename MicroserviceTemplate/src/{FeatureFlag.API/AmazonFeatureFlagProvider.cs => FeatureManagement.API/Configuration/AwsFeatureFlagConfigurationProvider.cs} (92%) create mode 100644 MicroserviceTemplate/src/FeatureManagement.API/Configuration/AwsFeatureFlagConfigurationSource.cs create mode 100644 MicroserviceTemplate/src/FeatureManagement.API/Configuration/AwsSettings.cs create mode 100644 MicroserviceTemplate/src/FeatureManagement.API/Extensions/Swagger/ConfigureSwaggerOptions.cs create mode 100644 MicroserviceTemplate/src/FeatureManagement.API/Extensions/Swagger/SwaggerDefaultValues.cs create mode 100644 MicroserviceTemplate/src/FeatureManagement.API/Extensions/SwaggerExtensions.cs create mode 100644 MicroserviceTemplate/src/FeatureManagement.API/Extensions/VersionExtension.cs rename MicroserviceTemplate/src/{FeatureFlag.API/FeatureFlag.API.csproj => FeatureManagement.API/FeatureManagement.API.csproj} (78%) create mode 100644 MicroserviceTemplate/src/FeatureManagement.API/Program.cs rename MicroserviceTemplate/src/{FeatureFlag.API => FeatureManagement.API}/Properties/launchSettings.json (100%) create mode 100644 MicroserviceTemplate/src/FeatureManagement.API/appsettings.json create mode 100644 MicroserviceTemplate/src/Mock.API/Extensions/Swagger/ConfigureSwaggerOptions.cs create mode 100644 MicroserviceTemplate/src/Mock.API/Extensions/Swagger/SwaggerDefaultValues.cs create mode 100644 MicroserviceTemplate/src/Mock.API/Extensions/SwaggerExtensions.cs create mode 100644 MicroserviceTemplate/src/Mock.API/Extensions/VersionExtension.cs diff --git a/MicroserviceTemplate/All.sln b/MicroserviceTemplate/All.sln index 0e634d9..7614d68 100644 --- a/MicroserviceTemplate/All.sln +++ b/MicroserviceTemplate/All.sln @@ -36,7 +36,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cdk", "aws\Cdk.csproj", "{F EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mock.API", "src\Mock.API\Mock.API.csproj", "{FBEEAEA7-7A4E-4D97-9947-C3375A1F8217}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FeatureFlag.API", "src\FeatureFlag.API\FeatureFlag.API.csproj", "{02C79D99-4EFD-45E0-AB01-37B2A43FC2E5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FeatureManagement.API", "src\FeatureManagement.API\FeatureManagement.API.csproj", "{0349212E-D1A7-41FB-97E2-4DADCB7B5265}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{779C3979-951C-44F8-9F71-42C588768D6A}" + ProjectSection(SolutionItems) = preProject + docs\README.md = docs\README.md + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -80,10 +85,10 @@ Global {FBEEAEA7-7A4E-4D97-9947-C3375A1F8217}.Debug|Any CPU.Build.0 = Debug|Any CPU {FBEEAEA7-7A4E-4D97-9947-C3375A1F8217}.Release|Any CPU.ActiveCfg = Release|Any CPU {FBEEAEA7-7A4E-4D97-9947-C3375A1F8217}.Release|Any CPU.Build.0 = Release|Any CPU - {02C79D99-4EFD-45E0-AB01-37B2A43FC2E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {02C79D99-4EFD-45E0-AB01-37B2A43FC2E5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {02C79D99-4EFD-45E0-AB01-37B2A43FC2E5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {02C79D99-4EFD-45E0-AB01-37B2A43FC2E5}.Release|Any CPU.Build.0 = Release|Any CPU + {0349212E-D1A7-41FB-97E2-4DADCB7B5265}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0349212E-D1A7-41FB-97E2-4DADCB7B5265}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0349212E-D1A7-41FB-97E2-4DADCB7B5265}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0349212E-D1A7-41FB-97E2-4DADCB7B5265}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -98,7 +103,7 @@ Global {A9FA5380-D9B1-4AA1-93FB-F805E22FC9B2} = {39870B6D-6682-4B08-8208-6C05B41930EC} {F9885267-A919-4711-B9AF-9C6ADEC4DA21} = {462D80EC-4E6B-4B0F-8062-779007AB3A90} {FBEEAEA7-7A4E-4D97-9947-C3375A1F8217} = {3FD41DDF-2B43-4372-9FB1-62597803431B} - {02C79D99-4EFD-45E0-AB01-37B2A43FC2E5} = {3FD41DDF-2B43-4372-9FB1-62597803431B} + {0349212E-D1A7-41FB-97E2-4DADCB7B5265} = {3FD41DDF-2B43-4372-9FB1-62597803431B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C9A3A709-C3C6-4654-AC2B-A92420A60DF5} diff --git a/MicroserviceTemplate/docs/README.md b/MicroserviceTemplate/docs/README.md new file mode 100644 index 0000000..7d9b192 --- /dev/null +++ b/MicroserviceTemplate/docs/README.md @@ -0,0 +1,7 @@ +1. Go to ```tools\EnvironmentInitializer``` +2. Run ```dotnet run``` + + +# Commands +Stop Environment: ```dotnet run --exit``` (Run as administrator) +Debug Applications: ```dotnet run --debug``` \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Authentication.API.csproj b/MicroserviceTemplate/src/Authentication.API/Authentication.API.csproj index aa28b3e..048f9df 100644 --- a/MicroserviceTemplate/src/Authentication.API/Authentication.API.csproj +++ b/MicroserviceTemplate/src/Authentication.API/Authentication.API.csproj @@ -20,7 +20,7 @@ - + diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/ConfigureSwaggerOptions.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/ConfigureSwaggerOptions.cs index ddf4849..46dd821 100644 --- a/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/ConfigureSwaggerOptions.cs +++ b/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/ConfigureSwaggerOptions.cs @@ -37,7 +37,7 @@ private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription descrip { var info = new OpenApiInfo() { - Title = "Authentication API", + Title = "API Documentation", Version = description.ApiVersion.ToString(), Description = "A sample application with Swagger, Swashbuckle, and API versioning.", Contact = new OpenApiContact() { Name = "Leandro", Email = "leandro@somewhere.com" }, diff --git a/MicroserviceTemplate/src/Authentication.API/Program.cs b/MicroserviceTemplate/src/Authentication.API/Program.cs index d4cbe61..837dd8f 100644 --- a/MicroserviceTemplate/src/Authentication.API/Program.cs +++ b/MicroserviceTemplate/src/Authentication.API/Program.cs @@ -11,7 +11,7 @@ // package will act as the webserver translating request and responses between the Lambda event source and ASP.NET Core. // https://aws.amazon.com/blogs/compute/introducing-the-net-6-runtime-for-aws-lambda/ -builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi); +builder.Services.AddAWSLambdaHosting(LambdaEventSource.HttpApi); builder.Services.AddInfrastructureServices(builder.Configuration); diff --git a/MicroserviceTemplate/src/FeatureFlag.API/AmazonAppConfigSettings.cs b/MicroserviceTemplate/src/FeatureFlag.API/AmazonAppConfigSettings.cs deleted file mode 100644 index f4e5672..0000000 --- a/MicroserviceTemplate/src/FeatureFlag.API/AmazonAppConfigSettings.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace FeatureFlag.API -{ - public class AmazonAppConfigSettings - { - /// - /// Name or Id - /// - public string ApplicationId { get; set; } = String.Empty; - - /// - /// Name or Id - /// - public string EnvironmentId { get; set; } = String.Empty; - - /// - /// Name or Id - /// - public string ConfigurationProfileId { get; set; } = String.Empty; - - /// - /// Data poll frequency, default is 10 minutes - /// - public TimeSpan? DataPollFrequency { get; set; } = TimeSpan.FromMinutes(10); - - /// - /// e.g.: FeatureManagement or MyApp:AWSFlags - /// - public string ConfigSectionNaming { get; set; } = String.Empty; - } -} diff --git a/MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagConfigSource.cs b/MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagConfigSource.cs deleted file mode 100644 index 2be5511..0000000 --- a/MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagConfigSource.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Amazon.AppConfigData; - -namespace FeatureFlag.API -{ - public class AmazonFeatureFlagConfigSource : IConfigurationSource - { - private readonly AmazonAppConfigDataClient _client; - private readonly AmazonAppConfigSettings _cfg; - - public AmazonFeatureFlagConfigSource(AmazonAppConfigDataClient client, AmazonAppConfigSettings cfg) - { - _client = client; - _cfg = cfg; - } - public IConfigurationProvider Build(IConfigurationBuilder builder) - { - return new AmazonFeatureFlagProvider(_client, _cfg); - } - } -} diff --git a/MicroserviceTemplate/src/FeatureFlag.API/Program.cs b/MicroserviceTemplate/src/FeatureFlag.API/Program.cs deleted file mode 100644 index 25603a3..0000000 --- a/MicroserviceTemplate/src/FeatureFlag.API/Program.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Amazon.AppConfigData; -using Amazon.AppConfigData.Model; -using FeatureFlag.API; -using Microsoft.FeatureManagement; - -var builder = WebApplication.CreateBuilder(args); - -// Add services to the container. -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); - -var dataPollFrequencyInMinutes = 5; - -builder.Configuration.SetBasePath(Environment.CurrentDirectory) - .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddSystemsManager($"/{builder.Configuration["AwsAppConfig:ApplicationId"]}/", TimeSpan.FromMinutes(dataPollFrequencyInMinutes)) - .AddAppConfigUsingLambdaExtension(builder.Configuration["AwsAppConfig:ApplicationId"], builder.Configuration["AwsAppConfig:EnvironmentId"], builder.Configuration["AwsAppConfig:FreeformConfigurationProfileId"]) - .AddAmazonFeatureFlags(c => - { - //these can be the id and the name as well... - c.ApplicationId = builder.Configuration["AwsAppConfig:ApplicationId"]; - c.EnvironmentId = builder.Configuration["AwsAppConfig:EnvironmentId"]; - c.ConfigurationProfileId = builder.Configuration["AwsAppConfig:FeatureFlagConfigurationProfileId"]; - c.DataPollFrequency = TimeSpan.FromMinutes(dataPollFrequencyInMinutes); - c.ConfigSectionNaming = "FeatureFlags"; //this is what you should use when u add the feature management service (next line), also can be empty. - }) - .Build(); - - -builder.Services.AddFeatureManagement(builder.Configuration.GetSection("FeatureFlags")); - -var app = builder.Build(); - -// Configure the HTTP request pipeline. -if (app.Environment.IsDevelopment()) -{ - app.UseSwagger(); - app.UseSwaggerUI(); -} - -app.UseHttpsRedirection(); - -app.MapGet("/feature/{featureName}", async (IFeatureManager featureManager, string featureName) => -{ - return await featureManager.IsEnabledAsync(featureName); -}); - -app.Run(); - diff --git a/MicroserviceTemplate/src/FeatureFlag.API/appsettings.json b/MicroserviceTemplate/src/FeatureFlag.API/appsettings.json deleted file mode 100644 index f3c399b..0000000 --- a/MicroserviceTemplate/src/FeatureFlag.API/appsettings.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*", - "AwsAppConfig": { - "ApplicationId": "kat546s", - "EnvironmentId": "bcte59f", - "FeatureFlagConfigurationProfileId": "b97qhdo", - "FreeformConfigurationProfileId": "gdz1prc" - }, - "FeatureFlags": { - "featureA": false - } -} diff --git a/MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagExtension.cs b/MicroserviceTemplate/src/FeatureManagement.API/Configuration/AwsConfigurationBuilderExtension.cs similarity index 74% rename from MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagExtension.cs rename to MicroserviceTemplate/src/FeatureManagement.API/Configuration/AwsConfigurationBuilderExtension.cs index b658d3c..a374119 100644 --- a/MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagExtension.cs +++ b/MicroserviceTemplate/src/FeatureManagement.API/Configuration/AwsConfigurationBuilderExtension.cs @@ -1,13 +1,13 @@ using Amazon.AppConfigData; -namespace FeatureFlag.API +namespace FeatureFlag.API.Configuration { - public static class AmazonFeatureFlagExtension + public static class AwsConfigurationBuilderExtension { public static IConfigurationBuilder AddAmazonFeatureFlags( this IConfigurationBuilder builder, AmazonAppConfigDataClient client, - AmazonAppConfigSettings config) + AwsSettings config) { var cfg = config ?? throw new ArgumentNullException(); if (string.IsNullOrEmpty(cfg.EnvironmentId)) @@ -23,25 +23,25 @@ public static IConfigurationBuilder AddAmazonFeatureFlags( throw new ArgumentException("Configuration id (or name) missing from Amazon AppConfig settings"); } - builder.Add(new AmazonFeatureFlagConfigSource(client, cfg)); + builder.Add(new AwsFeatureFlagConfigurationSource(client, cfg)); return builder; } - public static IConfigurationBuilder AddAmazonFeatureFlags(this IConfigurationBuilder builder, AmazonAppConfigSettings config) + public static IConfigurationBuilder AddAmazonFeatureFlags(this IConfigurationBuilder builder, AwsSettings config) { return builder.AddAmazonFeatureFlags(new AmazonAppConfigDataClient(), config); } - public static IConfigurationBuilder AddAmazonFeatureFlags(this IConfigurationBuilder builder, Action configAction) + public static IConfigurationBuilder AddAmazonFeatureFlags(this IConfigurationBuilder builder, Action configAction) { - var settings = new AmazonAppConfigSettings(); + var settings = new AwsSettings(); configAction(settings); return builder.AddAmazonFeatureFlags(new AmazonAppConfigDataClient(), settings); } - public static IConfigurationBuilder AddAmazonFeatureFlags(this IConfigurationBuilder builder, AmazonAppConfigSettings config, AmazonAppConfigDataClient client) + public static IConfigurationBuilder AddAmazonFeatureFlags(this IConfigurationBuilder builder, AwsSettings config, AmazonAppConfigDataClient client) { return builder.AddAmazonFeatureFlags(client, config); } diff --git a/MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagProvider.cs b/MicroserviceTemplate/src/FeatureManagement.API/Configuration/AwsFeatureFlagConfigurationProvider.cs similarity index 92% rename from MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagProvider.cs rename to MicroserviceTemplate/src/FeatureManagement.API/Configuration/AwsFeatureFlagConfigurationProvider.cs index c07a734..20a1a3a 100644 --- a/MicroserviceTemplate/src/FeatureFlag.API/AmazonFeatureFlagProvider.cs +++ b/MicroserviceTemplate/src/FeatureManagement.API/Configuration/AwsFeatureFlagConfigurationProvider.cs @@ -3,11 +3,11 @@ using Amazon.AppConfigData; using Amazon.AppConfigData.Model; -namespace FeatureFlag.API +namespace FeatureFlag.API.Configuration { - public class AmazonFeatureFlagProvider: ConfigurationProvider + public class AwsFeatureFlagConfigurationProvider : ConfigurationProvider { - private readonly AmazonAppConfigSettings _cfg; + private readonly AwsSettings _cfg; private readonly StartConfigurationSessionRequest _startRequest; private readonly AmazonAppConfigDataClient _client; private readonly TimeSpan? _frequency; @@ -17,7 +17,7 @@ public class AmazonFeatureFlagProvider: ConfigurationProvider private string _token = string.Empty; private readonly string _configSections = string.Empty; - public AmazonFeatureFlagProvider(AmazonAppConfigDataClient client, AmazonAppConfigSettings cfg) + public AwsFeatureFlagConfigurationProvider(AmazonAppConfigDataClient client, AwsSettings cfg) { _cfg = cfg; diff --git a/MicroserviceTemplate/src/FeatureManagement.API/Configuration/AwsFeatureFlagConfigurationSource.cs b/MicroserviceTemplate/src/FeatureManagement.API/Configuration/AwsFeatureFlagConfigurationSource.cs new file mode 100644 index 0000000..a7e5a87 --- /dev/null +++ b/MicroserviceTemplate/src/FeatureManagement.API/Configuration/AwsFeatureFlagConfigurationSource.cs @@ -0,0 +1,20 @@ +using Amazon.AppConfigData; + +namespace FeatureFlag.API.Configuration +{ + public class AwsFeatureFlagConfigurationSource : IConfigurationSource + { + private readonly AmazonAppConfigDataClient _client; + private readonly AwsSettings _cfg; + + public AwsFeatureFlagConfigurationSource(AmazonAppConfigDataClient client, AwsSettings cfg) + { + _client = client; + _cfg = cfg; + } + public IConfigurationProvider Build(IConfigurationBuilder builder) + { + return new AwsFeatureFlagConfigurationProvider(_client, _cfg); + } + } +} diff --git a/MicroserviceTemplate/src/FeatureManagement.API/Configuration/AwsSettings.cs b/MicroserviceTemplate/src/FeatureManagement.API/Configuration/AwsSettings.cs new file mode 100644 index 0000000..2b72d41 --- /dev/null +++ b/MicroserviceTemplate/src/FeatureManagement.API/Configuration/AwsSettings.cs @@ -0,0 +1,15 @@ +namespace FeatureFlag.API.Configuration +{ + public class AwsSettings + { + public string ApplicationId { get; set; } = string.Empty; + + public string EnvironmentId { get; set; } = string.Empty; + + public string ConfigurationProfileId { get; set; } = string.Empty; + + public TimeSpan? DataPollFrequency { get; set; } = TimeSpan.FromMinutes(10); + + public string ConfigSectionNaming { get; set; } = string.Empty; + } +} diff --git a/MicroserviceTemplate/src/FeatureManagement.API/Extensions/Swagger/ConfigureSwaggerOptions.cs b/MicroserviceTemplate/src/FeatureManagement.API/Extensions/Swagger/ConfigureSwaggerOptions.cs new file mode 100644 index 0000000..b97b1cd --- /dev/null +++ b/MicroserviceTemplate/src/FeatureManagement.API/Extensions/Swagger/ConfigureSwaggerOptions.cs @@ -0,0 +1,55 @@ +namespace FeatureManagement.API.Extensions.Swagger +{ + using System; + using Asp.Versioning.ApiExplorer; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Options; + using Microsoft.OpenApi.Models; + using Swashbuckle.AspNetCore.SwaggerGen; + + /// + /// Configures the Swagger generation options. + /// + /// This allows API versioning to define a Swagger document per API version after the + /// service has been resolved from the service container. + public class ConfigureSwaggerOptions : IConfigureOptions + { + private readonly IApiVersionDescriptionProvider provider; + + /// + /// Initializes a new instance of the class. + /// + /// The provider used to generate Swagger documents. + public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => this.provider = provider; + + /// + public void Configure(SwaggerGenOptions options) + { + // add a swagger document for each discovered API version + // note: you might choose to skip or document deprecated API versions differently + foreach (var description in provider.ApiVersionDescriptions) + { + options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); + } + } + + private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) + { + var info = new OpenApiInfo() + { + Title = "API Documentation", + Version = description.ApiVersion.ToString(), + Description = "A sample application with Swagger, Swashbuckle, and API versioning.", + Contact = new OpenApiContact() { Name = "Leandro", Email = "leandro@somewhere.com" }, + License = new OpenApiLicense() { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") } + }; + + if (description.IsDeprecated) + { + info.Description += " This API version has been deprecated."; + } + + return info; + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/FeatureManagement.API/Extensions/Swagger/SwaggerDefaultValues.cs b/MicroserviceTemplate/src/FeatureManagement.API/Extensions/Swagger/SwaggerDefaultValues.cs new file mode 100644 index 0000000..c958d6f --- /dev/null +++ b/MicroserviceTemplate/src/FeatureManagement.API/Extensions/Swagger/SwaggerDefaultValues.cs @@ -0,0 +1,48 @@ +namespace FeatureManagement.API.Extensions.Swagger +{ + using System.Linq; + using Microsoft.AspNetCore.Mvc.ApiExplorer; + using Microsoft.OpenApi.Any; + using Microsoft.OpenApi.Models; + using Swashbuckle.AspNetCore.SwaggerGen; + + /// + /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter. + /// + /// This is only required due to bugs in the . + /// Once they are fixed and published, this class can be removed. + public class SwaggerDefaultValues : IOperationFilter + { + /// + /// Applies the filter to the specified operation using the given context. + /// + /// The operation to apply the filter to. + /// The current operation filter context. + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + var apiDescription = context.ApiDescription; + operation.Deprecated |= apiDescription.IsDeprecated(); + + if (operation.Parameters == null) + return; + + // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412 + // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413 + foreach (var parameter in operation.Parameters) + { + var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); + if (parameter.Description == null) + { + parameter.Description = description.ModelMetadata?.Description; + } + + if (parameter.Schema.Default == null && description.DefaultValue != null) + { + parameter.Schema.Default = new OpenApiString(description.DefaultValue.ToString()); + } + + parameter.Required |= description.IsRequired; + } + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/FeatureManagement.API/Extensions/SwaggerExtensions.cs b/MicroserviceTemplate/src/FeatureManagement.API/Extensions/SwaggerExtensions.cs new file mode 100644 index 0000000..68e4dd9 --- /dev/null +++ b/MicroserviceTemplate/src/FeatureManagement.API/Extensions/SwaggerExtensions.cs @@ -0,0 +1,67 @@ +using FeatureManagement.API.Extensions.Swagger; +using Microsoft.Extensions.Options; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace FeatureManagement.API.Extensions +{ + public static class SwaggerExtensions + { + public static void AddSwaggerSupport(this IServiceCollection services) + { + services.AddTransient, ConfigureSwaggerOptions>(); + + services.AddSwaggerGen(options => + { + options.OperationFilter(); + + var securityScheme = new OpenApiSecurityScheme() + { + Name = "Authorization", + Type = SecuritySchemeType.ApiKey, + Scheme = "Bearer", + BearerFormat = "JWT", + In = ParameterLocation.Header, + Description = "JSON Web Token based security", + }; + + var securityReq = new OpenApiSecurityRequirement() + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + new string[] {} + } + }; + + options.AddSecurityDefinition("Bearer", securityScheme); + options.AddSecurityRequirement(securityReq); + }); + } + + public static void ConfigureSwaggerUI(this WebApplication app) + { + app.UseSwagger(); + app.UseSwaggerUI(options => + { + options.RoutePrefix = string.Empty; + + var descriptions = app.DescribeApiVersions(); + + // build a swagger endpoint for each discovered API version + foreach (var description in descriptions) + { + var url = $"./swagger/{description.GroupName}/swagger.json"; + var name = description.GroupName.ToUpperInvariant(); + options.SwaggerEndpoint(url, name); + } + }); + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/FeatureManagement.API/Extensions/VersionExtension.cs b/MicroserviceTemplate/src/FeatureManagement.API/Extensions/VersionExtension.cs new file mode 100644 index 0000000..668f909 --- /dev/null +++ b/MicroserviceTemplate/src/FeatureManagement.API/Extensions/VersionExtension.cs @@ -0,0 +1,21 @@ +namespace FeatureManagement.API.Extensions +{ + public static class VersionExtension + { + public static void AddApiVersioningSupport(this IServiceCollection services) + { + services.AddApiVersioning() + .AddApiExplorer( + options => + { + // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service + // note: the specified format code will format the version as "'v'major[.minor][-status]" + options.GroupNameFormat = "'v'VVV"; + + // note: this option is only necessary when versioning by url segment. the SubstitutionFormat + // can also be used to control the format of the API version in route templates + options.SubstituteApiVersionInUrl = true; + }); + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/FeatureFlag.API/FeatureFlag.API.csproj b/MicroserviceTemplate/src/FeatureManagement.API/FeatureManagement.API.csproj similarity index 78% rename from MicroserviceTemplate/src/FeatureFlag.API/FeatureFlag.API.csproj rename to MicroserviceTemplate/src/FeatureManagement.API/FeatureManagement.API.csproj index 01ad984..ffbab8b 100644 --- a/MicroserviceTemplate/src/FeatureFlag.API/FeatureFlag.API.csproj +++ b/MicroserviceTemplate/src/FeatureManagement.API/FeatureManagement.API.csproj @@ -8,7 +8,8 @@ - + + diff --git a/MicroserviceTemplate/src/FeatureManagement.API/Program.cs b/MicroserviceTemplate/src/FeatureManagement.API/Program.cs new file mode 100644 index 0000000..bb1c83c --- /dev/null +++ b/MicroserviceTemplate/src/FeatureManagement.API/Program.cs @@ -0,0 +1,75 @@ +using FeatureFlag.API.Configuration; +using FeatureManagement.API.Extensions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.FeatureManagement; +using Microsoft.FeatureManagement.FeatureFilters; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerSupport(); +builder.Services.AddApiVersioningSupport(); + +var dataPollFrequencyInMinutes = 5; + +builder.Configuration.SetBasePath(Environment.CurrentDirectory) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + //.AddSystemsManager($"/{builder.Configuration["AwsAppConfig:ApplicationId"]}/", TimeSpan.FromMinutes(dataPollFrequencyInMinutes)) + //.AddAppConfigUsingLambdaExtension(builder.Configuration["AwsAppConfig:ApplicationId"], + // builder.Configuration["AwsAppConfig:EnvironmentId"], + // builder.Configuration["AwsAppConfig:FreeformConfigurationProfileId"]) + //.AddAmazonFeatureFlags(c => + //{ + // c.ApplicationId = builder.Configuration["AwsAppConfig:ApplicationId"]; + // c.EnvironmentId = builder.Configuration["AwsAppConfig:EnvironmentId"]; + // c.ConfigurationProfileId = builder.Configuration["AwsAppConfig:FeatureFlagConfigurationProfileId"]; + // c.DataPollFrequency = TimeSpan.FromMinutes(dataPollFrequencyInMinutes); + // c.ConfigSectionNaming = "FeatureFlags"; + //}) + .Build(); + +builder.Services.AddFeatureManagement(builder.Configuration.GetSection("FeatureFlags")) + .AddFeatureFilter(); + +builder.Services.Configure(options => +{ + options.IgnoreMissingFeatureFilters = true; +}); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.ConfigureSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.MapGet("feature/toggle/simple/{featureName}/enabled", async (IFeatureManager featureManager, string featureName) => +{ + try + { + return await featureManager.IsEnabledAsync(featureName); + } + catch (Exception) + { + //Log Exception + return false; + } +}); + +app.MapGet("feature/toggle/targeting/{featureName}/enabled", async (IFeatureManager featureManager, string featureName, [FromQuery(Name = "instanceId")] string instanceId, [FromQuery(Name = "userId")] string userId) => +{ + // userId and groups defined somewhere earlier in application + var targetingContext = new TargetingContext() + { + UserId = $"{instanceId}:{userId}", + Groups = new List() { instanceId } + }; + return await featureManager.IsEnabledAsync(featureName, targetingContext); +}); + + +app.Run(); + diff --git a/MicroserviceTemplate/src/FeatureFlag.API/Properties/launchSettings.json b/MicroserviceTemplate/src/FeatureManagement.API/Properties/launchSettings.json similarity index 100% rename from MicroserviceTemplate/src/FeatureFlag.API/Properties/launchSettings.json rename to MicroserviceTemplate/src/FeatureManagement.API/Properties/launchSettings.json diff --git a/MicroserviceTemplate/src/FeatureManagement.API/appsettings.json b/MicroserviceTemplate/src/FeatureManagement.API/appsettings.json new file mode 100644 index 0000000..ff0b671 --- /dev/null +++ b/MicroserviceTemplate/src/FeatureManagement.API/appsettings.json @@ -0,0 +1,54 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "AwsAppConfig": { + "ApplicationId": "kbt546s", + "EnvironmentId": "bcre59f", + "FeatureFlagConfigurationProfileId": "b97vhdo", + "FreeformConfigurationProfileId": "gdx1prc" + }, + "FeatureFlags": { + "featureA": false, + "featureB": true, + "featureC": false, + "featureD": { + "EnabledFor": [ + { + "Name": "Microsoft.Targeting", + "Parameters": { + "Audience": { + "Users": [ + //"{instanceId}:{userId}", + "609de2ca-5b02-4e80-a196-c9154b1caaa2:fdaf1d4d-c599-4284-bcda-ebad6026bf1c", + "d723f57d-2084-4b23-87ea-1c5841959605:0acb86fe-4f63-4d75-bb8e-6ac15fd145e4" + ], + "Groups": [ + { + //"Name": "{instanceId}", + "Name": "609de2ca-5b02-4e80-a196-c9154b1caaa2", + "RolloutPercentage": 0 + }, + { + + "Name": "d723f57d-2084-4b23-87ea-1c5841959605", + "RolloutPercentage": 50 + }, + { + + "Name": "c8d8d711-46bc-4719-a577-d2a13715915d", + "RolloutPercentage": 100 + } + ], + "DefaultRolloutPercentage": 0 + } + } + } + ] + } + } +} diff --git a/MicroserviceTemplate/src/Mock.API/Extensions/Swagger/ConfigureSwaggerOptions.cs b/MicroserviceTemplate/src/Mock.API/Extensions/Swagger/ConfigureSwaggerOptions.cs new file mode 100644 index 0000000..79b57a1 --- /dev/null +++ b/MicroserviceTemplate/src/Mock.API/Extensions/Swagger/ConfigureSwaggerOptions.cs @@ -0,0 +1,55 @@ +namespace Mock.API.Extensions.Swagger +{ + using System; + using Asp.Versioning.ApiExplorer; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Options; + using Microsoft.OpenApi.Models; + using Swashbuckle.AspNetCore.SwaggerGen; + + /// + /// Configures the Swagger generation options. + /// + /// This allows API versioning to define a Swagger document per API version after the + /// service has been resolved from the service container. + public class ConfigureSwaggerOptions : IConfigureOptions + { + private readonly IApiVersionDescriptionProvider provider; + + /// + /// Initializes a new instance of the class. + /// + /// The provider used to generate Swagger documents. + public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => this.provider = provider; + + /// + public void Configure(SwaggerGenOptions options) + { + // add a swagger document for each discovered API version + // note: you might choose to skip or document deprecated API versions differently + foreach (var description in provider.ApiVersionDescriptions) + { + options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); + } + } + + private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) + { + var info = new OpenApiInfo() + { + Title = "API Documentation", + Version = description.ApiVersion.ToString(), + Description = "A sample application with Swagger, Swashbuckle, and API versioning.", + Contact = new OpenApiContact() { Name = "Leandro", Email = "leandro@somewhere.com" }, + License = new OpenApiLicense() { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") } + }; + + if (description.IsDeprecated) + { + info.Description += " This API version has been deprecated."; + } + + return info; + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Mock.API/Extensions/Swagger/SwaggerDefaultValues.cs b/MicroserviceTemplate/src/Mock.API/Extensions/Swagger/SwaggerDefaultValues.cs new file mode 100644 index 0000000..a1285d1 --- /dev/null +++ b/MicroserviceTemplate/src/Mock.API/Extensions/Swagger/SwaggerDefaultValues.cs @@ -0,0 +1,48 @@ +namespace Mock.API.Extensions.Swagger +{ + using System.Linq; + using Microsoft.AspNetCore.Mvc.ApiExplorer; + using Microsoft.OpenApi.Any; + using Microsoft.OpenApi.Models; + using Swashbuckle.AspNetCore.SwaggerGen; + + /// + /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter. + /// + /// This is only required due to bugs in the . + /// Once they are fixed and published, this class can be removed. + public class SwaggerDefaultValues : IOperationFilter + { + /// + /// Applies the filter to the specified operation using the given context. + /// + /// The operation to apply the filter to. + /// The current operation filter context. + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + var apiDescription = context.ApiDescription; + operation.Deprecated |= apiDescription.IsDeprecated(); + + if (operation.Parameters == null) + return; + + // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412 + // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413 + foreach (var parameter in operation.Parameters) + { + var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); + if (parameter.Description == null) + { + parameter.Description = description.ModelMetadata?.Description; + } + + if (parameter.Schema.Default == null && description.DefaultValue != null) + { + parameter.Schema.Default = new OpenApiString(description.DefaultValue.ToString()); + } + + parameter.Required |= description.IsRequired; + } + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Mock.API/Extensions/SwaggerExtensions.cs b/MicroserviceTemplate/src/Mock.API/Extensions/SwaggerExtensions.cs new file mode 100644 index 0000000..6d08b80 --- /dev/null +++ b/MicroserviceTemplate/src/Mock.API/Extensions/SwaggerExtensions.cs @@ -0,0 +1,67 @@ +using Microsoft.Extensions.Options; +using Microsoft.OpenApi.Models; +using Mock.API.Extensions.Swagger; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Mock.API.Extensions +{ + public static class SwaggerExtensions + { + public static void AddSwaggerSupport(this IServiceCollection services) + { + services.AddTransient, ConfigureSwaggerOptions>(); + + services.AddSwaggerGen(options => + { + options.OperationFilter(); + + var securityScheme = new OpenApiSecurityScheme() + { + Name = "Authorization", + Type = SecuritySchemeType.ApiKey, + Scheme = "Bearer", + BearerFormat = "JWT", + In = ParameterLocation.Header, + Description = "JSON Web Token based security", + }; + + var securityReq = new OpenApiSecurityRequirement() + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + new string[] {} + } + }; + + options.AddSecurityDefinition("Bearer", securityScheme); + options.AddSecurityRequirement(securityReq); + }); + } + + public static void ConfigureSwaggerUI(this WebApplication app) + { + app.UseSwagger(); + app.UseSwaggerUI(options => + { + options.RoutePrefix = string.Empty; + + var descriptions = app.DescribeApiVersions(); + + // build a swagger endpoint for each discovered API version + foreach (var description in descriptions) + { + var url = $"./swagger/{description.GroupName}/swagger.json"; + var name = description.GroupName.ToUpperInvariant(); + options.SwaggerEndpoint(url, name); + } + }); + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Mock.API/Extensions/VersionExtension.cs b/MicroserviceTemplate/src/Mock.API/Extensions/VersionExtension.cs new file mode 100644 index 0000000..2183a2f --- /dev/null +++ b/MicroserviceTemplate/src/Mock.API/Extensions/VersionExtension.cs @@ -0,0 +1,21 @@ +namespace Mock.API.Extensions +{ + public static class VersionExtension + { + public static void AddApiVersioningSupport(this IServiceCollection services) + { + services.AddApiVersioning() + .AddApiExplorer( + options => + { + // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service + // note: the specified format code will format the version as "'v'major[.minor][-status]" + options.GroupNameFormat = "'v'VVV"; + + // note: this option is only necessary when versioning by url segment. the SubstitutionFormat + // can also be used to control the format of the API version in route templates + options.SubstituteApiVersionInUrl = true; + }); + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Mock.API/Mock.API.csproj b/MicroserviceTemplate/src/Mock.API/Mock.API.csproj index ca8fea8..ba401d6 100644 --- a/MicroserviceTemplate/src/Mock.API/Mock.API.csproj +++ b/MicroserviceTemplate/src/Mock.API/Mock.API.csproj @@ -1,4 +1,4 @@ - + net6.0 enable @@ -12,6 +12,9 @@ + + + diff --git a/MicroserviceTemplate/src/Mock.API/Program.cs b/MicroserviceTemplate/src/Mock.API/Program.cs index 67e2187..488f54b 100644 --- a/MicroserviceTemplate/src/Mock.API/Program.cs +++ b/MicroserviceTemplate/src/Mock.API/Program.cs @@ -1,18 +1,27 @@ using System.Text.Json; using Microsoft.AspNetCore.Mvc; using Mock.API.Model; +using Mock.API.Extensions; var builder = WebApplication.CreateBuilder(args); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerSupport(); +builder.Services.AddApiVersioningSupport(); + // Add services to the container. builder.Services.AddControllers(); // Add AWS Lambda support. When application is run in Lambda Kestrel is swapped out as the web server with Amazon.Lambda.AspNetCoreServer. This // package will act as the webserver translating request and responses between the Lambda event source and ASP.NET Core. -builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi); +builder.Services.AddAWSLambdaHosting(LambdaEventSource.HttpApi); var app = builder.Build(); +if (app.Environment.IsDevelopment()) +{ + app.ConfigureSwaggerUI(); +} app.UseHttpsRedirection(); app.UseAuthorization(); diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs index 8674968..46dd821 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs @@ -37,7 +37,7 @@ private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription descrip { var info = new OpenApiInfo() { - Title = "ServiceName API", + Title = "API Documentation", Version = description.ApiVersion.ToString(), Description = "A sample application with Swagger, Swashbuckle, and API versioning.", Contact = new OpenApiContact() { Name = "Leandro", Email = "leandro@somewhere.com" }, diff --git a/MicroserviceTemplate/src/ServiceName.API/Program.cs b/MicroserviceTemplate/src/ServiceName.API/Program.cs index 2925f4f..e117fe7 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Program.cs +++ b/MicroserviceTemplate/src/ServiceName.API/Program.cs @@ -12,7 +12,7 @@ // package will act as the webserver translating request and responses between the Lambda event source and ASP.NET Core. // https://aws.amazon.com/blogs/compute/introducing-the-net-6-runtime-for-aws-lambda/ -builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi); +builder.Services.AddAWSLambdaHosting(LambdaEventSource.HttpApi); builder.Services.AddInfrastructureServices(builder.Configuration); @@ -28,7 +28,7 @@ builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddHostedService(); +//builder.Services.AddHostedService(); var app = builder.Build(); diff --git a/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj b/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj index aa28b3e..048f9df 100644 --- a/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj +++ b/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj @@ -20,7 +20,7 @@ - + diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs b/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs index b7d54ad..0af2a80 100644 --- a/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs @@ -19,7 +19,9 @@ Console.WriteLine("Cleaning up docker containers..."); Helper.KillProcess("tye"); //TODO: Add all processes here -Helper.KillProcess("FeatureFlag.API"); +Helper.KillProcess("ServiceName.API"); +Helper.KillProcess("Authentication.API"); +Helper.KillProcess("FeatureManagement.API"); Helper.KillProcess("Mock.API"); Helper.RunPowerShellCommand(@"docker kill $(docker ps -q)"); Helper.RunPowerShellCommand(@"docker rm --force $(docker ps -a -q)"); diff --git a/MicroserviceTemplate/tye.yaml b/MicroserviceTemplate/tye.yaml index 6838ff0..fc974b0 100644 --- a/MicroserviceTemplate/tye.yaml +++ b/MicroserviceTemplate/tye.yaml @@ -25,7 +25,7 @@ services: - name: servicename-api project: src/ServiceName.API/ServiceName.API.csproj bindings: - - port: 51002 + - port: 51005 protocol: https #replicas: 2 @@ -36,8 +36,8 @@ services: protocol: https #replicas: 2 - - name: featureflag-api - project: src/FeatureFlag.API/FeatureFlag.API.csproj + - name: featuremanagement-api + project: src/FeatureManagement.API/FeatureManagement.API.csproj bindings: - port: 51004 protocol: https @@ -85,7 +85,7 @@ services: - name: KMS_REGION value: "ap-southeast-2" - name: KMS_SEED_PATH - value: "/mnt/c/Dev/HF-Services/local-kms-seed.yaml" + value: "/mnt/c/Dev/local-kms-seed.yaml" - name: Cognito image: jagregory/cognito-local:latest From 6c6d8ff468b32dd9dc6416edb1a98fd8ada302d0 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Fri, 26 Aug 2022 10:01:57 +1000 Subject: [PATCH 46/56] Added Analytics API Moved Extensions to its own project --- MicroserviceTemplate/All.sln | 14 ++++ .../src/Analytics.API/Analytics.API.csproj | 20 ++++++ .../src/Analytics.API/Program.cs | 36 ++++++++++ .../Properties/launchSettings.json | 18 +++++ .../src/Analytics.API/Readme.md | 51 ++++++++++++++ .../appsettings.Development.json | 8 +++ .../src/Analytics.API/appsettings.json | 9 +++ .../aws-lambda-tools-defaults.json | 14 ++++ .../src/Analytics.API/serverless.template | 47 +++++++++++++ .../Authentication.API.csproj | 1 + .../{Extensions => }/EndpointExtensions.cs | 2 +- .../Extensions/AuthExtension.cs | 38 ----------- .../Extensions/HealthCheckExtensions.cs | 60 ----------------- .../Swagger/SwaggerDefaultValues.cs | 48 ------------- .../Extensions/VersionExtension.cs | 21 ------ .../src/Authentication.API/Program.cs | 1 + .../Extensions/AuthenticationExtension.cs | 3 + .../Extensions/ExceptionHandlingExtension.cs | 2 + .../src/Extensions/Extensions.csproj | 23 +++++++ .../HealthCheck/DynamoDbHealthCheck.cs | 1 + .../Extensions/HealthCheckExtensions.cs | 3 + .../Swagger/ConfigureSwaggerOptions.cs | 0 .../Swagger/SwaggerDefaultValues.cs | 0 .../Extensions/SwaggerExtensions.cs | 4 +- .../Extensions/VersionExtension.cs | 4 +- .../Swagger/ConfigureSwaggerOptions.cs | 55 --------------- .../Swagger/SwaggerDefaultValues.cs | 48 ------------- .../Extensions/SwaggerExtensions.cs | 67 ------------------- .../Extensions/VersionExtension.cs | 21 ------ .../FeatureManagement.API.csproj | 4 ++ .../src/FeatureManagement.API/Program.cs | 2 +- .../Swagger/ConfigureSwaggerOptions.cs | 55 --------------- .../Swagger/SwaggerDefaultValues.cs | 48 ------------- .../Mock.API/Extensions/SwaggerExtensions.cs | 67 ------------------- .../Mock.API/Extensions/VersionExtension.cs | 21 ------ .../src/Mock.API/Mock.API.csproj | 3 + MicroserviceTemplate/src/Mock.API/Program.cs | 2 +- .../{Extensions => }/EndpointExtensions.cs | 0 .../HealthCheck/DynamoDbHealthCheck.cs | 50 -------------- .../Swagger/ConfigureSwaggerOptions.cs | 55 --------------- .../Extensions/SwaggerExtensions.cs | 67 ------------------- .../ServiceName.API/ServiceName.API.csproj | 1 + MicroserviceTemplate/tye.yaml | 7 ++ 43 files changed, 275 insertions(+), 726 deletions(-) create mode 100644 MicroserviceTemplate/src/Analytics.API/Analytics.API.csproj create mode 100644 MicroserviceTemplate/src/Analytics.API/Program.cs create mode 100644 MicroserviceTemplate/src/Analytics.API/Properties/launchSettings.json create mode 100644 MicroserviceTemplate/src/Analytics.API/Readme.md create mode 100644 MicroserviceTemplate/src/Analytics.API/appsettings.Development.json create mode 100644 MicroserviceTemplate/src/Analytics.API/appsettings.json create mode 100644 MicroserviceTemplate/src/Analytics.API/aws-lambda-tools-defaults.json create mode 100644 MicroserviceTemplate/src/Analytics.API/serverless.template rename MicroserviceTemplate/src/Authentication.API/{Extensions => }/EndpointExtensions.cs (97%) delete mode 100644 MicroserviceTemplate/src/Authentication.API/Extensions/AuthExtension.cs delete mode 100644 MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheckExtensions.cs delete mode 100644 MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/SwaggerDefaultValues.cs delete mode 100644 MicroserviceTemplate/src/Authentication.API/Extensions/VersionExtension.cs rename MicroserviceTemplate/src/{ServiceName.API => }/Extensions/AuthenticationExtension.cs (92%) rename MicroserviceTemplate/src/{ServiceName.API => }/Extensions/ExceptionHandlingExtension.cs (91%) create mode 100644 MicroserviceTemplate/src/Extensions/Extensions.csproj rename MicroserviceTemplate/src/{Authentication.API => }/Extensions/HealthCheck/DynamoDbHealthCheck.cs (97%) rename MicroserviceTemplate/src/{ServiceName.API => }/Extensions/HealthCheckExtensions.cs (95%) rename MicroserviceTemplate/src/{Authentication.API => }/Extensions/Swagger/ConfigureSwaggerOptions.cs (100%) rename MicroserviceTemplate/src/{ServiceName.API => }/Extensions/Swagger/SwaggerDefaultValues.cs (100%) rename MicroserviceTemplate/src/{Authentication.API => }/Extensions/SwaggerExtensions.cs (94%) rename MicroserviceTemplate/src/{ServiceName.API => }/Extensions/VersionExtension.cs (91%) delete mode 100644 MicroserviceTemplate/src/FeatureManagement.API/Extensions/Swagger/ConfigureSwaggerOptions.cs delete mode 100644 MicroserviceTemplate/src/FeatureManagement.API/Extensions/Swagger/SwaggerDefaultValues.cs delete mode 100644 MicroserviceTemplate/src/FeatureManagement.API/Extensions/SwaggerExtensions.cs delete mode 100644 MicroserviceTemplate/src/FeatureManagement.API/Extensions/VersionExtension.cs delete mode 100644 MicroserviceTemplate/src/Mock.API/Extensions/Swagger/ConfigureSwaggerOptions.cs delete mode 100644 MicroserviceTemplate/src/Mock.API/Extensions/Swagger/SwaggerDefaultValues.cs delete mode 100644 MicroserviceTemplate/src/Mock.API/Extensions/SwaggerExtensions.cs delete mode 100644 MicroserviceTemplate/src/Mock.API/Extensions/VersionExtension.cs rename MicroserviceTemplate/src/ServiceName.API/{Extensions => }/EndpointExtensions.cs (100%) delete mode 100644 MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs delete mode 100644 MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs diff --git a/MicroserviceTemplate/All.sln b/MicroserviceTemplate/All.sln index 7614d68..ae19ec9 100644 --- a/MicroserviceTemplate/All.sln +++ b/MicroserviceTemplate/All.sln @@ -43,6 +43,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{779C3979-9 docs\README.md = docs\README.md EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Analytics.API", "src\Analytics.API\Analytics.API.csproj", "{04DA91BB-6AE0-4FAA-8008-2A398AFB52FA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Extensions", "src\Extensions\Extensions.csproj", "{6431BE6D-16FA-4259-8CFB-40698C43C37E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -89,6 +93,14 @@ Global {0349212E-D1A7-41FB-97E2-4DADCB7B5265}.Debug|Any CPU.Build.0 = Debug|Any CPU {0349212E-D1A7-41FB-97E2-4DADCB7B5265}.Release|Any CPU.ActiveCfg = Release|Any CPU {0349212E-D1A7-41FB-97E2-4DADCB7B5265}.Release|Any CPU.Build.0 = Release|Any CPU + {04DA91BB-6AE0-4FAA-8008-2A398AFB52FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04DA91BB-6AE0-4FAA-8008-2A398AFB52FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04DA91BB-6AE0-4FAA-8008-2A398AFB52FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04DA91BB-6AE0-4FAA-8008-2A398AFB52FA}.Release|Any CPU.Build.0 = Release|Any CPU + {6431BE6D-16FA-4259-8CFB-40698C43C37E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6431BE6D-16FA-4259-8CFB-40698C43C37E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6431BE6D-16FA-4259-8CFB-40698C43C37E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6431BE6D-16FA-4259-8CFB-40698C43C37E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -104,6 +116,8 @@ Global {F9885267-A919-4711-B9AF-9C6ADEC4DA21} = {462D80EC-4E6B-4B0F-8062-779007AB3A90} {FBEEAEA7-7A4E-4D97-9947-C3375A1F8217} = {3FD41DDF-2B43-4372-9FB1-62597803431B} {0349212E-D1A7-41FB-97E2-4DADCB7B5265} = {3FD41DDF-2B43-4372-9FB1-62597803431B} + {04DA91BB-6AE0-4FAA-8008-2A398AFB52FA} = {3FD41DDF-2B43-4372-9FB1-62597803431B} + {6431BE6D-16FA-4259-8CFB-40698C43C37E} = {3FD41DDF-2B43-4372-9FB1-62597803431B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C9A3A709-C3C6-4654-AC2B-A92420A60DF5} diff --git a/MicroserviceTemplate/src/Analytics.API/Analytics.API.csproj b/MicroserviceTemplate/src/Analytics.API/Analytics.API.csproj new file mode 100644 index 0000000..599aa62 --- /dev/null +++ b/MicroserviceTemplate/src/Analytics.API/Analytics.API.csproj @@ -0,0 +1,20 @@ + + + net6.0 + enable + enable + true + Lambda + + true + + true + + + + + + + + + \ No newline at end of file diff --git a/MicroserviceTemplate/src/Analytics.API/Program.cs b/MicroserviceTemplate/src/Analytics.API/Program.cs new file mode 100644 index 0000000..2461674 --- /dev/null +++ b/MicroserviceTemplate/src/Analytics.API/Program.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Mvc; +using Segment; +using Segment.Model; +using ServiceName.API.Extensions; + +var builder = WebApplication.CreateBuilder(args); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerSupport(); +builder.Services.AddApiVersioningSupport(); + +// Add services to the container. +builder.Services.AddControllers(); + +// Add AWS Lambda support. When application is run in Lambda Kestrel is swapped out as the web server with Amazon.Lambda.AspNetCoreServer. This +// package will act as the webserver translating request and responses between the Lambda event source and ASP.NET Core. +builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi); + +var app = builder.Build(); +if (app.Environment.IsDevelopment()) +{ + app.ConfigureSwaggerUI(); +} + + +app.UseHttpsRedirection(); +app.UseAuthorization(); +app.MapControllers(); + +app.MapPost("/{userId}/{action}", (string userId, string action, [FromBody] Traits traits) => +{ + var client = new Client("your project's write key"); + client.Identify(userId, traits); + client.Track(userId, action); +}); + +app.Run(); diff --git a/MicroserviceTemplate/src/Analytics.API/Properties/launchSettings.json b/MicroserviceTemplate/src/Analytics.API/Properties/launchSettings.json new file mode 100644 index 0000000..b753c1e --- /dev/null +++ b/MicroserviceTemplate/src/Analytics.API/Properties/launchSettings.json @@ -0,0 +1,18 @@ +{ + "profiles": { + "Analytics.API": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:54691;http://localhost:54692" + }, + "Mock Lambda Test Tool": { + "commandName": "Executable", + "commandLineArgs": "--port 5050", + "workingDirectory": ".\\bin\\$(Configuration)\\net6.0", + "executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe" + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Analytics.API/Readme.md b/MicroserviceTemplate/src/Analytics.API/Readme.md new file mode 100644 index 0000000..aea408c --- /dev/null +++ b/MicroserviceTemplate/src/Analytics.API/Readme.md @@ -0,0 +1,51 @@ +# ASP.NET Core Minimal API Serverless Application + +This project shows how to run an ASP.NET Core Web API project as an AWS Lambda exposed through Amazon API Gateway. The NuGet package [Amazon.Lambda.AspNetCoreServer](https://www.nuget.org/packages/Amazon.Lambda.AspNetCoreServer) contains a Lambda function that is used to translate requests from API Gateway into the ASP.NET Core framework and then the responses from ASP.NET Core back to API Gateway. + + +For more information about how the Amazon.Lambda.AspNetCoreServer package works and how to extend its behavior view its [README](https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.AspNetCoreServer/README.md) file in GitHub. + +## Executable Assembly ## + +.NET Lambda projects that use C# top level statements like this project must be deployed as an executable assembly instead of a class library. To indicate to Lambda that the .NET function is an executable assembly the +Lambda function handler value is set to the .NET Assembly name. This is different then deploying as a class library where the function handler string includes the assembly, type and method name. + +To deploy as an executable assembly the Lambda runtime client must be started to listen for incoming events to process. For an ASP.NET Core application the Lambda runtime client is started by included the +`Amazon.Lambda.AspNetCoreServer.Hosting` NuGet package and calling `AddAWSLambdaHosting(LambdaEventSource.HttpApi)` passing in the event source while configuring the services of the application. The +event source can be API Gateway REST API and HTTP API or Application Load Balancer. + +### Project Files ### + +* serverless.template - an AWS CloudFormation Serverless Application Model template file for declaring your Serverless functions and other AWS resources +* aws-lambda-tools-defaults.json - default argument settings for use with Visual Studio and command line deployment tools for AWS +* Program.cs - entry point to the application that contains all of the top level statements initializing the ASP.NET Core application. +The call to `AddAWSLambdaHosting` configures the application to work in Lambda when it detects Lambda is the executing environment. +* Controllers\CalculatorController - example Web API controller + +You may also have a test project depending on the options selected. + +## Here are some steps to follow from Visual Studio: + +To deploy your Serverless application, right click the project in Solution Explorer and select *Publish to AWS Lambda*. + +To view your deployed application open the Stack View window by double-clicking the stack name shown beneath the AWS CloudFormation node in the AWS Explorer tree. The Stack View also displays the root URL to your published application. + +## Here are some steps to follow to get started from the command line: + +Once you have edited your template and code you can deploy your application using the [Amazon.Lambda.Tools Global Tool](https://github.com/aws/aws-extensions-for-dotnet-cli#aws-lambda-amazonlambdatools) from the command line. + +Install Amazon.Lambda.Tools Global Tools if not already installed. +``` + dotnet tool install -g Amazon.Lambda.Tools +``` + +If already installed check if new version is available. +``` + dotnet tool update -g Amazon.Lambda.Tools +``` + +Deploy application +``` + cd "Analytics.API/src/Analytics.API" + dotnet lambda deploy-serverless +``` diff --git a/MicroserviceTemplate/src/Analytics.API/appsettings.Development.json b/MicroserviceTemplate/src/Analytics.API/appsettings.Development.json new file mode 100644 index 0000000..1b2d3ba --- /dev/null +++ b/MicroserviceTemplate/src/Analytics.API/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Analytics.API/appsettings.json b/MicroserviceTemplate/src/Analytics.API/appsettings.json new file mode 100644 index 0000000..ec04bc1 --- /dev/null +++ b/MicroserviceTemplate/src/Analytics.API/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Analytics.API/aws-lambda-tools-defaults.json b/MicroserviceTemplate/src/Analytics.API/aws-lambda-tools-defaults.json new file mode 100644 index 0000000..08d5472 --- /dev/null +++ b/MicroserviceTemplate/src/Analytics.API/aws-lambda-tools-defaults.json @@ -0,0 +1,14 @@ +{ + "Information": [ + "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", + "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", + "dotnet lambda help", + "All the command line options for the Lambda command can be specified in this file." + ], + "profile": "", + "region": "", + "configuration": "Release", + "s3-prefix": "Analytics.API/", + "template": "serverless.template", + "template-parameters": "" +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Analytics.API/serverless.template b/MicroserviceTemplate/src/Analytics.API/serverless.template new file mode 100644 index 0000000..0ec236f --- /dev/null +++ b/MicroserviceTemplate/src/Analytics.API/serverless.template @@ -0,0 +1,47 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Transform": "AWS::Serverless-2016-10-31", + "Description": "An AWS Serverless Application that uses the ASP.NET Core framework running in Amazon Lambda.", + "Parameters": {}, + "Conditions": {}, + "Resources": { + "AspNetCoreFunction": { + "Type": "AWS::Serverless::Function", + "Properties": { + "Handler": "Analytics.API", + "Runtime": "dotnet6", + "CodeUri": "", + "MemorySize": 256, + "Timeout": 30, + "Role": null, + "Policies": [ + "AWSLambda_FullAccess" + ], + "Events": { + "ProxyResource": { + "Type": "Api", + "Properties": { + "Path": "/{proxy+}", + "Method": "ANY" + } + }, + "RootResource": { + "Type": "Api", + "Properties": { + "Path": "/", + "Method": "ANY" + } + } + } + } + } + }, + "Outputs": { + "ApiURL": { + "Description": "API endpoint URL for Prod environment", + "Value": { + "Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" + } + } + } +} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Authentication.API.csproj b/MicroserviceTemplate/src/Authentication.API/Authentication.API.csproj index 048f9df..eb9cef1 100644 --- a/MicroserviceTemplate/src/Authentication.API/Authentication.API.csproj +++ b/MicroserviceTemplate/src/Authentication.API/Authentication.API.csproj @@ -23,6 +23,7 @@ + \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/EndpointExtensions.cs b/MicroserviceTemplate/src/Authentication.API/EndpointExtensions.cs similarity index 97% rename from MicroserviceTemplate/src/Authentication.API/Extensions/EndpointExtensions.cs rename to MicroserviceTemplate/src/Authentication.API/EndpointExtensions.cs index 797130c..60d9274 100644 --- a/MicroserviceTemplate/src/Authentication.API/Extensions/EndpointExtensions.cs +++ b/MicroserviceTemplate/src/Authentication.API/EndpointExtensions.cs @@ -3,7 +3,7 @@ using ServiceName.Core.Common.Interfaces; using ServiceName.Core.Model; -namespace ServiceName.API.Extensions +namespace Authentication.API { public static class EndpointExtensions { diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/AuthExtension.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/AuthExtension.cs deleted file mode 100644 index e73ccac..0000000 --- a/MicroserviceTemplate/src/Authentication.API/Extensions/AuthExtension.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.IdentityModel.Tokens; -using ServiceName.Core.Common.Security; - -namespace ServiceName.API.Extensions -{ - public static class SecurityExtension - { - public static void AddAuthSupport(this IServiceCollection services, ConfigurationManager configurationManager) - { - services.AddAuthentication(o => - { - o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; - o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; - }).AddJwtBearer(o => - { - o.TokenValidationParameters = new TokenValidationParameters - { - ValidateIssuerSigningKey = true, - ValidateIssuer = true, - ValidateAudience = true, - ValidIssuer = configurationManager["ModuleConfiguration:Jwt:Issuer"], - ValidAudience = configurationManager["ModuleConfiguration:Jwt:Audience"], - IssuerSigningKey = SecurityHelper.GetRsaSecurityKey(configurationManager["ModuleConfiguration:Infrastructure:Kms:PublicKey"]) - }; - }); - - services.AddAuthorization(); - } - - public static void UseAuth(this WebApplication app) - { - app.UseAuthentication(); - app.UseAuthorization(); - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheckExtensions.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheckExtensions.cs deleted file mode 100644 index 615713a..0000000 --- a/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheckExtensions.cs +++ /dev/null @@ -1,60 +0,0 @@ -using HealthChecks.UI.Client; -using Microsoft.AspNetCore.Diagnostics.HealthChecks; -using ServiceName.API.Extensions.HealthCheck; - -namespace ServiceName.API.Extensions -{ - /// - /// https://docs.steeltoe.io/api/v3/management/health.html - /// https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-6.0 - /// https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/monitor-app-health - /// https://blog.zhaytam.com/2020/04/30/health-checks-aspnetcore/ - - //AspNetCore.HealthChecks.UI which adds the UI. - //AspNetCore.HealthChecks.UI.Client which turns our old response(e.g.Healthy) into a more detailed response. - //AspNetCore.HealthChecks.UI.InMemory.Storage which saves the results in memory for the UI to use. - - /// - public static class HealthCheckExtensions - { - public static void AddHealthCheckSupport(this IServiceCollection services, ConfigurationManager configurationManager) - { - var sqlServerName = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Server"]; - var sqlPort = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Port"]; - var sqlDatabase = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Database"]; - var sqlUsername = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Username"]; - var sqlPassword = configurationManager["ModuleConfiguration:Infrastructure:SqlServer:Password"]; - - var sqlConnectionString = $"Data Source={sqlServerName},{sqlPort};Initial Catalog={sqlDatabase};User ID={sqlUsername};Password={sqlPassword}"; - - var redisServerName = configurationManager["ModuleConfiguration:Infrastructure:Redis:Server"]; - var redisPort = configurationManager["ModuleConfiguration:Infrastructure:Redis:Port"]; - - var redisConnectionString = $"{redisServerName}:{redisPort}"; - - services.AddHealthChecks() - .AddCheck("dynamodb") - .AddSqlServer(sqlConnectionString) - .AddRedis(redisConnectionString); - - services.AddHealthChecksUI(s => - { - s.AddHealthCheckEndpoint("ServiceName", "/health"); - }).AddInMemoryStorage(); - } - - public static void ConfigureHealthCheck(this WebApplication app) - { - app.UseHealthChecksUI(config => config.UIPath = "/health-ui"); - - app.MapHealthChecksUI(); - - app.MapHealthChecks("/health", new HealthCheckOptions() - { - Predicate = _ => true, - ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse, - AllowCachingResponses = false - }); - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/SwaggerDefaultValues.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/SwaggerDefaultValues.cs deleted file mode 100644 index 1a29ec6..0000000 --- a/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/SwaggerDefaultValues.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace ServiceName.API.Extensions.Swagger -{ - using System.Linq; - using Microsoft.AspNetCore.Mvc.ApiExplorer; - using Microsoft.OpenApi.Any; - using Microsoft.OpenApi.Models; - using Swashbuckle.AspNetCore.SwaggerGen; - - /// - /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter. - /// - /// This is only required due to bugs in the . - /// Once they are fixed and published, this class can be removed. - public class SwaggerDefaultValues : IOperationFilter - { - /// - /// Applies the filter to the specified operation using the given context. - /// - /// The operation to apply the filter to. - /// The current operation filter context. - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - var apiDescription = context.ApiDescription; - operation.Deprecated |= apiDescription.IsDeprecated(); - - if (operation.Parameters == null) - return; - - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412 - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413 - foreach (var parameter in operation.Parameters) - { - var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); - if (parameter.Description == null) - { - parameter.Description = description.ModelMetadata?.Description; - } - - if (parameter.Schema.Default == null && description.DefaultValue != null) - { - parameter.Schema.Default = new OpenApiString(description.DefaultValue.ToString()); - } - - parameter.Required |= description.IsRequired; - } - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/VersionExtension.cs b/MicroserviceTemplate/src/Authentication.API/Extensions/VersionExtension.cs deleted file mode 100644 index 8badf2e..0000000 --- a/MicroserviceTemplate/src/Authentication.API/Extensions/VersionExtension.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace ServiceName.API.Extensions -{ - public static class VersionExtension - { - public static void AddApiVersioningSupport(this IServiceCollection services) - { - services.AddApiVersioning() - .AddApiExplorer( - options => - { - // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service - // note: the specified format code will format the version as "'v'major[.minor][-status]" - options.GroupNameFormat = "'v'VVV"; - - // note: this option is only necessary when versioning by url segment. the SubstitutionFormat - // can also be used to control the format of the API version in route templates - options.SubstituteApiVersionInUrl = true; - }); - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Authentication.API/Program.cs b/MicroserviceTemplate/src/Authentication.API/Program.cs index 837dd8f..1771e32 100644 --- a/MicroserviceTemplate/src/Authentication.API/Program.cs +++ b/MicroserviceTemplate/src/Authentication.API/Program.cs @@ -1,3 +1,4 @@ +using Authentication.API; using ServiceName.API.Extensions; using ServiceName.Core; using ServiceName.Infrastructure; diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthenticationExtension.cs b/MicroserviceTemplate/src/Extensions/AuthenticationExtension.cs similarity index 92% rename from MicroserviceTemplate/src/ServiceName.API/Extensions/AuthenticationExtension.cs rename to MicroserviceTemplate/src/Extensions/AuthenticationExtension.cs index 090b464..8d2a7d2 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/AuthenticationExtension.cs +++ b/MicroserviceTemplate/src/Extensions/AuthenticationExtension.cs @@ -1,4 +1,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using ServiceName.Core.Common.Security; diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/ExceptionHandlingExtension.cs b/MicroserviceTemplate/src/Extensions/ExceptionHandlingExtension.cs similarity index 91% rename from MicroserviceTemplate/src/ServiceName.API/Extensions/ExceptionHandlingExtension.cs rename to MicroserviceTemplate/src/Extensions/ExceptionHandlingExtension.cs index df5c9b8..ac4058a 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/ExceptionHandlingExtension.cs +++ b/MicroserviceTemplate/src/Extensions/ExceptionHandlingExtension.cs @@ -1,4 +1,6 @@ using System.Net; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; namespace ServiceName.API.Extensions { diff --git a/MicroserviceTemplate/src/Extensions/Extensions.csproj b/MicroserviceTemplate/src/Extensions/Extensions.csproj new file mode 100644 index 0000000..4e28787 --- /dev/null +++ b/MicroserviceTemplate/src/Extensions/Extensions.csproj @@ -0,0 +1,23 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + + + + + diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs b/MicroserviceTemplate/src/Extensions/HealthCheck/DynamoDbHealthCheck.cs similarity index 97% rename from MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs rename to MicroserviceTemplate/src/Extensions/HealthCheck/DynamoDbHealthCheck.cs index 12e6296..7993837 100644 --- a/MicroserviceTemplate/src/Authentication.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs +++ b/MicroserviceTemplate/src/Extensions/HealthCheck/DynamoDbHealthCheck.cs @@ -1,6 +1,7 @@ using Amazon; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.DocumentModel; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Diagnostics.HealthChecks; namespace ServiceName.API.Extensions.HealthCheck diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs b/MicroserviceTemplate/src/Extensions/HealthCheckExtensions.cs similarity index 95% rename from MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs rename to MicroserviceTemplate/src/Extensions/HealthCheckExtensions.cs index 615713a..5415f3a 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheckExtensions.cs +++ b/MicroserviceTemplate/src/Extensions/HealthCheckExtensions.cs @@ -1,5 +1,8 @@ using HealthChecks.UI.Client; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using ServiceName.API.Extensions.HealthCheck; namespace ServiceName.API.Extensions diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/ConfigureSwaggerOptions.cs b/MicroserviceTemplate/src/Extensions/Swagger/ConfigureSwaggerOptions.cs similarity index 100% rename from MicroserviceTemplate/src/Authentication.API/Extensions/Swagger/ConfigureSwaggerOptions.cs rename to MicroserviceTemplate/src/Extensions/Swagger/ConfigureSwaggerOptions.cs diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/SwaggerDefaultValues.cs b/MicroserviceTemplate/src/Extensions/Swagger/SwaggerDefaultValues.cs similarity index 100% rename from MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/SwaggerDefaultValues.cs rename to MicroserviceTemplate/src/Extensions/Swagger/SwaggerDefaultValues.cs diff --git a/MicroserviceTemplate/src/Authentication.API/Extensions/SwaggerExtensions.cs b/MicroserviceTemplate/src/Extensions/SwaggerExtensions.cs similarity index 94% rename from MicroserviceTemplate/src/Authentication.API/Extensions/SwaggerExtensions.cs rename to MicroserviceTemplate/src/Extensions/SwaggerExtensions.cs index d4bd87d..afa60e0 100644 --- a/MicroserviceTemplate/src/Authentication.API/Extensions/SwaggerExtensions.cs +++ b/MicroserviceTemplate/src/Extensions/SwaggerExtensions.cs @@ -1,4 +1,6 @@ -using Microsoft.Extensions.Options; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using ServiceName.API.Extensions.Swagger; using Swashbuckle.AspNetCore.SwaggerGen; diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/VersionExtension.cs b/MicroserviceTemplate/src/Extensions/VersionExtension.cs similarity index 91% rename from MicroserviceTemplate/src/ServiceName.API/Extensions/VersionExtension.cs rename to MicroserviceTemplate/src/Extensions/VersionExtension.cs index 8badf2e..b826ba3 100644 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/VersionExtension.cs +++ b/MicroserviceTemplate/src/Extensions/VersionExtension.cs @@ -1,4 +1,6 @@ -namespace ServiceName.API.Extensions +using Microsoft.Extensions.DependencyInjection; + +namespace ServiceName.API.Extensions { public static class VersionExtension { diff --git a/MicroserviceTemplate/src/FeatureManagement.API/Extensions/Swagger/ConfigureSwaggerOptions.cs b/MicroserviceTemplate/src/FeatureManagement.API/Extensions/Swagger/ConfigureSwaggerOptions.cs deleted file mode 100644 index b97b1cd..0000000 --- a/MicroserviceTemplate/src/FeatureManagement.API/Extensions/Swagger/ConfigureSwaggerOptions.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace FeatureManagement.API.Extensions.Swagger -{ - using System; - using Asp.Versioning.ApiExplorer; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Options; - using Microsoft.OpenApi.Models; - using Swashbuckle.AspNetCore.SwaggerGen; - - /// - /// Configures the Swagger generation options. - /// - /// This allows API versioning to define a Swagger document per API version after the - /// service has been resolved from the service container. - public class ConfigureSwaggerOptions : IConfigureOptions - { - private readonly IApiVersionDescriptionProvider provider; - - /// - /// Initializes a new instance of the class. - /// - /// The provider used to generate Swagger documents. - public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => this.provider = provider; - - /// - public void Configure(SwaggerGenOptions options) - { - // add a swagger document for each discovered API version - // note: you might choose to skip or document deprecated API versions differently - foreach (var description in provider.ApiVersionDescriptions) - { - options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); - } - } - - private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) - { - var info = new OpenApiInfo() - { - Title = "API Documentation", - Version = description.ApiVersion.ToString(), - Description = "A sample application with Swagger, Swashbuckle, and API versioning.", - Contact = new OpenApiContact() { Name = "Leandro", Email = "leandro@somewhere.com" }, - License = new OpenApiLicense() { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") } - }; - - if (description.IsDeprecated) - { - info.Description += " This API version has been deprecated."; - } - - return info; - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/FeatureManagement.API/Extensions/Swagger/SwaggerDefaultValues.cs b/MicroserviceTemplate/src/FeatureManagement.API/Extensions/Swagger/SwaggerDefaultValues.cs deleted file mode 100644 index c958d6f..0000000 --- a/MicroserviceTemplate/src/FeatureManagement.API/Extensions/Swagger/SwaggerDefaultValues.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace FeatureManagement.API.Extensions.Swagger -{ - using System.Linq; - using Microsoft.AspNetCore.Mvc.ApiExplorer; - using Microsoft.OpenApi.Any; - using Microsoft.OpenApi.Models; - using Swashbuckle.AspNetCore.SwaggerGen; - - /// - /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter. - /// - /// This is only required due to bugs in the . - /// Once they are fixed and published, this class can be removed. - public class SwaggerDefaultValues : IOperationFilter - { - /// - /// Applies the filter to the specified operation using the given context. - /// - /// The operation to apply the filter to. - /// The current operation filter context. - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - var apiDescription = context.ApiDescription; - operation.Deprecated |= apiDescription.IsDeprecated(); - - if (operation.Parameters == null) - return; - - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412 - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413 - foreach (var parameter in operation.Parameters) - { - var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); - if (parameter.Description == null) - { - parameter.Description = description.ModelMetadata?.Description; - } - - if (parameter.Schema.Default == null && description.DefaultValue != null) - { - parameter.Schema.Default = new OpenApiString(description.DefaultValue.ToString()); - } - - parameter.Required |= description.IsRequired; - } - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/FeatureManagement.API/Extensions/SwaggerExtensions.cs b/MicroserviceTemplate/src/FeatureManagement.API/Extensions/SwaggerExtensions.cs deleted file mode 100644 index 68e4dd9..0000000 --- a/MicroserviceTemplate/src/FeatureManagement.API/Extensions/SwaggerExtensions.cs +++ /dev/null @@ -1,67 +0,0 @@ -using FeatureManagement.API.Extensions.Swagger; -using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace FeatureManagement.API.Extensions -{ - public static class SwaggerExtensions - { - public static void AddSwaggerSupport(this IServiceCollection services) - { - services.AddTransient, ConfigureSwaggerOptions>(); - - services.AddSwaggerGen(options => - { - options.OperationFilter(); - - var securityScheme = new OpenApiSecurityScheme() - { - Name = "Authorization", - Type = SecuritySchemeType.ApiKey, - Scheme = "Bearer", - BearerFormat = "JWT", - In = ParameterLocation.Header, - Description = "JSON Web Token based security", - }; - - var securityReq = new OpenApiSecurityRequirement() - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = "Bearer" - } - }, - new string[] {} - } - }; - - options.AddSecurityDefinition("Bearer", securityScheme); - options.AddSecurityRequirement(securityReq); - }); - } - - public static void ConfigureSwaggerUI(this WebApplication app) - { - app.UseSwagger(); - app.UseSwaggerUI(options => - { - options.RoutePrefix = string.Empty; - - var descriptions = app.DescribeApiVersions(); - - // build a swagger endpoint for each discovered API version - foreach (var description in descriptions) - { - var url = $"./swagger/{description.GroupName}/swagger.json"; - var name = description.GroupName.ToUpperInvariant(); - options.SwaggerEndpoint(url, name); - } - }); - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/FeatureManagement.API/Extensions/VersionExtension.cs b/MicroserviceTemplate/src/FeatureManagement.API/Extensions/VersionExtension.cs deleted file mode 100644 index 668f909..0000000 --- a/MicroserviceTemplate/src/FeatureManagement.API/Extensions/VersionExtension.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace FeatureManagement.API.Extensions -{ - public static class VersionExtension - { - public static void AddApiVersioningSupport(this IServiceCollection services) - { - services.AddApiVersioning() - .AddApiExplorer( - options => - { - // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service - // note: the specified format code will format the version as "'v'major[.minor][-status]" - options.GroupNameFormat = "'v'VVV"; - - // note: this option is only necessary when versioning by url segment. the SubstitutionFormat - // can also be used to control the format of the API version in route templates - options.SubstituteApiVersionInUrl = true; - }); - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/FeatureManagement.API/FeatureManagement.API.csproj b/MicroserviceTemplate/src/FeatureManagement.API/FeatureManagement.API.csproj index ffbab8b..447eb98 100644 --- a/MicroserviceTemplate/src/FeatureManagement.API/FeatureManagement.API.csproj +++ b/MicroserviceTemplate/src/FeatureManagement.API/FeatureManagement.API.csproj @@ -14,6 +14,10 @@ + + + + Always diff --git a/MicroserviceTemplate/src/FeatureManagement.API/Program.cs b/MicroserviceTemplate/src/FeatureManagement.API/Program.cs index bb1c83c..2e38dbb 100644 --- a/MicroserviceTemplate/src/FeatureManagement.API/Program.cs +++ b/MicroserviceTemplate/src/FeatureManagement.API/Program.cs @@ -1,8 +1,8 @@ using FeatureFlag.API.Configuration; -using FeatureManagement.API.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.FeatureManagement; using Microsoft.FeatureManagement.FeatureFilters; +using ServiceName.API.Extensions; var builder = WebApplication.CreateBuilder(args); diff --git a/MicroserviceTemplate/src/Mock.API/Extensions/Swagger/ConfigureSwaggerOptions.cs b/MicroserviceTemplate/src/Mock.API/Extensions/Swagger/ConfigureSwaggerOptions.cs deleted file mode 100644 index 79b57a1..0000000 --- a/MicroserviceTemplate/src/Mock.API/Extensions/Swagger/ConfigureSwaggerOptions.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace Mock.API.Extensions.Swagger -{ - using System; - using Asp.Versioning.ApiExplorer; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Options; - using Microsoft.OpenApi.Models; - using Swashbuckle.AspNetCore.SwaggerGen; - - /// - /// Configures the Swagger generation options. - /// - /// This allows API versioning to define a Swagger document per API version after the - /// service has been resolved from the service container. - public class ConfigureSwaggerOptions : IConfigureOptions - { - private readonly IApiVersionDescriptionProvider provider; - - /// - /// Initializes a new instance of the class. - /// - /// The provider used to generate Swagger documents. - public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => this.provider = provider; - - /// - public void Configure(SwaggerGenOptions options) - { - // add a swagger document for each discovered API version - // note: you might choose to skip or document deprecated API versions differently - foreach (var description in provider.ApiVersionDescriptions) - { - options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); - } - } - - private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) - { - var info = new OpenApiInfo() - { - Title = "API Documentation", - Version = description.ApiVersion.ToString(), - Description = "A sample application with Swagger, Swashbuckle, and API versioning.", - Contact = new OpenApiContact() { Name = "Leandro", Email = "leandro@somewhere.com" }, - License = new OpenApiLicense() { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") } - }; - - if (description.IsDeprecated) - { - info.Description += " This API version has been deprecated."; - } - - return info; - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Mock.API/Extensions/Swagger/SwaggerDefaultValues.cs b/MicroserviceTemplate/src/Mock.API/Extensions/Swagger/SwaggerDefaultValues.cs deleted file mode 100644 index a1285d1..0000000 --- a/MicroserviceTemplate/src/Mock.API/Extensions/Swagger/SwaggerDefaultValues.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace Mock.API.Extensions.Swagger -{ - using System.Linq; - using Microsoft.AspNetCore.Mvc.ApiExplorer; - using Microsoft.OpenApi.Any; - using Microsoft.OpenApi.Models; - using Swashbuckle.AspNetCore.SwaggerGen; - - /// - /// Represents the Swagger/Swashbuckle operation filter used to document the implicit API version parameter. - /// - /// This is only required due to bugs in the . - /// Once they are fixed and published, this class can be removed. - public class SwaggerDefaultValues : IOperationFilter - { - /// - /// Applies the filter to the specified operation using the given context. - /// - /// The operation to apply the filter to. - /// The current operation filter context. - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - var apiDescription = context.ApiDescription; - operation.Deprecated |= apiDescription.IsDeprecated(); - - if (operation.Parameters == null) - return; - - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/412 - // REF: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/413 - foreach (var parameter in operation.Parameters) - { - var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name); - if (parameter.Description == null) - { - parameter.Description = description.ModelMetadata?.Description; - } - - if (parameter.Schema.Default == null && description.DefaultValue != null) - { - parameter.Schema.Default = new OpenApiString(description.DefaultValue.ToString()); - } - - parameter.Required |= description.IsRequired; - } - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Mock.API/Extensions/SwaggerExtensions.cs b/MicroserviceTemplate/src/Mock.API/Extensions/SwaggerExtensions.cs deleted file mode 100644 index 6d08b80..0000000 --- a/MicroserviceTemplate/src/Mock.API/Extensions/SwaggerExtensions.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; -using Mock.API.Extensions.Swagger; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace Mock.API.Extensions -{ - public static class SwaggerExtensions - { - public static void AddSwaggerSupport(this IServiceCollection services) - { - services.AddTransient, ConfigureSwaggerOptions>(); - - services.AddSwaggerGen(options => - { - options.OperationFilter(); - - var securityScheme = new OpenApiSecurityScheme() - { - Name = "Authorization", - Type = SecuritySchemeType.ApiKey, - Scheme = "Bearer", - BearerFormat = "JWT", - In = ParameterLocation.Header, - Description = "JSON Web Token based security", - }; - - var securityReq = new OpenApiSecurityRequirement() - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = "Bearer" - } - }, - new string[] {} - } - }; - - options.AddSecurityDefinition("Bearer", securityScheme); - options.AddSecurityRequirement(securityReq); - }); - } - - public static void ConfigureSwaggerUI(this WebApplication app) - { - app.UseSwagger(); - app.UseSwaggerUI(options => - { - options.RoutePrefix = string.Empty; - - var descriptions = app.DescribeApiVersions(); - - // build a swagger endpoint for each discovered API version - foreach (var description in descriptions) - { - var url = $"./swagger/{description.GroupName}/swagger.json"; - var name = description.GroupName.ToUpperInvariant(); - options.SwaggerEndpoint(url, name); - } - }); - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Mock.API/Extensions/VersionExtension.cs b/MicroserviceTemplate/src/Mock.API/Extensions/VersionExtension.cs deleted file mode 100644 index 2183a2f..0000000 --- a/MicroserviceTemplate/src/Mock.API/Extensions/VersionExtension.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Mock.API.Extensions -{ - public static class VersionExtension - { - public static void AddApiVersioningSupport(this IServiceCollection services) - { - services.AddApiVersioning() - .AddApiExplorer( - options => - { - // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service - // note: the specified format code will format the version as "'v'major[.minor][-status]" - options.GroupNameFormat = "'v'VVV"; - - // note: this option is only necessary when versioning by url segment. the SubstitutionFormat - // can also be used to control the format of the API version in route templates - options.SubstituteApiVersionInUrl = true; - }); - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/Mock.API/Mock.API.csproj b/MicroserviceTemplate/src/Mock.API/Mock.API.csproj index ba401d6..3636fa3 100644 --- a/MicroserviceTemplate/src/Mock.API/Mock.API.csproj +++ b/MicroserviceTemplate/src/Mock.API/Mock.API.csproj @@ -16,6 +16,9 @@ + + + Always diff --git a/MicroserviceTemplate/src/Mock.API/Program.cs b/MicroserviceTemplate/src/Mock.API/Program.cs index 488f54b..2b01f37 100644 --- a/MicroserviceTemplate/src/Mock.API/Program.cs +++ b/MicroserviceTemplate/src/Mock.API/Program.cs @@ -1,7 +1,7 @@ using System.Text.Json; using Microsoft.AspNetCore.Mvc; using Mock.API.Model; -using Mock.API.Extensions; +using ServiceName.API.Extensions; var builder = WebApplication.CreateBuilder(args); diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/EndpointExtensions.cs similarity index 100% rename from MicroserviceTemplate/src/ServiceName.API/Extensions/EndpointExtensions.cs rename to MicroserviceTemplate/src/ServiceName.API/EndpointExtensions.cs diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs deleted file mode 100644 index 12e6296..0000000 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/HealthCheck/DynamoDbHealthCheck.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Amazon; -using Amazon.DynamoDBv2; -using Amazon.DynamoDBv2.DocumentModel; -using Microsoft.Extensions.Diagnostics.HealthChecks; - -namespace ServiceName.API.Extensions.HealthCheck -{ - public class DynamoDbHealthCheck : IHealthCheck - { - private readonly IConfiguration _configuration; - - public DynamoDbHealthCheck(IConfiguration configuration) - { - _configuration = configuration; - } - - public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) - { - try - { - var accessKey = _configuration["ModuleConfiguration:Infrastructure:DynamoDb:AccessKey"]; - var secretKey = _configuration["ModuleConfiguration:Infrastructure:DynamoDb:SecretKey"]; - var regionEndpoint = RegionEndpoint.GetBySystemName(_configuration["ModuleConfiguration:Infrastructure:DynamoDb:RegionEndpoint"]); - var tableName = _configuration["ModuleConfiguration:Infrastructure:DynamoDb:TableName"]; - var localTestEndpoint = _configuration["ModuleConfiguration:Infrastructure:DynamoDb:LocalTestEndpoint"]; - - AmazonDynamoDBConfig clientConfig = new() - { - RegionEndpoint = regionEndpoint, - }; - - if (!string.IsNullOrEmpty(localTestEndpoint)) - { - clientConfig.UseHttp = true; - clientConfig.ServiceURL = localTestEndpoint; - } - - var amazonDynamoDBClient = new AmazonDynamoDBClient(accessKey, secretKey, clientConfig); - - Table.LoadTable(amazonDynamoDBClient, tableName); - - return Task.FromResult(HealthCheckResult.Healthy($"Table {tableName} exists.")); - } - catch (Exception ex) - { - return Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus, ex.Message)); - } - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs deleted file mode 100644 index 46dd821..0000000 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/Swagger/ConfigureSwaggerOptions.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace ServiceName.API.Extensions.Swagger -{ - using System; - using Asp.Versioning.ApiExplorer; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Options; - using Microsoft.OpenApi.Models; - using Swashbuckle.AspNetCore.SwaggerGen; - - /// - /// Configures the Swagger generation options. - /// - /// This allows API versioning to define a Swagger document per API version after the - /// service has been resolved from the service container. - public class ConfigureSwaggerOptions : IConfigureOptions - { - private readonly IApiVersionDescriptionProvider provider; - - /// - /// Initializes a new instance of the class. - /// - /// The provider used to generate Swagger documents. - public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => this.provider = provider; - - /// - public void Configure(SwaggerGenOptions options) - { - // add a swagger document for each discovered API version - // note: you might choose to skip or document deprecated API versions differently - foreach (var description in provider.ApiVersionDescriptions) - { - options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description)); - } - } - - private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description) - { - var info = new OpenApiInfo() - { - Title = "API Documentation", - Version = description.ApiVersion.ToString(), - Description = "A sample application with Swagger, Swashbuckle, and API versioning.", - Contact = new OpenApiContact() { Name = "Leandro", Email = "leandro@somewhere.com" }, - License = new OpenApiLicense() { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") } - }; - - if (description.IsDeprecated) - { - info.Description += " This API version has been deprecated."; - } - - return info; - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs b/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs deleted file mode 100644 index d4bd87d..0000000 --- a/MicroserviceTemplate/src/ServiceName.API/Extensions/SwaggerExtensions.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; -using ServiceName.API.Extensions.Swagger; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace ServiceName.API.Extensions -{ - public static class SwaggerExtensions - { - public static void AddSwaggerSupport(this IServiceCollection services) - { - services.AddTransient, ConfigureSwaggerOptions>(); - - services.AddSwaggerGen(options => - { - options.OperationFilter(); - - var securityScheme = new OpenApiSecurityScheme() - { - Name = "Authorization", - Type = SecuritySchemeType.ApiKey, - Scheme = "Bearer", - BearerFormat = "JWT", - In = ParameterLocation.Header, - Description = "JSON Web Token based security", - }; - - var securityReq = new OpenApiSecurityRequirement() - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Type = ReferenceType.SecurityScheme, - Id = "Bearer" - } - }, - new string[] {} - } - }; - - options.AddSecurityDefinition("Bearer", securityScheme); - options.AddSecurityRequirement(securityReq); - }); - } - - public static void ConfigureSwaggerUI(this WebApplication app) - { - app.UseSwagger(); - app.UseSwaggerUI(options => - { - options.RoutePrefix = string.Empty; - - var descriptions = app.DescribeApiVersions(); - - // build a swagger endpoint for each discovered API version - foreach (var description in descriptions) - { - var url = $"./swagger/{description.GroupName}/swagger.json"; - var name = description.GroupName.ToUpperInvariant(); - options.SwaggerEndpoint(url, name); - } - }); - } - } -} \ No newline at end of file diff --git a/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj b/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj index 048f9df..eb9cef1 100644 --- a/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj +++ b/MicroserviceTemplate/src/ServiceName.API/ServiceName.API.csproj @@ -23,6 +23,7 @@ + \ No newline at end of file diff --git a/MicroserviceTemplate/tye.yaml b/MicroserviceTemplate/tye.yaml index fc974b0..76df951 100644 --- a/MicroserviceTemplate/tye.yaml +++ b/MicroserviceTemplate/tye.yaml @@ -43,6 +43,13 @@ services: protocol: https #replicas: 2 + - name: analytics-api + project: src/Analytics.API/Analytics.API.csproj + bindings: + - port: 51006 + protocol: https + #replicas: 2 + - name: SqlServer image: mcr.microsoft.com/mssql/server:2019-latest bindings: From 82467ef64b6854dfe5a0a968a7bc986b7c6739f1 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Fri, 26 Aug 2022 10:24:24 +1000 Subject: [PATCH 47/56] Added Secret Configuration for Analytics.API --- MicroserviceTemplate/src/Analytics.API/Analytics.API.csproj | 3 ++- MicroserviceTemplate/src/Analytics.API/Program.cs | 4 +++- MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/MicroserviceTemplate/src/Analytics.API/Analytics.API.csproj b/MicroserviceTemplate/src/Analytics.API/Analytics.API.csproj index 599aa62..1f47f29 100644 --- a/MicroserviceTemplate/src/Analytics.API/Analytics.API.csproj +++ b/MicroserviceTemplate/src/Analytics.API/Analytics.API.csproj @@ -1,4 +1,4 @@ - + net6.0 enable @@ -9,6 +9,7 @@ true true + 97044e34-8f68-4b40-9f11-f66af3c78a30 diff --git a/MicroserviceTemplate/src/Analytics.API/Program.cs b/MicroserviceTemplate/src/Analytics.API/Program.cs index 2461674..4b9777e 100644 --- a/MicroserviceTemplate/src/Analytics.API/Program.cs +++ b/MicroserviceTemplate/src/Analytics.API/Program.cs @@ -15,6 +15,8 @@ // package will act as the webserver translating request and responses between the Lambda event source and ASP.NET Core. builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi); +var segmentWriteKey = builder.Configuration["Segment:WriteKey"]; + var app = builder.Build(); if (app.Environment.IsDevelopment()) { @@ -28,7 +30,7 @@ app.MapPost("/{userId}/{action}", (string userId, string action, [FromBody] Traits traits) => { - var client = new Client("your project's write key"); + var client = new Client(segmentWriteKey); client.Identify(userId, traits); client.Track(userId, action); }); diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs b/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs index 0af2a80..a35b463 100644 --- a/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs @@ -22,6 +22,7 @@ Helper.KillProcess("ServiceName.API"); Helper.KillProcess("Authentication.API"); Helper.KillProcess("FeatureManagement.API"); +Helper.KillProcess("Analytics.API"); Helper.KillProcess("Mock.API"); Helper.RunPowerShellCommand(@"docker kill $(docker ps -q)"); Helper.RunPowerShellCommand(@"docker rm --force $(docker ps -a -q)"); From e6baed5d6094c249ea23f0fcd879b186a92914be Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Fri, 26 Aug 2022 10:48:32 +1000 Subject: [PATCH 48/56] Added TailwindUIKit Example --- TailwindUIKit/README.md | 2 + TailwindUIKit/Website/.browserslistrc | 16 + TailwindUIKit/Website/.editorconfig | 16 + TailwindUIKit/Website/.gitignore | 42 + TailwindUIKit/Website/.vscode/extensions.json | 4 + TailwindUIKit/Website/.vscode/launch.json | 20 + TailwindUIKit/Website/.vscode/tasks.json | 42 + TailwindUIKit/Website/README.md | 27 + TailwindUIKit/Website/angular.json | 102 + TailwindUIKit/Website/karma.conf.js | 44 + TailwindUIKit/Website/package-lock.json | 20213 ++++++++++++++++ TailwindUIKit/Website/package.json | 39 + .../Website/src/app/app.component.css | 0 .../Website/src/app/app.component.html | 4 + .../Website/src/app/app.component.spec.ts | 31 + .../Website/src/app/app.component.ts | 10 + TailwindUIKit/Website/src/app/app.module.ts | 24 + .../Website/src/app/faq1/faq1.component.css | 0 .../Website/src/app/faq1/faq1.component.html | 90 + .../src/app/faq1/faq1.component.spec.ts | 25 + .../Website/src/app/faq1/faq1.component.ts | 13 + .../Website/src/app/hero1/hero1.component.css | 0 .../src/app/hero1/hero1.component.html | 57 + .../src/app/hero1/hero1.component.spec.ts | 24 + .../Website/src/app/hero1/hero1.component.ts | 23 + .../Website/src/app/team1/team1.component.css | 0 .../src/app/team1/team1.component.html | 246 + .../src/app/team1/team1.component.spec.ts | 25 + .../Website/src/app/team1/team1.component.ts | 12 + .../testimonials1/testimonials1.component.css | 0 .../testimonials1.component.html | 148 + .../testimonials1.component.spec.ts | 25 + .../testimonials1/testimonials1.component.ts | 15 + TailwindUIKit/Website/src/assets/.gitkeep | 0 .../src/environments/environment.prod.ts | 3 + .../Website/src/environments/environment.ts | 16 + TailwindUIKit/Website/src/favicon.ico | Bin 0 -> 948 bytes TailwindUIKit/Website/src/index.html | 14 + TailwindUIKit/Website/src/main.ts | 12 + TailwindUIKit/Website/src/polyfills.ts | 53 + TailwindUIKit/Website/src/styles.css | 1 + TailwindUIKit/Website/src/test.ts | 26 + TailwindUIKit/Website/tsconfig.app.json | 15 + TailwindUIKit/Website/tsconfig.json | 23 + TailwindUIKit/Website/tsconfig.spec.json | 18 + TailwindUIKit/package-lock.json | 1308 + TailwindUIKit/package.json | 7 + TailwindUIKit/tailwind.config.js | 7 + 48 files changed, 22842 insertions(+) create mode 100644 TailwindUIKit/README.md create mode 100644 TailwindUIKit/Website/.browserslistrc create mode 100644 TailwindUIKit/Website/.editorconfig create mode 100644 TailwindUIKit/Website/.gitignore create mode 100644 TailwindUIKit/Website/.vscode/extensions.json create mode 100644 TailwindUIKit/Website/.vscode/launch.json create mode 100644 TailwindUIKit/Website/.vscode/tasks.json create mode 100644 TailwindUIKit/Website/README.md create mode 100644 TailwindUIKit/Website/angular.json create mode 100644 TailwindUIKit/Website/karma.conf.js create mode 100644 TailwindUIKit/Website/package-lock.json create mode 100644 TailwindUIKit/Website/package.json create mode 100644 TailwindUIKit/Website/src/app/app.component.css create mode 100644 TailwindUIKit/Website/src/app/app.component.html create mode 100644 TailwindUIKit/Website/src/app/app.component.spec.ts create mode 100644 TailwindUIKit/Website/src/app/app.component.ts create mode 100644 TailwindUIKit/Website/src/app/app.module.ts create mode 100644 TailwindUIKit/Website/src/app/faq1/faq1.component.css create mode 100644 TailwindUIKit/Website/src/app/faq1/faq1.component.html create mode 100644 TailwindUIKit/Website/src/app/faq1/faq1.component.spec.ts create mode 100644 TailwindUIKit/Website/src/app/faq1/faq1.component.ts create mode 100644 TailwindUIKit/Website/src/app/hero1/hero1.component.css create mode 100644 TailwindUIKit/Website/src/app/hero1/hero1.component.html create mode 100644 TailwindUIKit/Website/src/app/hero1/hero1.component.spec.ts create mode 100644 TailwindUIKit/Website/src/app/hero1/hero1.component.ts create mode 100644 TailwindUIKit/Website/src/app/team1/team1.component.css create mode 100644 TailwindUIKit/Website/src/app/team1/team1.component.html create mode 100644 TailwindUIKit/Website/src/app/team1/team1.component.spec.ts create mode 100644 TailwindUIKit/Website/src/app/team1/team1.component.ts create mode 100644 TailwindUIKit/Website/src/app/testimonials1/testimonials1.component.css create mode 100644 TailwindUIKit/Website/src/app/testimonials1/testimonials1.component.html create mode 100644 TailwindUIKit/Website/src/app/testimonials1/testimonials1.component.spec.ts create mode 100644 TailwindUIKit/Website/src/app/testimonials1/testimonials1.component.ts create mode 100644 TailwindUIKit/Website/src/assets/.gitkeep create mode 100644 TailwindUIKit/Website/src/environments/environment.prod.ts create mode 100644 TailwindUIKit/Website/src/environments/environment.ts create mode 100644 TailwindUIKit/Website/src/favicon.ico create mode 100644 TailwindUIKit/Website/src/index.html create mode 100644 TailwindUIKit/Website/src/main.ts create mode 100644 TailwindUIKit/Website/src/polyfills.ts create mode 100644 TailwindUIKit/Website/src/styles.css create mode 100644 TailwindUIKit/Website/src/test.ts create mode 100644 TailwindUIKit/Website/tsconfig.app.json create mode 100644 TailwindUIKit/Website/tsconfig.json create mode 100644 TailwindUIKit/Website/tsconfig.spec.json create mode 100644 TailwindUIKit/package-lock.json create mode 100644 TailwindUIKit/package.json create mode 100644 TailwindUIKit/tailwind.config.js diff --git a/TailwindUIKit/README.md b/TailwindUIKit/README.md new file mode 100644 index 0000000..8c58c67 --- /dev/null +++ b/TailwindUIKit/README.md @@ -0,0 +1,2 @@ +1. Navigate to Website folder +2. Run ```ng serve``` \ No newline at end of file diff --git a/TailwindUIKit/Website/.browserslistrc b/TailwindUIKit/Website/.browserslistrc new file mode 100644 index 0000000..4f9ac26 --- /dev/null +++ b/TailwindUIKit/Website/.browserslistrc @@ -0,0 +1,16 @@ +# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries + +# For the full list of supported browsers by the Angular framework, please see: +# https://angular.io/guide/browser-support + +# You can see what browsers were selected by your queries by running: +# npx browserslist + +last 1 Chrome version +last 1 Firefox version +last 2 Edge major versions +last 2 Safari major versions +last 2 iOS major versions +Firefox ESR diff --git a/TailwindUIKit/Website/.editorconfig b/TailwindUIKit/Website/.editorconfig new file mode 100644 index 0000000..59d9a3a --- /dev/null +++ b/TailwindUIKit/Website/.editorconfig @@ -0,0 +1,16 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/TailwindUIKit/Website/.gitignore b/TailwindUIKit/Website/.gitignore new file mode 100644 index 0000000..0711527 --- /dev/null +++ b/TailwindUIKit/Website/.gitignore @@ -0,0 +1,42 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/node_modules +npm-debug.log +yarn-error.log + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings + +# System files +.DS_Store +Thumbs.db diff --git a/TailwindUIKit/Website/.vscode/extensions.json b/TailwindUIKit/Website/.vscode/extensions.json new file mode 100644 index 0000000..77b3745 --- /dev/null +++ b/TailwindUIKit/Website/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 + "recommendations": ["angular.ng-template"] +} diff --git a/TailwindUIKit/Website/.vscode/launch.json b/TailwindUIKit/Website/.vscode/launch.json new file mode 100644 index 0000000..740e35a --- /dev/null +++ b/TailwindUIKit/Website/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "ng serve", + "type": "pwa-chrome", + "request": "launch", + "preLaunchTask": "npm: start", + "url": "http://localhost:4200/" + }, + { + "name": "ng test", + "type": "chrome", + "request": "launch", + "preLaunchTask": "npm: test", + "url": "http://localhost:9876/debug.html" + } + ] +} diff --git a/TailwindUIKit/Website/.vscode/tasks.json b/TailwindUIKit/Website/.vscode/tasks.json new file mode 100644 index 0000000..a298b5b --- /dev/null +++ b/TailwindUIKit/Website/.vscode/tasks.json @@ -0,0 +1,42 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "start", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "(.*?)" + }, + "endsPattern": { + "regexp": "bundle generation complete" + } + } + } + }, + { + "type": "npm", + "script": "test", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "(.*?)" + }, + "endsPattern": { + "regexp": "bundle generation complete" + } + } + } + } + ] +} diff --git a/TailwindUIKit/Website/README.md b/TailwindUIKit/Website/README.md new file mode 100644 index 0000000..681738b --- /dev/null +++ b/TailwindUIKit/Website/README.md @@ -0,0 +1,27 @@ +# Website + +This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 13.3.4. + +## Development server + +Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. + +## Code scaffolding + +Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. + +## Build + +Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Running unit tests + +Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Running end-to-end tests + +Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. diff --git a/TailwindUIKit/Website/angular.json b/TailwindUIKit/Website/angular.json new file mode 100644 index 0000000..3b79a70 --- /dev/null +++ b/TailwindUIKit/Website/angular.json @@ -0,0 +1,102 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "Website": { + "projectType": "application", + "schematics": {}, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/website", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.app.json", + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "src/styles.css" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "2mb", + "maximumError": "5mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "6kb", + "maximumError": "10kb" + } + ], + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "browserTarget": "Website:build:production" + }, + "development": { + "browserTarget": "Website:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "Website:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "src/test.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.spec.json", + "karmaConfig": "karma.conf.js", + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "src/styles.css" + ], + "scripts": [] + } + } + } + } + }, + "defaultProject": "Website" +} diff --git a/TailwindUIKit/Website/karma.conf.js b/TailwindUIKit/Website/karma.conf.js new file mode 100644 index 0000000..66566d4 --- /dev/null +++ b/TailwindUIKit/Website/karma.conf.js @@ -0,0 +1,44 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true // removes the duplicated traces + }, + coverageReporter: { + dir: require('path').join(__dirname, './coverage/website'), + subdir: '.', + reporters: [ + { type: 'html' }, + { type: 'text-summary' } + ] + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/TailwindUIKit/Website/package-lock.json b/TailwindUIKit/Website/package-lock.json new file mode 100644 index 0000000..80b2b96 --- /dev/null +++ b/TailwindUIKit/Website/package-lock.json @@ -0,0 +1,20213 @@ +{ + "name": "website", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "website", + "version": "0.0.0", + "dependencies": { + "@angular/animations": "~13.3.0", + "@angular/common": "~13.3.0", + "@angular/compiler": "~13.3.0", + "@angular/core": "~13.3.0", + "@angular/forms": "~13.3.0", + "@angular/platform-browser": "~13.3.0", + "@angular/platform-browser-dynamic": "~13.3.0", + "@angular/router": "~13.3.0", + "rxjs": "~7.5.0", + "tslib": "^2.3.0", + "zone.js": "~0.11.4" + }, + "devDependencies": { + "@angular-devkit/build-angular": "~13.3.4", + "@angular/cli": "~13.3.4", + "@angular/compiler-cli": "~13.3.0", + "@types/jasmine": "~3.10.0", + "@types/node": "^12.11.1", + "jasmine-core": "~4.0.0", + "karma": "~6.3.0", + "karma-chrome-launcher": "~3.1.0", + "karma-coverage": "~2.1.0", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "~1.7.0", + "typescript": "~4.6.2" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.1303.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1303.6.tgz", + "integrity": "sha512-Quh8KzO17PZH38mrDlBihrT6TioTnD8I+nSXuTZIqHwDpyFCTB9wBm9wC1J6HzMzEJ1GoYWEH/ukfbVpmj8ghw==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "13.3.6", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/architect/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/architect/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular": { + "version": "13.3.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-13.3.6.tgz", + "integrity": "sha512-VrEw9AhS0BBPRA59aZZYDXelXX8SdTfo7mW1gSeuvyeqk8RpMdjHWJPUmuvNJro6s5zOe4lc9dVrGxdevCbQFQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "2.2.0", + "@angular-devkit/architect": "0.1303.6", + "@angular-devkit/build-webpack": "0.1303.6", + "@angular-devkit/core": "13.3.6", + "@babel/core": "7.16.12", + "@babel/generator": "7.16.8", + "@babel/helper-annotate-as-pure": "7.16.7", + "@babel/plugin-proposal-async-generator-functions": "7.16.8", + "@babel/plugin-transform-async-to-generator": "7.16.8", + "@babel/plugin-transform-runtime": "7.16.10", + "@babel/preset-env": "7.16.11", + "@babel/runtime": "7.16.7", + "@babel/template": "7.16.7", + "@discoveryjs/json-ext": "0.5.6", + "@ngtools/webpack": "13.3.6", + "ansi-colors": "4.1.1", + "babel-loader": "8.2.5", + "babel-plugin-istanbul": "6.1.1", + "browserslist": "^4.9.1", + "cacache": "15.3.0", + "circular-dependency-plugin": "5.2.2", + "copy-webpack-plugin": "10.2.1", + "core-js": "3.20.3", + "critters": "0.0.16", + "css-loader": "6.5.1", + "esbuild-wasm": "0.14.22", + "glob": "7.2.0", + "https-proxy-agent": "5.0.0", + "inquirer": "8.2.0", + "jsonc-parser": "3.0.0", + "karma-source-map-support": "1.4.0", + "less": "4.1.2", + "less-loader": "10.2.0", + "license-webpack-plugin": "4.0.2", + "loader-utils": "3.2.0", + "mini-css-extract-plugin": "2.5.3", + "minimatch": "3.0.5", + "open": "8.4.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.2.0", + "postcss": "8.4.5", + "postcss-import": "14.0.2", + "postcss-loader": "6.2.1", + "postcss-preset-env": "7.2.3", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "5.0.0", + "rxjs": "6.6.7", + "sass": "1.49.9", + "sass-loader": "12.4.0", + "semver": "7.3.5", + "source-map-loader": "3.0.1", + "source-map-support": "0.5.21", + "stylus": "0.56.0", + "stylus-loader": "6.2.0", + "terser": "5.11.0", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.3.1", + "webpack": "5.70.0", + "webpack-dev-middleware": "5.3.0", + "webpack-dev-server": "4.7.3", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "5.1.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "esbuild": "0.14.22" + }, + "peerDependencies": { + "@angular/compiler-cli": "^13.0.0 || ^13.3.0-rc.0", + "@angular/localize": "^13.0.0 || ^13.3.0-rc.0", + "@angular/service-worker": "^13.0.0 || ^13.3.0-rc.0", + "karma": "^6.3.0", + "ng-packagr": "^13.0.0", + "protractor": "^7.0.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=4.4.3 <4.7" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "karma": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "protractor": { + "optional": true + }, + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1303.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1303.6.tgz", + "integrity": "sha512-T2Uwt57RGPNH5OOblWcWd4Q4NaOK0f2jNTH29L6kSFVlYQ7kKCe9xo3YdzVdiY5b4w/hfnDlpGR+J3aSkkmBRw==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1303.6", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "webpack": "^5.30.0", + "webpack-dev-server": "^4.0.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/core": { + "version": "13.3.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.6.tgz", + "integrity": "sha512-ZmD586B+RnM2CG5+jbXh2NVfIydTc/yKSjppYDDOv4I530YBm6vpfZMwClpiNk6XLbMv7KqX4Tlr4wfxlPYYbA==", + "dev": true, + "dependencies": { + "ajv": "8.9.0", + "ajv-formats": "2.1.1", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.7", + "source-map": "0.7.3" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/core/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/schematics": { + "version": "13.3.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.3.6.tgz", + "integrity": "sha512-yLh5xc92C/FiaAp27coPiKWpSUmwoXF7vMxbJYJTyOXlt0mUITAEAwtrZQNr4yAxW/yvgTdyg7PhXaveQNTUuQ==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "13.3.6", + "jsonc-parser": "3.0.0", + "magic-string": "0.25.7", + "ora": "5.4.1", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular/animations": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-13.3.9.tgz", + "integrity": "sha512-PIspkNm1r7Uq1F3/c24mTQjCbKvl84Iy5kRmKOtjxp9uBGg1Dy+akw29hJt2FAqKa5yKFustBFCgWKypqgSoUQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "13.3.9" + } + }, + "node_modules/@angular/cli": { + "version": "13.3.6", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.3.6.tgz", + "integrity": "sha512-+OC7uspa8oDGQzcpml3DI8XyLvYurhSFhcmLPsyY/naHAV78NKSNf3dIWMPNozAioDzkZXPZXH0dwSdb+cOeQA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@angular-devkit/architect": "0.1303.6", + "@angular-devkit/core": "13.3.6", + "@angular-devkit/schematics": "13.3.6", + "@schematics/angular": "13.3.6", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.1", + "debug": "4.3.3", + "ini": "2.0.0", + "inquirer": "8.2.0", + "jsonc-parser": "3.0.0", + "npm-package-arg": "8.1.5", + "npm-pick-manifest": "6.1.1", + "open": "8.4.0", + "ora": "5.4.1", + "pacote": "12.0.3", + "resolve": "1.22.0", + "semver": "7.3.5", + "symbol-observable": "4.0.0", + "uuid": "8.3.2" + }, + "bin": { + "ng": "bin/ng.js" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/common": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-13.3.9.tgz", + "integrity": "sha512-+bTleNL1XGlzuxLbVbsol82/33IW2pJasQN8ViraxKIElT2F8ooBJevIBMCSIcdvxvMBGAjn4ayQJr6tkPqPaw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "13.3.9", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-13.3.9.tgz", + "integrity": "sha512-fXmcN9PIUTJ9Vw2QyQE9vtW95K5ML9bVI7409Zf3DAQJEo4IhX2eUjgiGF3RtSn9Kdjj3tZY0xSbGGwp1hTBIA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + } + }, + "node_modules/@angular/compiler-cli": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-13.3.9.tgz", + "integrity": "sha512-jf4iG7uqE1ZW2mJgKJyDyFby/sVQDlGOtUl68TMf8r06koWeny5nk/LhL7jp3Q6rgN2ZdYALtPTRSj7HVQD8pA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.17.2", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.11.0", + "magic-string": "^0.26.0", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js", + "ngcc": "bundles/ngcc/main-ngcc.js" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/compiler": "13.3.9", + "typescript": ">=4.4.2 <4.7" + } + }, + "node_modules/@angular/compiler-cli/node_modules/@babel/core": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.12.tgz", + "integrity": "sha512-44ODe6O1IVz9s2oJE3rZ4trNNKTX9O7KpQpfAP4t8QII/zwrVRHL7i2pxhqtcY7tqMLrrKfMlBKnm1QlrRFs5w==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.12", + "@babel/helper-compilation-targets": "^7.17.10", + "@babel/helper-module-transforms": "^7.17.12", + "@babel/helpers": "^7.17.9", + "@babel/parser": "^7.17.12", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.12", + "@babel/types": "^7.17.12", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@angular/compiler-cli/node_modules/@babel/generator": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.12.tgz", + "integrity": "sha512-V49KtZiiiLjH/CnIW6OjJdrenrGoyh6AmKQ3k2AZFKozC1h846Q4NYlZ5nqAigPDUXfGzC88+LOUuG8yKd2kCw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.17.12", + "@jridgewell/gen-mapping": "^0.3.0", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@angular/compiler-cli/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz", + "integrity": "sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular/compiler-cli/node_modules/magic-string": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", + "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@angular/core": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-13.3.9.tgz", + "integrity": "sha512-LaY3yBDgN/efG11x/cwVeuC4gUG3YUMmk/sgAP3L1VGawYOiKoJ76decFpy6y4UgYPShQQRzpZQEVXF405jrLg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.11.4" + } + }, + "node_modules/@angular/forms": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-13.3.9.tgz", + "integrity": "sha512-eNOsqMVrMsBceoAJ9pS+2qQDWsgwt62q7abqfYdzSdkjWbnLrtaIPP6iYMGQke1pIcdUSyoun29VsdQTSXXksg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "13.3.9", + "@angular/core": "13.3.9", + "@angular/platform-browser": "13.3.9", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.3.9.tgz", + "integrity": "sha512-1l0IVYFbKCEfACR60bfLjH35BYP69CerIW9Ok58pedp0MIUsvPttBzUjM5HhW+3jhvNyO0cCMaK4r5kKTRMo4w==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/animations": "13.3.9", + "@angular/common": "13.3.9", + "@angular/core": "13.3.9" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-13.3.9.tgz", + "integrity": "sha512-flyfoJG9vBSj3rmH/jUNaOPGfGlGHSj4v34OL16Qjk3M5bxbQKxBYNrDAUwk0Ve4S4qUfXF7ZDE0v1vUvn+3MA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "13.3.9", + "@angular/compiler": "13.3.9", + "@angular/core": "13.3.9", + "@angular/platform-browser": "13.3.9" + } + }, + "node_modules/@angular/router": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-13.3.9.tgz", + "integrity": "sha512-M9j3ZscdRLsTnOLware1Yyw87JlRw/axoeZcN8JnTs3ltCc0+UMEJ22q0s8ud6j9GmYiA0+kmaM35OsEoHX72g==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "13.3.9", + "@angular/core": "13.3.9", + "@angular/platform-browser": "13.3.9", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@assemblyscript/loader": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", + "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", + "dev": true + }, + "node_modules/@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.10.tgz", + "integrity": "sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.16.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz", + "integrity": "sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.16.8", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helpers": "^7.16.7", + "@babel/parser": "^7.16.12", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.10", + "@babel/types": "^7.16.8", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/core/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", + "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.8", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", + "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", + "dev": true, + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz", + "integrity": "sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.17.10", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.12.tgz", + "integrity": "sha512-sZoOeUTkFJMyhqCei2+Z+wtH/BehW8NVKQt7IRUQlRiOARuXymJYfN/FCcI8CvVbR0XVyDM6eLFOlR7YtiXnew==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.17.9", + "@babel/helper-member-expression-to-functions": "^7.17.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.12.tgz", + "integrity": "sha512-b2aZrV4zvutr9AIa6/gA3wsZKRwTKYoDxYiFKcESS3Ug2GTXzwBEvMuuFLhCQpEnRXs1zng4ISAXSUxxKBIcxw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "regexpu-core": "^5.0.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz", + "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", + "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", + "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.16.7", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", + "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.12.tgz", + "integrity": "sha512-t5s2BeSWIghhFRPh9XMn6EIGmvn8Lmw5RVASJzkIx1mSemubQQBNIZiQD7WzaFmaHIrjAec4x8z9Yx8SjJ1/LA==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.12", + "@babel/types": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", + "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz", + "integrity": "sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", + "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-wrap-function": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", + "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", + "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", + "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", + "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz", + "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", + "dev": true, + "dependencies": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.9", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz", + "integrity": "sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.12.tgz", + "integrity": "sha512-FLzHmN9V3AJIrWfOpvRlZCeVg/WLdicSnTMsLur6uDj9TT8ymUlG9XxURdW/XvuygK+2CW0poOJABdA4m/YKxA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.17.12.tgz", + "integrity": "sha512-xCJQXl4EeQ3J9C4yOmpTrtVGmzpm2iSzyxbkZHw7UCnZBftHpF/hpII80uWVyVrc40ytIClHjgWGTG1g/yB+aw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.17.12.tgz", + "integrity": "sha512-/vt0hpIw0x4b6BLKUkwlvEoiGZYYLNZ96CzyHYPbtG2jZGz6LBe7/V+drYrc/d+ovrF9NBi0pmtvmNb/FsWtRQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz", + "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.17.12.tgz", + "integrity": "sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.12.tgz", + "integrity": "sha512-8ILyDG6eL14F8iub97dVc8q35Md0PJYAnA5Kz9NACFOkt6ffCcr0FISyUPKHsvuAy36fkpIitxZ9bVYPFMGQHA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", + "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.17.12.tgz", + "integrity": "sha512-j7Ye5EWdwoXOpRmo5QmRyHPsDIe6+u70ZYZrd7uz+ebPYFKfRcLcNu3Ro0vOlJ5zuv8rU7xa+GttNiRzX56snQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.17.12.tgz", + "integrity": "sha512-rKJ+rKBoXwLnIn7n6o6fulViHMrOThz99ybH+hKHcOZbnN14VuMnH9fo2eHE69C8pO4uX1Q7t2HYYIDmv8VYkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.17.12.tgz", + "integrity": "sha512-EqFo2s1Z5yy+JeJu7SFfbIUtToJTVlC61/C7WLKDntSw4Sz6JNAIfL7zQ74VvirxpjB5kz/kIx0gCcb+5OEo2Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.17.12.tgz", + "integrity": "sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", + "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.12.tgz", + "integrity": "sha512-6l9cO3YXXRh4yPCPRA776ZyJ3RobG4ZKJZhp7NDRbKIOeV3dBPG8FXCF7ZtiO2RTCIOkQOph1xDDcc01iWVNjQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.17.10", + "@babel/helper-compilation-targets": "^7.17.10", + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", + "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.17.12.tgz", + "integrity": "sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.17.12.tgz", + "integrity": "sha512-SllXoxo19HmxhDWm3luPz+cPhtoTSKLJE9PXshsfrOzBqs60QP0r8OaJItrPhAj0d7mZMnNF0Y1UUggCDgMz1A==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.17.12.tgz", + "integrity": "sha512-/6BtVi57CJfrtDNKfK5b66ydK2J5pXUKBKSPD2G1whamMuEnZWgoOIfO8Vf9F/DoD4izBLD/Au4NMQfruzzykg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.17.12.tgz", + "integrity": "sha512-Wb9qLjXf3ZazqXA7IvI7ozqRIXIGPtSo+L5coFmEkhTQK18ao4UDDD0zdTGAarmbLj2urpRwrc6893cu5Bfh0A==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.17.12.tgz", + "integrity": "sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz", + "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", + "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.17.12.tgz", + "integrity": "sha512-jw8XW/B1i7Lqwqj2CbrViPcZijSxfguBWZP2aN59NHgxUyO/OcO1mfdCxH13QhN5LbWhPkX+f+brKGhZTiqtZQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.17.12.tgz", + "integrity": "sha512-cvO7lc7pZat6BsvH6l/EGaI8zpl8paICaoGk+7x7guvtfak/TbIf66nYmJOH13EuG0H+Xx3M+9LQDtSvZFKXKw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.17.9", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.17.12.tgz", + "integrity": "sha512-a7XINeplB5cQUWMg1E/GI1tFz3LfK021IjV1rj1ypE+R7jHm+pIHmHl25VNkZxtx9uuYp7ThGk8fur1HHG7PgQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.12.tgz", + "integrity": "sha512-P8pt0YiKtX5UMUL5Xzsc9Oyij+pJE6JuC+F1k0/brq/OOGs5jDa1If3OY0LRWGvJsJhI+8tsiecL3nJLc0WTlg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", + "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.17.12.tgz", + "integrity": "sha512-EA5eYFUG6xeerdabina/xIoB95jJ17mAkR8ivx6ZSu9frKShBjpOGZPn511MTDTkiCO+zXnzNczvUM69YSf3Zw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", + "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.17.12.tgz", + "integrity": "sha512-76lTwYaCxw8ldT7tNmye4LLwSoKDbRCBzu6n/DcK/P3FOR29+38CIIaVIZfwol9By8W/QHORYEnYSLuvcQKrsg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", + "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.17.12.tgz", + "integrity": "sha512-8iRkvaTjJciWycPIZ9k9duu663FT7VrBdNqNgxnVXEFwOIp55JWcZd23VBRySYbnS3PwQ3rGiabJBBBGj5APmQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", + "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.17.12.tgz", + "integrity": "sha512-p5rt9tB5Ndcc2Za7CeNxVf7YAjRcUMR6yi8o8tKjb9KhRkEvXwa+C0hj6DA5bVDkKRxB0NYhMUGbVKoFu4+zEA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.12.tgz", + "integrity": "sha512-tVPs6MImAJz+DiX8Y1xXEMdTk5Lwxu9jiPjlS+nv5M2A59R7+/d1+9A8C/sbuY0b3QjIxqClkj6KAplEtRvzaA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/helper-simple-access": "^7.17.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.12.tgz", + "integrity": "sha512-NVhDb0q00hqZcuLduUf/kMzbOQHiocmPbIxIvk23HLiEqaTKC/l4eRxeC7lO63M72BmACoiKOcb9AkOAJRerpw==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-module-transforms": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/helper-validator-identifier": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.17.12.tgz", + "integrity": "sha512-BnsPkrUHsjzZGpnrmJeDFkOMMljWFHPjDc9xDcz71/C+ybF3lfC3V4m3dwXPLZrE5b3bgd4V+3/Pj+3620d7IA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.17.12.tgz", + "integrity": "sha512-vWoWFM5CKaTeHrdUJ/3SIOTRV+MBVGybOC9mhJkaprGNt5demMymDW24yC74avb915/mIRe3TgNb/d8idvnCRA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.17.12.tgz", + "integrity": "sha512-CaOtzk2fDYisbjAD4Sd1MTKGVIpRtx9bWLyj24Y/k6p4s4gQ3CqDGJauFJxt8M/LEx003d0i3klVqnN73qvK3w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", + "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.17.12.tgz", + "integrity": "sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", + "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.17.9.tgz", + "integrity": "sha512-Lc2TfbxR1HOyn/c6b4Y/b6NHoTb67n/IoWLxTu4kC7h4KQnWlhCq2S8Tx0t2SVvv5Uu87Hs+6JEJ5kt2tYGylQ==", + "dev": true, + "dependencies": { + "regenerator-transform": "^0.15.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.17.12.tgz", + "integrity": "sha512-1KYqwbJV3Co03NIi14uEHW8P50Md6KqFgt0FfpHdK6oyAHQVTosgPuPSiWud1HX0oYJ1hGRRlk0fP87jFpqXZA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.10.tgz", + "integrity": "sha512-9nwTiqETv2G7xI4RvXHNfpGdr8pAA+Q/YtN3yLK7OoK7n9OibVm/xymJ838a9A6E/IciOLPj82lZk0fW6O4O7w==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", + "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.17.12.tgz", + "integrity": "sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", + "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.17.12.tgz", + "integrity": "sha512-kAKJ7DX1dSRa2s7WN1xUAuaQmkTpN+uig4wCKWivVXIObqGbVTUlSavHyfI2iZvz89GFAMGm9p2DBJ4Y1Tp0hw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.17.12.tgz", + "integrity": "sha512-Q8y+Jp7ZdtSPXCThB6zjQ74N3lj0f6TDh1Hnf5B+sYlzQ8i5Pjp8gW0My79iekSpT4WnI06blqP6DT0OmaXXmw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.17.12" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", + "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", + "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz", + "integrity": "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.16.8", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-option": "^7.16.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-async-generator-functions": "^7.16.8", + "@babel/plugin-proposal-class-properties": "^7.16.7", + "@babel/plugin-proposal-class-static-block": "^7.16.7", + "@babel/plugin-proposal-dynamic-import": "^7.16.7", + "@babel/plugin-proposal-export-namespace-from": "^7.16.7", + "@babel/plugin-proposal-json-strings": "^7.16.7", + "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", + "@babel/plugin-proposal-numeric-separator": "^7.16.7", + "@babel/plugin-proposal-object-rest-spread": "^7.16.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", + "@babel/plugin-proposal-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-private-methods": "^7.16.11", + "@babel/plugin-proposal-private-property-in-object": "^7.16.7", + "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.16.7", + "@babel/plugin-transform-async-to-generator": "^7.16.8", + "@babel/plugin-transform-block-scoped-functions": "^7.16.7", + "@babel/plugin-transform-block-scoping": "^7.16.7", + "@babel/plugin-transform-classes": "^7.16.7", + "@babel/plugin-transform-computed-properties": "^7.16.7", + "@babel/plugin-transform-destructuring": "^7.16.7", + "@babel/plugin-transform-dotall-regex": "^7.16.7", + "@babel/plugin-transform-duplicate-keys": "^7.16.7", + "@babel/plugin-transform-exponentiation-operator": "^7.16.7", + "@babel/plugin-transform-for-of": "^7.16.7", + "@babel/plugin-transform-function-name": "^7.16.7", + "@babel/plugin-transform-literals": "^7.16.7", + "@babel/plugin-transform-member-expression-literals": "^7.16.7", + "@babel/plugin-transform-modules-amd": "^7.16.7", + "@babel/plugin-transform-modules-commonjs": "^7.16.8", + "@babel/plugin-transform-modules-systemjs": "^7.16.7", + "@babel/plugin-transform-modules-umd": "^7.16.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", + "@babel/plugin-transform-new-target": "^7.16.7", + "@babel/plugin-transform-object-super": "^7.16.7", + "@babel/plugin-transform-parameters": "^7.16.7", + "@babel/plugin-transform-property-literals": "^7.16.7", + "@babel/plugin-transform-regenerator": "^7.16.7", + "@babel/plugin-transform-reserved-words": "^7.16.7", + "@babel/plugin-transform-shorthand-properties": "^7.16.7", + "@babel/plugin-transform-spread": "^7.16.7", + "@babel/plugin-transform-sticky-regex": "^7.16.7", + "@babel/plugin-transform-template-literals": "^7.16.7", + "@babel/plugin-transform-typeof-symbol": "^7.16.7", + "@babel/plugin-transform-unicode-escapes": "^7.16.7", + "@babel/plugin-transform-unicode-regex": "^7.16.7", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.16.8", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "core-js-compat": "^3.20.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", + "integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.12.tgz", + "integrity": "sha512-zULPs+TbCvOkIFd4FrG53xrpxvCBwLIgo6tO0tJorY7YV2IWFxUfS/lXDJbGgfyYt9ery/Gxj2niwttNnB0gIw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.12", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.17.9", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.12", + "@babel/types": "^7.17.12", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.12.tgz", + "integrity": "sha512-V49KtZiiiLjH/CnIW6OjJdrenrGoyh6AmKQ3k2AZFKozC1h846Q4NYlZ5nqAigPDUXfGzC88+LOUuG8yKd2kCw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.17.12", + "@jridgewell/gen-mapping": "^0.3.0", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz", + "integrity": "sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.12.tgz", + "integrity": "sha512-rH8i29wcZ6x9xjzI5ILHL/yZkbQnCERdHlogKuIb4PUr7do4iT8DPekrTbBLWTnRQm6U0GYABbTMSzijmEqlAg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-1.0.0.tgz", + "integrity": "sha512-RkYG5KiGNX0fJ5YoI0f4Wfq2Yo74D25Hru4fxTOioYdQvHBxcrrtTTyT5Ozzh2ejcNrhFy7IEts2WyEY7yi5yw==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3", + "postcss-selector-parser": "^6.0.10" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", + "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz", + "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.1.tgz", + "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz", + "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz", + "integrity": "sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@ngtools/webpack": { + "version": "13.3.6", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.3.6.tgz", + "integrity": "sha512-QSdFtQIUgnDvM0EXFOpVRp5HTN0U4B9z60fHHfKKzxpNuU3z9EFzJoDUMZ8WabLXtWboUZWCSx0x3tdBt/sVQw==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^13.0.0", + "typescript": ">=4.4.3 <4.7", + "webpack": "^5.30.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/git": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz", + "integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==", + "dev": true, + "dependencies": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", + "dev": true, + "dependencies": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "installed-package-contents": "index.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz", + "integrity": "sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA==", + "dev": true + }, + "node_modules/@npmcli/promise-spawn": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz", + "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==", + "dev": true, + "dependencies": { + "infer-owner": "^1.0.4" + } + }, + "node_modules/@npmcli/run-script": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-2.0.0.tgz", + "integrity": "sha512-fSan/Pu11xS/TdaTpTB0MRn9guwGU8dye+x56mEVgBEd/QsybBbYcAL0phPXi8SGWFEChkQd6M9qL4y6VOpFig==", + "dev": true, + "dependencies": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "node-gyp": "^8.2.0", + "read-package-json-fast": "^2.0.1" + } + }, + "node_modules/@schematics/angular": { + "version": "13.3.6", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.3.6.tgz", + "integrity": "sha512-BGBmIasjipBxQhV+UdN8B5P73SBXgBPkc7rcOK3Py+xpqMcoTWn290nqxIxLxRVmSeHLabE7+n1m3WnCumlm9Q==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "13.3.6", + "@angular-devkit/schematics": "13.3.6", + "jsonc-parser": "3.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/component-emitter": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", + "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==", + "dev": true + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", + "dev": true + }, + "node_modules/@types/eslint": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.2.tgz", + "integrity": "sha512-Z1nseZON+GEnFjJc04sv4NSALGjhFwy6K0HXt7qsn5ArfAKtb63dXNJHf+1YW6IpOIYRBGUbu3GwJdj8DGnCjA==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jasmine": { + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.10.6.tgz", + "integrity": "sha512-twY9adK/vz72oWxCWxzXaxoDtF9TpfEEsxvbc1ibjF3gMD/RThSuSud/GKUTR3aJnfbivAbC/vLqhY+gdWCHfA==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "12.20.52", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.52.tgz", + "integrity": "sha512-cfkwWw72849SNYp3Zx0IcIs25vABmFh73xicxhCkTcvtZQeIez15PpwQN8fY3RD7gv1Wrxlc9MEtfMORZDEsGw==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", + "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/agentkeepalive/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", + "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz", + "integrity": "sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "node_modules/array-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.7.tgz", + "integrity": "sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.20.3", + "caniuse-lite": "^1.0.30001335", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-loader/node_modules/loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", + "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.3.1", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", + "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.1", + "core-js-compat": "^3.21.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", + "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==", + "dev": true, + "dependencies": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz", + "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001332", + "electron-to-chromium": "^1.4.118", + "escalade": "^3.1.1", + "node-releases": "^2.0.3", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "node_modules/builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001341", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001341.tgz", + "integrity": "sha512-2SodVrFFtvGENGCv0ChVJIDQ0KPaS1cg7/qtfMaICgeMolDdo/Z2OD32F0Aq9yl6F4YFwGPBS5AaPqNYiW4PoA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/circular-dependency-plugin": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", + "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", + "dev": true, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "webpack": ">=4.0.1" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.1.tgz", + "integrity": "sha512-nr81NhCAIpAWXGCK5thrKmfCQ6GDY0L5RN0U+BnIn/7Us55+UCex5ANNsNKmIVtDRnk0Ecf+/kzp9SUVrrBMLg==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.7", + "glob-parent": "^6.0.1", + "globby": "^12.0.2", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 12.20.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/core-js": { + "version": "3.20.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", + "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.22.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.22.5.tgz", + "integrity": "sha512-rEF75n3QtInrYICvJjrAgV03HwKiYvtKHdPtaba1KucG+cNZ4NJnH9isqt979e67KZlhpbCOTwnsvnIr+CVeOg==", + "dev": true, + "dependencies": { + "browserslist": "^4.20.3", + "semver": "7.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/critters": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", + "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "css-select": "^4.2.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "postcss": "^8.3.7", + "pretty-bytes": "^5.3.0" + } + }, + "node_modules/critters/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/critters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/critters/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/critters/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/critters/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/critters/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + } + }, + "node_modules/css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-blank-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-has-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-loader": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz", + "integrity": "sha512-gEy2w9AnJNnD9Kuo4XAP9VflW/ujKoS9c/syO+uWMlm5igc7LysKzPXaDoR2vroROkSwsTS2tGr1yGGEbZOYZQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "dev": true, + "bin": { + "css-prefers-color-scheme": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssdb": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-5.1.0.tgz", + "integrity": "sha512-/vqjXhv1x9eGkE/zO6o8ZOI7dgdZbLVLUGyVRbPgk6YipXbW87YzUCcO+Jrmi5bwJlAH6oD+MNeZyRgXea1GZw==", + "dev": true + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "dev": true + }, + "node_modules/date-format": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.10.tgz", + "integrity": "sha512-RuMIHocrVjF84bUSTcd1uokIsLsOsk1Awb7TexNOI3f48ukCu39mjslWquDTA08VaDMF2umr3MB9ow5EyJTWyA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "dependencies": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/del": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.0.tgz", + "integrity": "sha512-OpcRktOt7G7HBfyxP0srBH4Djg4824EQORX8E1qvIhIzthNNArxxhrB/Mm7dRMiLi1nvFyUpDhzD2cTtbBhV8A==", + "dev": true, + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del/node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/del/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "node_modules/dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "dependencies": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "dependencies": { + "buffer-indexof": "^1.0.0" + } + }, + "node_modules/dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "dev": true, + "dependencies": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.137", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz", + "integrity": "sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/engine.io": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", + "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==", + "dev": true, + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz", + "integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.22.tgz", + "integrity": "sha512-CjFCFGgYtbFOPrwZNJf7wsuzesx8kqwAffOlbYcFDLFuUtP8xloK1GH+Ai13Qr0RZQf9tE7LMTHJ2iVGJ1SKZA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "esbuild-android-arm64": "0.14.22", + "esbuild-darwin-64": "0.14.22", + "esbuild-darwin-arm64": "0.14.22", + "esbuild-freebsd-64": "0.14.22", + "esbuild-freebsd-arm64": "0.14.22", + "esbuild-linux-32": "0.14.22", + "esbuild-linux-64": "0.14.22", + "esbuild-linux-arm": "0.14.22", + "esbuild-linux-arm64": "0.14.22", + "esbuild-linux-mips64le": "0.14.22", + "esbuild-linux-ppc64le": "0.14.22", + "esbuild-linux-riscv64": "0.14.22", + "esbuild-linux-s390x": "0.14.22", + "esbuild-netbsd-64": "0.14.22", + "esbuild-openbsd-64": "0.14.22", + "esbuild-sunos-64": "0.14.22", + "esbuild-windows-32": "0.14.22", + "esbuild-windows-64": "0.14.22", + "esbuild-windows-arm64": "0.14.22" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.22.tgz", + "integrity": "sha512-k1Uu4uC4UOFgrnTj2zuj75EswFSEBK+H6lT70/DdS4mTAOfs2ECv2I9ZYvr3w0WL0T4YItzJdK7fPNxcPw6YmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.22.tgz", + "integrity": "sha512-d8Ceuo6Vw6HM3fW218FB6jTY6O3r2WNcTAU0SGsBkXZ3k8SDoRLd3Nrc//EqzdgYnzDNMNtrWegK2Qsss4THhw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.22.tgz", + "integrity": "sha512-YAt9Tj3SkIUkswuzHxkaNlT9+sg0xvzDvE75LlBo4DI++ogSgSmKNR6B4eUhU5EUUepVXcXdRIdqMq9ppeRqfw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.22.tgz", + "integrity": "sha512-ek1HUv7fkXMy87Qm2G4IRohN+Qux4IcnrDBPZGXNN33KAL0pEJJzdTv0hB/42+DCYWylSrSKxk3KUXfqXOoH4A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.22.tgz", + "integrity": "sha512-zPh9SzjRvr9FwsouNYTqgqFlsMIW07O8mNXulGeQx6O5ApgGUBZBgtzSlBQXkHi18WjrosYfsvp5nzOKiWzkjQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.22.tgz", + "integrity": "sha512-SnpveoE4nzjb9t2hqCIzzTWBM0RzcCINDMBB67H6OXIuDa4KqFqaIgmTchNA9pJKOVLVIKd5FYxNiJStli21qg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.22.tgz", + "integrity": "sha512-Zcl9Wg7gKhOWWNqAjygyqzB+fJa19glgl2JG7GtuxHyL1uEnWlpSMytTLMqtfbmRykIHdab797IOZeKwk5g0zg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.22.tgz", + "integrity": "sha512-soPDdbpt/C0XvOOK45p4EFt8HbH5g+0uHs5nUKjHVExfgR7du734kEkXR/mE5zmjrlymk5AA79I0VIvj90WZ4g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.22.tgz", + "integrity": "sha512-8q/FRBJtV5IHnQChO3LHh/Jf7KLrxJ/RCTGdBvlVZhBde+dk3/qS9fFsUy+rs3dEi49aAsyVitTwlKw1SUFm+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.22.tgz", + "integrity": "sha512-SiNDfuRXhGh1JQLLA9JPprBgPVFOsGuQ0yDfSPTNxztmVJd8W2mX++c4FfLpAwxuJe183mLuKf7qKCHQs5ZnBQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.22.tgz", + "integrity": "sha512-6t/GI9I+3o1EFm2AyN9+TsjdgWCpg2nwniEhjm2qJWtJyJ5VzTXGUU3alCO3evopu8G0hN2Bu1Jhz2YmZD0kng==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.22.tgz", + "integrity": "sha512-AyJHipZKe88sc+tp5layovquw5cvz45QXw5SaDgAq2M911wLHiCvDtf/07oDx8eweCyzYzG5Y39Ih568amMTCQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.22.tgz", + "integrity": "sha512-Sz1NjZewTIXSblQDZWEFZYjOK6p8tV6hrshYdXZ0NHTjWE+lwxpOpWeElUGtEmiPcMT71FiuA9ODplqzzSxkzw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.22.tgz", + "integrity": "sha512-TBbCtx+k32xydImsHxvFgsOCuFqCTGIxhzRNbgSL1Z2CKhzxwT92kQMhxort9N/fZM2CkRCPPs5wzQSamtzEHA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.22.tgz", + "integrity": "sha512-vK912As725haT313ANZZZN+0EysEEQXWC/+YE4rQvOQzLuxAQc2tjbzlAFREx3C8+uMuZj/q7E5gyVB7TzpcTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.22.tgz", + "integrity": "sha512-/mbJdXTW7MTcsPhtfDsDyPEOju9EOABvCjeUU2OJ7fWpX/Em/H3WYDa86tzLUbcVg++BScQDzqV/7RYw5XNY0g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.14.22.tgz", + "integrity": "sha512-FOSAM29GN1fWusw0oLMv6JYhoheDIh5+atC72TkJKfIUMID6yISlicoQSd9gsNSFsNBvABvtE2jR4JB1j4FkFw==", + "dev": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.22.tgz", + "integrity": "sha512-1vRIkuvPTjeSVK3diVrnMLSbkuE36jxA+8zGLUOrT4bb7E/JZvDRhvtbWXWaveUc/7LbhaNFhHNvfPuSw2QOQg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.22.tgz", + "integrity": "sha512-AxjIDcOmx17vr31C5hp20HIwz1MymtMjKqX4qL6whPj0dT9lwxPexmLj6G1CpR3vFhui6m75EnBEe4QL82SYqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.22.tgz", + "integrity": "sha512-5wvQ+39tHmRhNpu2Fx04l7QfeK3mQ9tKzDqqGR8n/4WUxsFxnVLfDRBGirIfk4AfWlxk60kqirlODPoT5LqMUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter-asyncresource": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", + "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", + "dev": true + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/express/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/express/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/finalhandler/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz", + "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "dev": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz", + "integrity": "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==", + "dev": true, + "dependencies": { + "array-union": "^3.0.1", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.7", + "ignore": "^5.1.9", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "node_modules/hdr-histogram-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", + "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", + "dev": true, + "dependencies": { + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" + } + }, + "node_modules/hdr-histogram-percentiles-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", + "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", + "dev": true + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "dev": true + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", + "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "dev": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-4.0.1.tgz", + "integrity": "sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/inquirer": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.0.tgz", + "integrity": "sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.2.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/inquirer/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/inquirer/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/inquirer/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", + "dev": true + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", + "dev": true + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", + "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jasmine-core": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.0.1.tgz", + "integrity": "sha512-w+JDABxQCkxbGGxg+a2hUVZyqUS2JKngvIyLGu/xiw2ZwgsoSB0iiecLQsQORSeaKQ6iGrCyWG86RfNDuoA7Lg==", + "dev": true + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/karma": { + "version": "6.3.20", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.20.tgz", + "integrity": "sha512-HRNQhMuKOwKpjYlWiJP0DUrJOh+QjaI/DTaD8b9rEm4Il3tJ8MijutVZH4ts10LuUFst/CedwTS6vieCN8yTSw==", + "dev": true, + "dependencies": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.4.1", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/karma-chrome-launcher": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz", + "integrity": "sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==", + "dev": true, + "dependencies": { + "which": "^1.2.1" + } + }, + "node_modules/karma-coverage": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.1.1.tgz", + "integrity": "sha512-oxeOSBVK/jdZsiX03LhHQkO4eISSQb5GbHi6Nsw3Mw7G4u6yUgacBAftnO7q+emPBLMsrNbz1pGIrj+Jb3z17A==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.0.5", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/karma-coverage/node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma-coverage/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/karma-jasmine": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.2.tgz", + "integrity": "sha512-ggi84RMNQffSDmWSyyt4zxzh2CQGwsxvYYsprgyR1j8ikzIduEdOlcLvXjZGwXG/0j41KUXOWsUCBfbEHPWP9g==", + "dev": true, + "dependencies": { + "jasmine-core": "^3.6.0" + }, + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "karma": "*" + } + }, + "node_modules/karma-jasmine-html-reporter": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz", + "integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==", + "dev": true, + "peerDependencies": { + "jasmine-core": ">=3.8", + "karma": ">=0.9", + "karma-jasmine": ">=1.1" + } + }, + "node_modules/karma-jasmine/node_modules/jasmine-core": { + "version": "3.99.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.99.1.tgz", + "integrity": "sha512-Hu1dmuoGcZ7AfyynN3LsfruwMbxMALMka+YtZeGoLuDEySVmVAPaonkNoBRIw/ectu8b9tVQCJNgp4a4knp+tg==", + "dev": true + }, + "node_modules/karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "dependencies": { + "source-map-support": "^0.5.5" + } + }, + "node_modules/karma/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/karma/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/karma/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/karma/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/less": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.2.tgz", + "integrity": "sha512-EoQp/Et7OSOVu0aJknJOtlXZsnr8XE8KwuzTHOLeVSEx8pVWUICc8Q0VYRHgzyjX78nMEyC/oztWFbgyhtNfDA==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^2.5.2", + "source-map": "~0.6.0" + } + }, + "node_modules/less-loader": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-10.2.0.tgz", + "integrity": "sha512-AV5KHWvCezW27GT90WATaDnfXBv99llDbtaj4bshq6DvAihMdNjaPDcUMa6EXKLRF+P2opFenJp89BXg91XLYg==", + "dev": true, + "dependencies": { + "klona": "^2.0.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/less/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/less/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/license-webpack-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "dev": true, + "dependencies": { + "webpack-sources": "^3.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", + "integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log4js": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.4.7.tgz", + "integrity": "sha512-q/9Eyw/hkvQ4e9DNHLbK2AfuDDm5QnNnmF022aamyw4nUnVLQRhvGoryccN5aEI4J/UcA4W36xttBCrlrdzt2g==", + "dev": true, + "dependencies": { + "date-format": "^4.0.10", + "debug": "^4.3.4", + "flatted": "^3.2.5", + "rfdc": "^1.3.0", + "streamroller": "^3.0.9" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/log4js/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.4" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.3.tgz", + "integrity": "sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg==", + "dev": true, + "dependencies": { + "fs-monkey": "1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.5.3.tgz", + "integrity": "sha512-YseMB8cs8U/KCaAGQoqYmfUuhhGW0a9p9XvWXrxVOkE3/IiISTLw4ALNt7JR5B2eYauFM+PQGSbXMDmVbR7Tfw==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "node_modules/minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-json-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", + "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", + "dev": true, + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "dependencies": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/needle": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", + "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", + "dev": true, + "optional": true, + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "optional": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "!win32" + ], + "dependencies": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "optional": true + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", + "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==", + "dev": true, + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/node-releases": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz", + "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==", + "dev": true + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-install-checks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", + "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", + "dev": true, + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "node_modules/npm-package-arg": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz", + "integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", + "validate-npm-package-name": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-packlist": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-3.0.0.tgz", + "integrity": "sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ==", + "dev": true, + "dependencies": { + "glob": "^7.1.6", + "ignore-walk": "^4.0.1", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-pick-manifest": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz", + "integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==", + "dev": true, + "dependencies": { + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" + } + }, + "node_modules/npm-registry-fetch": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-12.0.2.tgz", + "integrity": "sha512-Df5QT3RaJnXYuOwtXBXS9BWs+tHH2olvkCLh6jcR/b/u3DvPMlp3J0TvvYwplPKxHMOwfg287PYih9QqaVFoKA==", + "dev": true, + "dependencies": { + "make-fetch-happen": "^10.0.1", + "minipass": "^3.1.6", + "minipass-fetch": "^1.4.1", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^8.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/npm-registry-fetch/node_modules/@npmcli/fs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.0.tgz", + "integrity": "sha512-DmfBvNXGaetMxj9LTp8NAN9vEidXURrf5ZTslQzEAi/6GbW+4yjaLFQc6Tue5cpZ9Frlk4OBo/Snf1Bh/S7qTQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/@npmcli/move-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.0.tgz", + "integrity": "sha512-UR6D5f4KEGWJV6BGPH3Qb2EtgH+t+1XQ1Tt85c7qicN6cezzuHPdZwwAxqZr4JLtnQu0LZsTza/5gmNmSl8XLg==", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm-registry-fetch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/cacache": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.0.tgz", + "integrity": "sha512-Pk4aQkwCW82A4jGKFvcGkQFqZcMspfP9YWq9Pr87/ldDvlWf718zeI6KWCdKt/jeihu6BytHRUicJPB1K2k8EQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm-registry-fetch/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm-registry-fetch/node_modules/lru-cache": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.10.1.tgz", + "integrity": "sha512-BQuhQxPuRl79J5zSXRP+uNzPOyZw2oFI9JLRQ80XswSvg21KMKNtQza9eF42rfI/3Z40RvzBdXgziEkudzjo8A==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.1.4.tgz", + "integrity": "sha512-hU1w68PqfH7FdMgjbiziJoACY0edlbIZ0CyKnpcEruVdCjsUrN+qoenOCIayNqVBK7toSWwbDxvQlrhH0gjRdg==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.1.1", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/make-fetch-happen/node_modules/minipass-fetch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.0.tgz", + "integrity": "sha512-H9U4UVBGXEyyWJnqYDCLp1PwD8XIkJ4akNHp1aGVI+2Ym7wQMlxDKi4IB4JbmyU+pl9pEs/cVrK6cOuvmbK4Sg==", + "dev": true, + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm-registry-fetch/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-registry-fetch/node_modules/ssri": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.0.tgz", + "integrity": "sha512-Y1Z6J8UYnexKFN1R/hxUaYoY2LVdKEzziPmVAFKiKX8fiwvCJTVzn/xYE9TEWod5OVyNfIHHuVfIEuBClL/uJQ==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/ora/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-retry/node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pacote": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-12.0.3.tgz", + "integrity": "sha512-CdYEl03JDrRO3x18uHjBYA9TyoW8gy+ThVcypcDkxPtKlw76e4ejhYB6i9lJ+/cebbjpqPW/CijjqxwDTts8Ow==", + "dev": true, + "dependencies": { + "@npmcli/git": "^2.1.0", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^2.0.0", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^3.0.0", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^12.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parse5-html-rewriting-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", + "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1", + "parse5-sax-parser": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-sax-parser": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", + "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/piscina": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", + "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", + "dev": true, + "dependencies": { + "eventemitter-asyncresource": "^1.0.0", + "hdr-histogram-js": "^2.0.1", + "hdr-histogram-percentiles-obj": "^3.0.0" + }, + "optionalDependencies": { + "nice-napi": "^1.0.2" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "dependencies": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/portfinder/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/postcss": { + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", + "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", + "dev": true, + "dependencies": { + "nanoid": "^3.1.30", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.0.tgz", + "integrity": "sha512-b4g9eagFGq9T5SWX4+USfVyjIb3liPnjhHHRMP7FMB2kFVpYyfEscV0wP3eaXhKlcHKUut8lt5BGoeylWA/dBQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.2" + }, + "peerDependencies": { + "postcss": "^8.0.2" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.2.tgz", + "integrity": "sha512-DXVtwUhIk4f49KK5EGuEdgx4Gnyj6+t2jBSEmxvpIK9QI40tWrpS2Pua8Q7iIZWBrki2QOaeUdEaLPPa91K0RQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.3.tgz", + "integrity": "sha512-fESawWJCrBV035DcbKRPAVmy21LpoyiXdPTuHUfWJ14ZRjY7Y7PA6P4g8z6LQGYhU1WAxkTxjIjurXzoe68Glw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.0.2.tgz", + "integrity": "sha512-SFc3MaocHaQ6k3oZaFwH8io6MdypkUtEy/eXzXEB1vEQlO3S3oDc/FSZA8AsS04Z25RirQhlDlHLh3dn7XewWw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-custom-media": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz", + "integrity": "sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-custom-properties": { + "version": "12.1.7", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.7.tgz", + "integrity": "sha512-N/hYP5gSoFhaqxi2DPCmvto/ZcRDVjE3T1LiAMzc/bg53hvhcHOLpXOHb526LzBBp5ZlAUhkuot/bfpmpgStJg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.0.tgz", + "integrity": "sha512-/1iyBhz/W8jUepjGyu7V1OPcGbc636snN1yXEQCinb6Bwt7KxsiU7/bLQlp8GwAXzCh7cobBU5odNn/2zQWR8Q==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.2" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.4.tgz", + "integrity": "sha512-I8epwGy5ftdzNWEYok9VjW9whC4xnelAtbajGv4adql4FIF09rnrxnA9Y8xSHN47y7gqFIv10C5+ImsLeJpKBw==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.1.tgz", + "integrity": "sha512-jM+CGkTs4FcG53sMPjrrGE0rIvLDdCrqMzgDC5fLI7JHDO7o6QG8C5TQBtExb13hdBdoH9C2QVbG4jo2y9lErQ==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "dev": true, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.3.tgz", + "integrity": "sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-image-set-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.6.tgz", + "integrity": "sha512-KfdC6vg53GC+vPd2+HYzsZ6obmPqOk6HY09kttU19+Gj1nC3S3XBVEXDHxkhxTohgZqzbUb94bKXvKDnYWBm/A==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-import": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.2.tgz", + "integrity": "sha512-BJ2pVK4KhUyMcqjuKs9RijV5tatNzNa73e/32aBVE/ejYPe37iH+6vAu9WvqUkB5OAYgLHzbSvzHnorybJCm9g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "dev": true, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-lab-function": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.0.tgz", + "integrity": "sha512-Zb1EO9DGYfa3CP8LhINHCcTTCTLI+R3t7AX2mKsDzdgVQ/GkCpHOTgOr6HBHslP7XDdVbqgHW5vvRPMdVANQ8w==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nesting": { + "version": "10.1.6", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.6.tgz", + "integrity": "sha512-8C0X0pOOShlgqYkCzB4wlWLyulos+GXFpw7r2+x7g+ROQ1RwN8MlN2NCcpNQScNBPfbjxbjwY8e25raTcEXqmg==", + "dev": true, + "dependencies": { + "@csstools/selector-specificity": "1.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-overflow-shorthand": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.3.tgz", + "integrity": "sha512-CxZwoWup9KXzQeeIxtgOciQ00tDtnylYIlJBBODqkgS/PU2jISuWOL/mYLHmZb9ZhZiCaNKsCRiLp22dZUtNsg==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "dev": true, + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/postcss-place": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.4.tgz", + "integrity": "sha512-MrgKeiiu5OC/TETQO45kV3npRjOFxEHthsqGtkh3I1rPbZSbXGD/lZVi9j13cYh+NA8PIAPyk6sGjT9QbRyvSg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-preset-env": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.2.3.tgz", + "integrity": "sha512-Ok0DhLfwrcNGrBn8sNdy1uZqWRk/9FId0GiQ39W4ILop5GHtjJs8bu1MY9isPwHInpVEPWjb4CEcEaSbBLpfwA==", + "dev": true, + "dependencies": { + "autoprefixer": "^10.4.2", + "browserslist": "^4.19.1", + "caniuse-lite": "^1.0.30001299", + "css-blank-pseudo": "^3.0.2", + "css-has-pseudo": "^3.0.3", + "css-prefers-color-scheme": "^6.0.2", + "cssdb": "^5.0.0", + "postcss-attribute-case-insensitive": "^5.0.0", + "postcss-color-functional-notation": "^4.2.1", + "postcss-color-hex-alpha": "^8.0.2", + "postcss-color-rebeccapurple": "^7.0.2", + "postcss-custom-media": "^8.0.0", + "postcss-custom-properties": "^12.1.2", + "postcss-custom-selectors": "^6.0.0", + "postcss-dir-pseudo-class": "^6.0.3", + "postcss-double-position-gradients": "^3.0.4", + "postcss-env-function": "^4.0.4", + "postcss-focus-visible": "^6.0.3", + "postcss-focus-within": "^5.0.3", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.2", + "postcss-image-set-function": "^4.0.4", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.0.3", + "postcss-logical": "^5.0.3", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.1.2", + "postcss-overflow-shorthand": "^3.0.2", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.3", + "postcss-pseudo-class-any-link": "^7.0.2", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^5.0.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.4.tgz", + "integrity": "sha512-JxRcLXm96u14N3RzFavPIE9cRPuOqLDuzKeBsqi4oRk4vt8n0A7I0plFs/VXTg7U2n7g/XkQi0OwqTO3VWBfEg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "dev": true, + "peerDependencies": { + "postcss": "^8.0.3" + } + }, + "node_modules/postcss-selector-not": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-5.0.0.tgz", + "integrity": "sha512-/2K3A4TCP9orP4TNS7u3tGdRFVKqz/E6pX3aGnriPG0jU78of8wsUcqE4QAhWEU0d+WnMSF93Ah3F//vUtK+iQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true, + "optional": true + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true, + "engines": { + "node": ">=0.9" + } + }, + "node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-package-json-fast": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "dev": true, + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "node_modules/regenerator-transform": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", + "dev": true + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", + "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "dev": true, + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/sass": { + "version": "1.49.9", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.9.tgz", + "integrity": "sha512-YlYWkkHP9fbwaFRZQRXgDi3mXZShslVmmo+FVK3kHLUELHHEYrCmL1x6IUjC7wLS6VuJSAFXRQS/DxdsC4xL1A==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/sass-loader": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.4.0.tgz", + "integrity": "sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==", + "dev": true, + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "node_modules/selfsigned": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz", + "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==", + "dev": true, + "dependencies": { + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/send/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.1.tgz", + "integrity": "sha512-0y9pnIso5a9i+lJmsCdtmTTgJFFSvNQKDnPQRz28mGNnxbmqYg2QPtJTLFxhymFZhAIn50eHAKzJeiNaKr+yUQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.2.0", + "socket.io-adapter": "~2.4.0", + "socket.io-parser": "~4.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", + "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", + "dev": true + }, + "node_modules/socket.io-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", + "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", + "dev": true, + "dependencies": { + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/socks": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", + "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", + "dev": true, + "dependencies": { + "ip": "^1.1.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.0.tgz", + "integrity": "sha512-wWqJhjb32Q6GsrUqzuFkukxb/zzide5quXYcMVpIjxalDBBYy2nqKCFQ/9+Ie4dvOYSQdOk3hUlZSdzZOd3zMQ==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.1.tgz", + "integrity": "sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/streamroller": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.9.tgz", + "integrity": "sha512-Y46Aq/ftqFP6Wb6sK79hgnZeRfEVz2F0nquBy4lMftUuJoTiwKa6Y96AWAUGV1F3CjhFark9sQmzL9eDpltkRw==", + "dev": true, + "dependencies": { + "date-format": "^4.0.10", + "debug": "^4.3.4", + "fs-extra": "^10.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/streamroller/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/stylus": { + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.56.0.tgz", + "integrity": "sha512-Ev3fOb4bUElwWu4F9P9WjnnaSpc8XB9OFHSFZSKMFL1CE1oM+oFXWEgAqPmmZIyhBihuqIQlFsVTypiiS9RxeA==", + "dev": true, + "dependencies": { + "css": "^3.0.0", + "debug": "^4.3.2", + "glob": "^7.1.6", + "safer-buffer": "^2.1.2", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + } + }, + "node_modules/stylus-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-6.2.0.tgz", + "integrity": "sha512-5dsDc7qVQGRoc6pvCL20eYgRUxepZ9FpeK28XhdXaIPP6kXr6nI1zAAKFQgP5OBkOfKaURp4WUpJzspg1f01Gg==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.7", + "klona": "^2.0.4", + "normalize-path": "^3.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "stylus": ">=0.52.4", + "webpack": "^5.0.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/terser": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.11.0.tgz", + "integrity": "sha512-uCA9DLanzzWSsN1UirKwylhhRz3aKPInlfmpGfw8VN6jHsAtu8HJtIpeeHHK23rxnE/cDc+yvmq5wqkIC6Kn0A==", + "dev": true, + "dependencies": { + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", + "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", + "dev": true, + "dependencies": { + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-assert": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", + "dev": true + }, + "node_modules/typescript": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", + "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "engines": { + "node": "*" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", + "dev": true, + "dependencies": { + "builtins": "^1.0.3" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webpack": { + "version": "5.70.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.70.0.tgz", + "integrity": "sha512-ZMWWy8CeuTTjCxbeaQI21xSswseF2oNOwc70QSKNePvmxE7XW36i7vpBMYZFAUHPwQiEbNGCEYIOOlyRbdGmxw==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.9.2", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.0.tgz", + "integrity": "sha512-MouJz+rXAm9B1OTOYaJnn6rtD/lWZPy2ufQCH3BPs8Rloh/Du6Jze4p7AeLYHkVi0giJnYLaSGDC7S+GM9arhg==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.2.2", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.3.tgz", + "integrity": "sha512-mlxq2AsIw2ag016nixkzUkdyOE8ST2GTy34uKSABp1c4nhjZvH90D5ZRR+UOLSsG4Z3TFahAi72a3ymRtfRm+Q==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/serve-index": "^1.9.1", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.2.2", + "ansi-html-community": "^0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^3.5.2", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "default-gateway": "^6.0.3", + "del": "^6.0.0", + "express": "^4.17.1", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.0", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "portfinder": "^1.0.28", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "spdy": "^4.0.2", + "strip-ansi": "^7.0.0", + "webpack-dev-middleware": "^5.3.0", + "ws": "^8.1.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server/node_modules/strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-subresource-integrity": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", + "dev": true, + "dependencies": { + "typed-assert": "^1.0.8" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "html-webpack-plugin": ">= 5.0.0-beta.1 < 6", + "webpack": "^5.12.0" + }, + "peerDependenciesMeta": { + "html-webpack-plugin": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/zone.js": { + "version": "0.11.5", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.5.tgz", + "integrity": "sha512-D1/7VxEuQ7xk6z/kAROe4SUbd9CzxY4zOwVGnGHerd/SgLIVU5f4esDzQUsOCeArn933BZfWMKydH7l7dPEp0g==", + "dependencies": { + "tslib": "^2.3.0" + } + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@angular-devkit/architect": { + "version": "0.1303.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1303.6.tgz", + "integrity": "sha512-Quh8KzO17PZH38mrDlBihrT6TioTnD8I+nSXuTZIqHwDpyFCTB9wBm9wC1J6HzMzEJ1GoYWEH/ukfbVpmj8ghw==", + "dev": true, + "requires": { + "@angular-devkit/core": "13.3.6", + "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/build-angular": { + "version": "13.3.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-13.3.6.tgz", + "integrity": "sha512-VrEw9AhS0BBPRA59aZZYDXelXX8SdTfo7mW1gSeuvyeqk8RpMdjHWJPUmuvNJro6s5zOe4lc9dVrGxdevCbQFQ==", + "dev": true, + "requires": { + "@ampproject/remapping": "2.2.0", + "@angular-devkit/architect": "0.1303.6", + "@angular-devkit/build-webpack": "0.1303.6", + "@angular-devkit/core": "13.3.6", + "@babel/core": "7.16.12", + "@babel/generator": "7.16.8", + "@babel/helper-annotate-as-pure": "7.16.7", + "@babel/plugin-proposal-async-generator-functions": "7.16.8", + "@babel/plugin-transform-async-to-generator": "7.16.8", + "@babel/plugin-transform-runtime": "7.16.10", + "@babel/preset-env": "7.16.11", + "@babel/runtime": "7.16.7", + "@babel/template": "7.16.7", + "@discoveryjs/json-ext": "0.5.6", + "@ngtools/webpack": "13.3.6", + "ansi-colors": "4.1.1", + "babel-loader": "8.2.5", + "babel-plugin-istanbul": "6.1.1", + "browserslist": "^4.9.1", + "cacache": "15.3.0", + "circular-dependency-plugin": "5.2.2", + "copy-webpack-plugin": "10.2.1", + "core-js": "3.20.3", + "critters": "0.0.16", + "css-loader": "6.5.1", + "esbuild": "0.14.22", + "esbuild-wasm": "0.14.22", + "glob": "7.2.0", + "https-proxy-agent": "5.0.0", + "inquirer": "8.2.0", + "jsonc-parser": "3.0.0", + "karma-source-map-support": "1.4.0", + "less": "4.1.2", + "less-loader": "10.2.0", + "license-webpack-plugin": "4.0.2", + "loader-utils": "3.2.0", + "mini-css-extract-plugin": "2.5.3", + "minimatch": "3.0.5", + "open": "8.4.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.2.0", + "postcss": "8.4.5", + "postcss-import": "14.0.2", + "postcss-loader": "6.2.1", + "postcss-preset-env": "7.2.3", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "5.0.0", + "rxjs": "6.6.7", + "sass": "1.49.9", + "sass-loader": "12.4.0", + "semver": "7.3.5", + "source-map-loader": "3.0.1", + "source-map-support": "0.5.21", + "stylus": "0.56.0", + "stylus-loader": "6.2.0", + "terser": "5.11.0", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.3.1", + "webpack": "5.70.0", + "webpack-dev-middleware": "5.3.0", + "webpack-dev-server": "4.7.3", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "5.1.0" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + } + } + }, + "@angular-devkit/build-webpack": { + "version": "0.1303.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1303.6.tgz", + "integrity": "sha512-T2Uwt57RGPNH5OOblWcWd4Q4NaOK0f2jNTH29L6kSFVlYQ7kKCe9xo3YdzVdiY5b4w/hfnDlpGR+J3aSkkmBRw==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.1303.6", + "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/core": { + "version": "13.3.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.6.tgz", + "integrity": "sha512-ZmD586B+RnM2CG5+jbXh2NVfIydTc/yKSjppYDDOv4I530YBm6vpfZMwClpiNk6XLbMv7KqX4Tlr4wfxlPYYbA==", + "dev": true, + "requires": { + "ajv": "8.9.0", + "ajv-formats": "2.1.1", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.7", + "source-map": "0.7.3" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/schematics": { + "version": "13.3.6", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.3.6.tgz", + "integrity": "sha512-yLh5xc92C/FiaAp27coPiKWpSUmwoXF7vMxbJYJTyOXlt0mUITAEAwtrZQNr4yAxW/yvgTdyg7PhXaveQNTUuQ==", + "dev": true, + "requires": { + "@angular-devkit/core": "13.3.6", + "jsonc-parser": "3.0.0", + "magic-string": "0.25.7", + "ora": "5.4.1", + "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular/animations": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-13.3.9.tgz", + "integrity": "sha512-PIspkNm1r7Uq1F3/c24mTQjCbKvl84Iy5kRmKOtjxp9uBGg1Dy+akw29hJt2FAqKa5yKFustBFCgWKypqgSoUQ==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/cli": { + "version": "13.3.6", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-13.3.6.tgz", + "integrity": "sha512-+OC7uspa8oDGQzcpml3DI8XyLvYurhSFhcmLPsyY/naHAV78NKSNf3dIWMPNozAioDzkZXPZXH0dwSdb+cOeQA==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.1303.6", + "@angular-devkit/core": "13.3.6", + "@angular-devkit/schematics": "13.3.6", + "@schematics/angular": "13.3.6", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.1", + "debug": "4.3.3", + "ini": "2.0.0", + "inquirer": "8.2.0", + "jsonc-parser": "3.0.0", + "npm-package-arg": "8.1.5", + "npm-pick-manifest": "6.1.1", + "open": "8.4.0", + "ora": "5.4.1", + "pacote": "12.0.3", + "resolve": "1.22.0", + "semver": "7.3.5", + "symbol-observable": "4.0.0", + "uuid": "8.3.2" + } + }, + "@angular/common": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-13.3.9.tgz", + "integrity": "sha512-+bTleNL1XGlzuxLbVbsol82/33IW2pJasQN8ViraxKIElT2F8ooBJevIBMCSIcdvxvMBGAjn4ayQJr6tkPqPaw==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/compiler": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-13.3.9.tgz", + "integrity": "sha512-fXmcN9PIUTJ9Vw2QyQE9vtW95K5ML9bVI7409Zf3DAQJEo4IhX2eUjgiGF3RtSn9Kdjj3tZY0xSbGGwp1hTBIA==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/compiler-cli": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-13.3.9.tgz", + "integrity": "sha512-jf4iG7uqE1ZW2mJgKJyDyFby/sVQDlGOtUl68TMf8r06koWeny5nk/LhL7jp3Q6rgN2ZdYALtPTRSj7HVQD8pA==", + "dev": true, + "requires": { + "@babel/core": "^7.17.2", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.11.0", + "magic-string": "^0.26.0", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + }, + "dependencies": { + "@babel/core": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.12.tgz", + "integrity": "sha512-44ODe6O1IVz9s2oJE3rZ4trNNKTX9O7KpQpfAP4t8QII/zwrVRHL7i2pxhqtcY7tqMLrrKfMlBKnm1QlrRFs5w==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.12", + "@babel/helper-compilation-targets": "^7.17.10", + "@babel/helper-module-transforms": "^7.17.12", + "@babel/helpers": "^7.17.9", + "@babel/parser": "^7.17.12", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.12", + "@babel/types": "^7.17.12", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.12.tgz", + "integrity": "sha512-V49KtZiiiLjH/CnIW6OjJdrenrGoyh6AmKQ3k2AZFKozC1h846Q4NYlZ5nqAigPDUXfGzC88+LOUuG8yKd2kCw==", + "dev": true, + "requires": { + "@babel/types": "^7.17.12", + "@jridgewell/gen-mapping": "^0.3.0", + "jsesc": "^2.5.1" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz", + "integrity": "sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "magic-string": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", + "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.8" + } + } + } + }, + "@angular/core": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-13.3.9.tgz", + "integrity": "sha512-LaY3yBDgN/efG11x/cwVeuC4gUG3YUMmk/sgAP3L1VGawYOiKoJ76decFpy6y4UgYPShQQRzpZQEVXF405jrLg==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/forms": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-13.3.9.tgz", + "integrity": "sha512-eNOsqMVrMsBceoAJ9pS+2qQDWsgwt62q7abqfYdzSdkjWbnLrtaIPP6iYMGQke1pIcdUSyoun29VsdQTSXXksg==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/platform-browser": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-13.3.9.tgz", + "integrity": "sha512-1l0IVYFbKCEfACR60bfLjH35BYP69CerIW9Ok58pedp0MIUsvPttBzUjM5HhW+3jhvNyO0cCMaK4r5kKTRMo4w==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/platform-browser-dynamic": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-13.3.9.tgz", + "integrity": "sha512-flyfoJG9vBSj3rmH/jUNaOPGfGlGHSj4v34OL16Qjk3M5bxbQKxBYNrDAUwk0Ve4S4qUfXF7ZDE0v1vUvn+3MA==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/router": { + "version": "13.3.9", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-13.3.9.tgz", + "integrity": "sha512-M9j3ZscdRLsTnOLware1Yyw87JlRw/axoeZcN8JnTs3ltCc0+UMEJ22q0s8ud6j9GmYiA0+kmaM35OsEoHX72g==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@assemblyscript/loader": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", + "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", + "dev": true + }, + "@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.16.7" + } + }, + "@babel/compat-data": { + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.10.tgz", + "integrity": "sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==", + "dev": true + }, + "@babel/core": { + "version": "7.16.12", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz", + "integrity": "sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.16.8", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helpers": "^7.16.7", + "@babel/parser": "^7.16.12", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.10", + "@babel/types": "^7.16.8", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", + "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.8", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", + "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz", + "integrity": "sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.10", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.12.tgz", + "integrity": "sha512-sZoOeUTkFJMyhqCei2+Z+wtH/BehW8NVKQt7IRUQlRiOARuXymJYfN/FCcI8CvVbR0XVyDM6eLFOlR7YtiXnew==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.17.9", + "@babel/helper-member-expression-to-functions": "^7.17.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.12.tgz", + "integrity": "sha512-b2aZrV4zvutr9AIa6/gA3wsZKRwTKYoDxYiFKcESS3Ug2GTXzwBEvMuuFLhCQpEnRXs1zng4ISAXSUxxKBIcxw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "regexpu-core": "^5.0.1" + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz", + "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", + "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-function-name": { + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", + "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", + "dev": true, + "requires": { + "@babel/template": "^7.16.7", + "@babel/types": "^7.17.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", + "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", + "dev": true, + "requires": { + "@babel/types": "^7.17.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-module-transforms": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.12.tgz", + "integrity": "sha512-t5s2BeSWIghhFRPh9XMn6EIGmvn8Lmw5RVASJzkIx1mSemubQQBNIZiQD7WzaFmaHIrjAec4x8z9Yx8SjJ1/LA==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.12", + "@babel/types": "^7.17.12" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", + "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz", + "integrity": "sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", + "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-wrap-function": "^7.16.8", + "@babel/types": "^7.16.8" + } + }, + "@babel/helper-replace-supers": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", + "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-simple-access": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", + "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "dev": true, + "requires": { + "@babel/types": "^7.17.0" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", + "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dev": true, + "requires": { + "@babel/types": "^7.16.7" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", + "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.8", + "@babel/types": "^7.16.8" + } + }, + "@babel/helpers": { + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz", + "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", + "dev": true, + "requires": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.9", + "@babel/types": "^7.17.0" + } + }, + "@babel/highlight": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz", + "integrity": "sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.12.tgz", + "integrity": "sha512-FLzHmN9V3AJIrWfOpvRlZCeVg/WLdicSnTMsLur6uDj9TT8ymUlG9XxURdW/XvuygK+2CW0poOJABdA4m/YKxA==", + "dev": true + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.17.12.tgz", + "integrity": "sha512-xCJQXl4EeQ3J9C4yOmpTrtVGmzpm2iSzyxbkZHw7UCnZBftHpF/hpII80uWVyVrc40ytIClHjgWGTG1g/yB+aw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.17.12.tgz", + "integrity": "sha512-/vt0hpIw0x4b6BLKUkwlvEoiGZYYLNZ96CzyHYPbtG2jZGz6LBe7/V+drYrc/d+ovrF9NBi0pmtvmNb/FsWtRQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.17.12" + } + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz", + "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.17.12.tgz", + "integrity": "sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.12.tgz", + "integrity": "sha512-8ILyDG6eL14F8iub97dVc8q35Md0PJYAnA5Kz9NACFOkt6ffCcr0FISyUPKHsvuAy36fkpIitxZ9bVYPFMGQHA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", + "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.17.12.tgz", + "integrity": "sha512-j7Ye5EWdwoXOpRmo5QmRyHPsDIe6+u70ZYZrd7uz+ebPYFKfRcLcNu3Ro0vOlJ5zuv8rU7xa+GttNiRzX56snQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.17.12.tgz", + "integrity": "sha512-rKJ+rKBoXwLnIn7n6o6fulViHMrOThz99ybH+hKHcOZbnN14VuMnH9fo2eHE69C8pO4uX1Q7t2HYYIDmv8VYkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.17.12.tgz", + "integrity": "sha512-EqFo2s1Z5yy+JeJu7SFfbIUtToJTVlC61/C7WLKDntSw4Sz6JNAIfL7zQ74VvirxpjB5kz/kIx0gCcb+5OEo2Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.17.12.tgz", + "integrity": "sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", + "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.12.tgz", + "integrity": "sha512-6l9cO3YXXRh4yPCPRA776ZyJ3RobG4ZKJZhp7NDRbKIOeV3dBPG8FXCF7ZtiO2RTCIOkQOph1xDDcc01iWVNjQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.10", + "@babel/helper-compilation-targets": "^7.17.10", + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.17.12" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", + "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.17.12.tgz", + "integrity": "sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.17.12.tgz", + "integrity": "sha512-SllXoxo19HmxhDWm3luPz+cPhtoTSKLJE9PXshsfrOzBqs60QP0r8OaJItrPhAj0d7mZMnNF0Y1UUggCDgMz1A==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.17.12.tgz", + "integrity": "sha512-/6BtVi57CJfrtDNKfK5b66ydK2J5pXUKBKSPD2G1whamMuEnZWgoOIfO8Vf9F/DoD4izBLD/Au4NMQfruzzykg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.17.12.tgz", + "integrity": "sha512-Wb9qLjXf3ZazqXA7IvI7ozqRIXIGPtSo+L5coFmEkhTQK18ao4UDDD0zdTGAarmbLj2urpRwrc6893cu5Bfh0A==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.17.12.tgz", + "integrity": "sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz", + "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", + "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.17.12.tgz", + "integrity": "sha512-jw8XW/B1i7Lqwqj2CbrViPcZijSxfguBWZP2aN59NHgxUyO/OcO1mfdCxH13QhN5LbWhPkX+f+brKGhZTiqtZQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.17.12.tgz", + "integrity": "sha512-cvO7lc7pZat6BsvH6l/EGaI8zpl8paICaoGk+7x7guvtfak/TbIf66nYmJOH13EuG0H+Xx3M+9LQDtSvZFKXKw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.17.9", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.17.12.tgz", + "integrity": "sha512-a7XINeplB5cQUWMg1E/GI1tFz3LfK021IjV1rj1ypE+R7jHm+pIHmHl25VNkZxtx9uuYp7ThGk8fur1HHG7PgQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.12.tgz", + "integrity": "sha512-P8pt0YiKtX5UMUL5Xzsc9Oyij+pJE6JuC+F1k0/brq/OOGs5jDa1If3OY0LRWGvJsJhI+8tsiecL3nJLc0WTlg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", + "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.17.12.tgz", + "integrity": "sha512-EA5eYFUG6xeerdabina/xIoB95jJ17mAkR8ivx6ZSu9frKShBjpOGZPn511MTDTkiCO+zXnzNczvUM69YSf3Zw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", + "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.17.12.tgz", + "integrity": "sha512-76lTwYaCxw8ldT7tNmye4LLwSoKDbRCBzu6n/DcK/P3FOR29+38CIIaVIZfwol9By8W/QHORYEnYSLuvcQKrsg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", + "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.17.12.tgz", + "integrity": "sha512-8iRkvaTjJciWycPIZ9k9duu663FT7VrBdNqNgxnVXEFwOIp55JWcZd23VBRySYbnS3PwQ3rGiabJBBBGj5APmQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", + "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.17.12.tgz", + "integrity": "sha512-p5rt9tB5Ndcc2Za7CeNxVf7YAjRcUMR6yi8o8tKjb9KhRkEvXwa+C0hj6DA5bVDkKRxB0NYhMUGbVKoFu4+zEA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.12.tgz", + "integrity": "sha512-tVPs6MImAJz+DiX8Y1xXEMdTk5Lwxu9jiPjlS+nv5M2A59R7+/d1+9A8C/sbuY0b3QjIxqClkj6KAplEtRvzaA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/helper-simple-access": "^7.17.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.12.tgz", + "integrity": "sha512-NVhDb0q00hqZcuLduUf/kMzbOQHiocmPbIxIvk23HLiEqaTKC/l4eRxeC7lO63M72BmACoiKOcb9AkOAJRerpw==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-module-transforms": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/helper-validator-identifier": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.17.12.tgz", + "integrity": "sha512-BnsPkrUHsjzZGpnrmJeDFkOMMljWFHPjDc9xDcz71/C+ybF3lfC3V4m3dwXPLZrE5b3bgd4V+3/Pj+3620d7IA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.17.12.tgz", + "integrity": "sha512-vWoWFM5CKaTeHrdUJ/3SIOTRV+MBVGybOC9mhJkaprGNt5demMymDW24yC74avb915/mIRe3TgNb/d8idvnCRA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.17.12", + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.17.12.tgz", + "integrity": "sha512-CaOtzk2fDYisbjAD4Sd1MTKGVIpRtx9bWLyj24Y/k6p4s4gQ3CqDGJauFJxt8M/LEx003d0i3klVqnN73qvK3w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", + "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.17.12.tgz", + "integrity": "sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", + "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.17.9.tgz", + "integrity": "sha512-Lc2TfbxR1HOyn/c6b4Y/b6NHoTb67n/IoWLxTu4kC7h4KQnWlhCq2S8Tx0t2SVvv5Uu87Hs+6JEJ5kt2tYGylQ==", + "dev": true, + "requires": { + "regenerator-transform": "^0.15.0" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.17.12.tgz", + "integrity": "sha512-1KYqwbJV3Co03NIi14uEHW8P50Md6KqFgt0FfpHdK6oyAHQVTosgPuPSiWud1HX0oYJ1hGRRlk0fP87jFpqXZA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.10.tgz", + "integrity": "sha512-9nwTiqETv2G7xI4RvXHNfpGdr8pAA+Q/YtN3yLK7OoK7n9OibVm/xymJ838a9A6E/IciOLPj82lZk0fW6O4O7w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", + "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.17.12.tgz", + "integrity": "sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", + "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.17.12.tgz", + "integrity": "sha512-kAKJ7DX1dSRa2s7WN1xUAuaQmkTpN+uig4wCKWivVXIObqGbVTUlSavHyfI2iZvz89GFAMGm9p2DBJ4Y1Tp0hw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.17.12.tgz", + "integrity": "sha512-Q8y+Jp7ZdtSPXCThB6zjQ74N3lj0f6TDh1Hnf5B+sYlzQ8i5Pjp8gW0My79iekSpT4WnI06blqP6DT0OmaXXmw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.17.12" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", + "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", + "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + } + }, + "@babel/preset-env": { + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz", + "integrity": "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.16.8", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-option": "^7.16.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-async-generator-functions": "^7.16.8", + "@babel/plugin-proposal-class-properties": "^7.16.7", + "@babel/plugin-proposal-class-static-block": "^7.16.7", + "@babel/plugin-proposal-dynamic-import": "^7.16.7", + "@babel/plugin-proposal-export-namespace-from": "^7.16.7", + "@babel/plugin-proposal-json-strings": "^7.16.7", + "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", + "@babel/plugin-proposal-numeric-separator": "^7.16.7", + "@babel/plugin-proposal-object-rest-spread": "^7.16.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", + "@babel/plugin-proposal-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-private-methods": "^7.16.11", + "@babel/plugin-proposal-private-property-in-object": "^7.16.7", + "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.16.7", + "@babel/plugin-transform-async-to-generator": "^7.16.8", + "@babel/plugin-transform-block-scoped-functions": "^7.16.7", + "@babel/plugin-transform-block-scoping": "^7.16.7", + "@babel/plugin-transform-classes": "^7.16.7", + "@babel/plugin-transform-computed-properties": "^7.16.7", + "@babel/plugin-transform-destructuring": "^7.16.7", + "@babel/plugin-transform-dotall-regex": "^7.16.7", + "@babel/plugin-transform-duplicate-keys": "^7.16.7", + "@babel/plugin-transform-exponentiation-operator": "^7.16.7", + "@babel/plugin-transform-for-of": "^7.16.7", + "@babel/plugin-transform-function-name": "^7.16.7", + "@babel/plugin-transform-literals": "^7.16.7", + "@babel/plugin-transform-member-expression-literals": "^7.16.7", + "@babel/plugin-transform-modules-amd": "^7.16.7", + "@babel/plugin-transform-modules-commonjs": "^7.16.8", + "@babel/plugin-transform-modules-systemjs": "^7.16.7", + "@babel/plugin-transform-modules-umd": "^7.16.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", + "@babel/plugin-transform-new-target": "^7.16.7", + "@babel/plugin-transform-object-super": "^7.16.7", + "@babel/plugin-transform-parameters": "^7.16.7", + "@babel/plugin-transform-property-literals": "^7.16.7", + "@babel/plugin-transform-regenerator": "^7.16.7", + "@babel/plugin-transform-reserved-words": "^7.16.7", + "@babel/plugin-transform-shorthand-properties": "^7.16.7", + "@babel/plugin-transform-spread": "^7.16.7", + "@babel/plugin-transform-sticky-regex": "^7.16.7", + "@babel/plugin-transform-template-literals": "^7.16.7", + "@babel/plugin-transform-typeof-symbol": "^7.16.7", + "@babel/plugin-transform-unicode-escapes": "^7.16.7", + "@babel/plugin-transform-unicode-regex": "^7.16.7", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.16.8", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "core-js-compat": "^3.20.2", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/runtime": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", + "integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + } + }, + "@babel/traverse": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.12.tgz", + "integrity": "sha512-zULPs+TbCvOkIFd4FrG53xrpxvCBwLIgo6tO0tJorY7YV2IWFxUfS/lXDJbGgfyYt9ery/Gxj2niwttNnB0gIw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.12", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.17.9", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.12", + "@babel/types": "^7.17.12", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "@babel/generator": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.12.tgz", + "integrity": "sha512-V49KtZiiiLjH/CnIW6OjJdrenrGoyh6AmKQ3k2AZFKozC1h846Q4NYlZ5nqAigPDUXfGzC88+LOUuG8yKd2kCw==", + "dev": true, + "requires": { + "@babel/types": "^7.17.12", + "@jridgewell/gen-mapping": "^0.3.0", + "jsesc": "^2.5.1" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz", + "integrity": "sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/types": { + "version": "7.17.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.12.tgz", + "integrity": "sha512-rH8i29wcZ6x9xjzI5ILHL/yZkbQnCERdHlogKuIb4PUr7do4iT8DPekrTbBLWTnRQm6U0GYABbTMSzijmEqlAg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + } + }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true + }, + "@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/selector-specificity": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-1.0.0.tgz", + "integrity": "sha512-RkYG5KiGNX0fJ5YoI0f4Wfq2Yo74D25Hru4fxTOioYdQvHBxcrrtTTyT5Ozzh2ejcNrhFy7IEts2WyEY7yi5yw==", + "dev": true, + "requires": {} + }, + "@discoveryjs/json-ext": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", + "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==", + "dev": true + }, + "@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz", + "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.1.tgz", + "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz", + "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz", + "integrity": "sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@ngtools/webpack": { + "version": "13.3.6", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-13.3.6.tgz", + "integrity": "sha512-QSdFtQIUgnDvM0EXFOpVRp5HTN0U4B9z60fHHfKKzxpNuU3z9EFzJoDUMZ8WabLXtWboUZWCSx0x3tdBt/sVQw==", + "dev": true, + "requires": {} + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "requires": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "@npmcli/git": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz", + "integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==", + "dev": true, + "requires": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "@npmcli/installed-package-contents": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", + "dev": true, + "requires": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@npmcli/node-gyp": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz", + "integrity": "sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA==", + "dev": true + }, + "@npmcli/promise-spawn": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz", + "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==", + "dev": true, + "requires": { + "infer-owner": "^1.0.4" + } + }, + "@npmcli/run-script": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-2.0.0.tgz", + "integrity": "sha512-fSan/Pu11xS/TdaTpTB0MRn9guwGU8dye+x56mEVgBEd/QsybBbYcAL0phPXi8SGWFEChkQd6M9qL4y6VOpFig==", + "dev": true, + "requires": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "node-gyp": "^8.2.0", + "read-package-json-fast": "^2.0.1" + } + }, + "@schematics/angular": { + "version": "13.3.6", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.3.6.tgz", + "integrity": "sha512-BGBmIasjipBxQhV+UdN8B5P73SBXgBPkc7rcOK3Py+xpqMcoTWn290nqxIxLxRVmSeHLabE7+n1m3WnCumlm9Q==", + "dev": true, + "requires": { + "@angular-devkit/core": "13.3.6", + "@angular-devkit/schematics": "13.3.6", + "jsonc-parser": "3.0.0" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/component-emitter": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz", + "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==", + "dev": true + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", + "dev": true + }, + "@types/eslint": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.2.tgz", + "integrity": "sha512-Z1nseZON+GEnFjJc04sv4NSALGjhFwy6K0HXt7qsn5ArfAKtb63dXNJHf+1YW6IpOIYRBGUbu3GwJdj8DGnCjA==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/jasmine": { + "version": "3.10.6", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.10.6.tgz", + "integrity": "sha512-twY9adK/vz72oWxCWxzXaxoDtF9TpfEEsxvbc1ibjF3gMD/RThSuSud/GKUTR3aJnfbivAbC/vLqhY+gdWCHfA==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "@types/node": { + "version": "12.20.52", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.52.tgz", + "integrity": "sha512-cfkwWw72849SNYp3Zx0IcIs25vABmFh73xicxhCkTcvtZQeIez15PpwQN8fY3RD7gv1Wrxlc9MEtfMORZDEsGw==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "dev": true + }, + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "requires": {} + }, + "adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "agentkeepalive": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", + "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + } + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", + "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "are-we-there-yet": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz", + "integrity": "sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", + "dev": true + }, + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "autoprefixer": { + "version": "10.4.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.7.tgz", + "integrity": "sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==", + "dev": true, + "requires": { + "browserslist": "^4.20.3", + "caniuse-lite": "^1.0.30001335", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "dev": true, + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", + "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.3.1", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", + "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.1", + "core-js-compat": "^3.21.0" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", + "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.1" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz", + "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001332", + "electron-to-chromium": "^1.4.118", + "escalade": "^3.1.1", + "node-releases": "^2.0.3", + "picocolors": "^1.0.0" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", + "dev": true + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, + "cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "requires": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001341", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001341.tgz", + "integrity": "sha512-2SodVrFFtvGENGCv0ChVJIDQ0KPaS1cg7/qtfMaICgeMolDdo/Z2OD32F0Aq9yl6F4YFwGPBS5AaPqNYiW4PoA==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "circular-dependency-plugin": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", + "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", + "dev": true, + "requires": {} + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", + "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", + "dev": true + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true + }, + "colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "requires": { + "is-what": "^3.14.1" + } + }, + "copy-webpack-plugin": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.1.tgz", + "integrity": "sha512-nr81NhCAIpAWXGCK5thrKmfCQ6GDY0L5RN0U+BnIn/7Us55+UCex5ANNsNKmIVtDRnk0Ecf+/kzp9SUVrrBMLg==", + "dev": true, + "requires": { + "fast-glob": "^3.2.7", + "glob-parent": "^6.0.1", + "globby": "^12.0.2", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "core-js": { + "version": "3.20.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", + "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", + "dev": true + }, + "core-js-compat": { + "version": "3.22.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.22.5.tgz", + "integrity": "sha512-rEF75n3QtInrYICvJjrAgV03HwKiYvtKHdPtaba1KucG+cNZ4NJnH9isqt979e67KZlhpbCOTwnsvnIr+CVeOg==", + "dev": true, + "requires": { + "browserslist": "^4.20.3", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "critters": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", + "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "css-select": "^4.2.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "postcss": "^8.3.7", + "pretty-bytes": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-loader": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.5.1.tgz", + "integrity": "sha512-gEy2w9AnJNnD9Kuo4XAP9VflW/ujKoS9c/syO+uWMlm5igc7LysKzPXaDoR2vroROkSwsTS2tGr1yGGEbZOYZQ==", + "dev": true, + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.2.15", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.1.0", + "semver": "^7.3.5" + } + }, + "css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "dev": true, + "requires": {} + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true + }, + "cssdb": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-5.1.0.tgz", + "integrity": "sha512-/vqjXhv1x9eGkE/zO6o8ZOI7dgdZbLVLUGyVRbPgk6YipXbW87YzUCcO+Jrmi5bwJlAH6oD+MNeZyRgXea1GZw==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "dev": true + }, + "date-format": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.10.tgz", + "integrity": "sha512-RuMIHocrVjF84bUSTcd1uokIsLsOsk1Awb7TexNOI3f48ukCu39mjslWquDTA08VaDMF2umr3MB9ow5EyJTWyA==", + "dev": true + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + } + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "del": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.0.tgz", + "integrity": "sha512-OpcRktOt7G7HBfyxP0srBH4Djg4824EQORX8E1qvIhIzthNNArxxhrB/Mm7dRMiLi1nvFyUpDhzD2cTtbBhV8A==", + "dev": true, + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "dependencies": { + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "dev": true, + "requires": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.4.137", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz", + "integrity": "sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "engine.io": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz", + "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==", + "dev": true, + "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3" + } + }, + "engine.io-parser": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "dev": true + }, + "enhanced-resolve": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz", + "integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true + }, + "err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "esbuild": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.22.tgz", + "integrity": "sha512-CjFCFGgYtbFOPrwZNJf7wsuzesx8kqwAffOlbYcFDLFuUtP8xloK1GH+Ai13Qr0RZQf9tE7LMTHJ2iVGJ1SKZA==", + "dev": true, + "optional": true, + "requires": { + "esbuild-android-arm64": "0.14.22", + "esbuild-darwin-64": "0.14.22", + "esbuild-darwin-arm64": "0.14.22", + "esbuild-freebsd-64": "0.14.22", + "esbuild-freebsd-arm64": "0.14.22", + "esbuild-linux-32": "0.14.22", + "esbuild-linux-64": "0.14.22", + "esbuild-linux-arm": "0.14.22", + "esbuild-linux-arm64": "0.14.22", + "esbuild-linux-mips64le": "0.14.22", + "esbuild-linux-ppc64le": "0.14.22", + "esbuild-linux-riscv64": "0.14.22", + "esbuild-linux-s390x": "0.14.22", + "esbuild-netbsd-64": "0.14.22", + "esbuild-openbsd-64": "0.14.22", + "esbuild-sunos-64": "0.14.22", + "esbuild-windows-32": "0.14.22", + "esbuild-windows-64": "0.14.22", + "esbuild-windows-arm64": "0.14.22" + } + }, + "esbuild-android-arm64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.22.tgz", + "integrity": "sha512-k1Uu4uC4UOFgrnTj2zuj75EswFSEBK+H6lT70/DdS4mTAOfs2ECv2I9ZYvr3w0WL0T4YItzJdK7fPNxcPw6YmQ==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.22.tgz", + "integrity": "sha512-d8Ceuo6Vw6HM3fW218FB6jTY6O3r2WNcTAU0SGsBkXZ3k8SDoRLd3Nrc//EqzdgYnzDNMNtrWegK2Qsss4THhw==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.22.tgz", + "integrity": "sha512-YAt9Tj3SkIUkswuzHxkaNlT9+sg0xvzDvE75LlBo4DI++ogSgSmKNR6B4eUhU5EUUepVXcXdRIdqMq9ppeRqfw==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.22.tgz", + "integrity": "sha512-ek1HUv7fkXMy87Qm2G4IRohN+Qux4IcnrDBPZGXNN33KAL0pEJJzdTv0hB/42+DCYWylSrSKxk3KUXfqXOoH4A==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.22.tgz", + "integrity": "sha512-zPh9SzjRvr9FwsouNYTqgqFlsMIW07O8mNXulGeQx6O5ApgGUBZBgtzSlBQXkHi18WjrosYfsvp5nzOKiWzkjQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.22.tgz", + "integrity": "sha512-SnpveoE4nzjb9t2hqCIzzTWBM0RzcCINDMBB67H6OXIuDa4KqFqaIgmTchNA9pJKOVLVIKd5FYxNiJStli21qg==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.22.tgz", + "integrity": "sha512-Zcl9Wg7gKhOWWNqAjygyqzB+fJa19glgl2JG7GtuxHyL1uEnWlpSMytTLMqtfbmRykIHdab797IOZeKwk5g0zg==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.22.tgz", + "integrity": "sha512-soPDdbpt/C0XvOOK45p4EFt8HbH5g+0uHs5nUKjHVExfgR7du734kEkXR/mE5zmjrlymk5AA79I0VIvj90WZ4g==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.22.tgz", + "integrity": "sha512-8q/FRBJtV5IHnQChO3LHh/Jf7KLrxJ/RCTGdBvlVZhBde+dk3/qS9fFsUy+rs3dEi49aAsyVitTwlKw1SUFm+A==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.22.tgz", + "integrity": "sha512-SiNDfuRXhGh1JQLLA9JPprBgPVFOsGuQ0yDfSPTNxztmVJd8W2mX++c4FfLpAwxuJe183mLuKf7qKCHQs5ZnBQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.22.tgz", + "integrity": "sha512-6t/GI9I+3o1EFm2AyN9+TsjdgWCpg2nwniEhjm2qJWtJyJ5VzTXGUU3alCO3evopu8G0hN2Bu1Jhz2YmZD0kng==", + "dev": true, + "optional": true + }, + "esbuild-linux-riscv64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.22.tgz", + "integrity": "sha512-AyJHipZKe88sc+tp5layovquw5cvz45QXw5SaDgAq2M911wLHiCvDtf/07oDx8eweCyzYzG5Y39Ih568amMTCQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-s390x": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.22.tgz", + "integrity": "sha512-Sz1NjZewTIXSblQDZWEFZYjOK6p8tV6hrshYdXZ0NHTjWE+lwxpOpWeElUGtEmiPcMT71FiuA9ODplqzzSxkzw==", + "dev": true, + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.22.tgz", + "integrity": "sha512-TBbCtx+k32xydImsHxvFgsOCuFqCTGIxhzRNbgSL1Z2CKhzxwT92kQMhxort9N/fZM2CkRCPPs5wzQSamtzEHA==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.22.tgz", + "integrity": "sha512-vK912As725haT313ANZZZN+0EysEEQXWC/+YE4rQvOQzLuxAQc2tjbzlAFREx3C8+uMuZj/q7E5gyVB7TzpcTA==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.22.tgz", + "integrity": "sha512-/mbJdXTW7MTcsPhtfDsDyPEOju9EOABvCjeUU2OJ7fWpX/Em/H3WYDa86tzLUbcVg++BScQDzqV/7RYw5XNY0g==", + "dev": true, + "optional": true + }, + "esbuild-wasm": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.14.22.tgz", + "integrity": "sha512-FOSAM29GN1fWusw0oLMv6JYhoheDIh5+atC72TkJKfIUMID6yISlicoQSd9gsNSFsNBvABvtE2jR4JB1j4FkFw==", + "dev": true + }, + "esbuild-windows-32": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.22.tgz", + "integrity": "sha512-1vRIkuvPTjeSVK3diVrnMLSbkuE36jxA+8zGLUOrT4bb7E/JZvDRhvtbWXWaveUc/7LbhaNFhHNvfPuSw2QOQg==", + "dev": true, + "optional": true + }, + "esbuild-windows-64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.22.tgz", + "integrity": "sha512-AxjIDcOmx17vr31C5hp20HIwz1MymtMjKqX4qL6whPj0dT9lwxPexmLj6G1CpR3vFhui6m75EnBEe4QL82SYqw==", + "dev": true, + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.14.22", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.22.tgz", + "integrity": "sha512-5wvQ+39tHmRhNpu2Fx04l7QfeK3mQ9tKzDqqGR8n/4WUxsFxnVLfDRBGirIfk4AfWlxk60kqirlODPoT5LqMUg==", + "dev": true, + "optional": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter-asyncresource": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", + "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "dev": true, + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + } + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, + "follow-redirects": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz", + "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==", + "dev": true + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, + "gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "dev": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + } + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globby": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz", + "integrity": "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==", + "dev": true, + "requires": { + "array-union": "^3.0.1", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.7", + "ignore": "^5.1.9", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "hdr-histogram-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", + "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", + "dev": true, + "requires": { + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" + } + }, + "hdr-histogram-percentiles-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", + "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", + "dev": true + }, + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "dependencies": { + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } + } + }, + "http-parser-js": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", + "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dev": true, + "requires": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "requires": {} + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "ignore-walk": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-4.0.1.tgz", + "integrity": "sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true + }, + "immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true + }, + "inquirer": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.0.tgz", + "integrity": "sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.2.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "ip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", + "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", + "dev": true + }, + "ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "dev": true + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true + }, + "is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", + "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jasmine-core": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.0.1.tgz", + "integrity": "sha512-w+JDABxQCkxbGGxg+a2hUVZyqUS2JKngvIyLGu/xiw2ZwgsoSB0iiecLQsQORSeaKQ6iGrCyWG86RfNDuoA7Lg==", + "dev": true + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true + }, + "jsonc-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", + "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, + "karma": { + "version": "6.3.20", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.20.tgz", + "integrity": "sha512-HRNQhMuKOwKpjYlWiJP0DUrJOh+QjaI/DTaD8b9rEm4Il3tJ8MijutVZH4ts10LuUFst/CedwTS6vieCN8yTSw==", + "dev": true, + "requires": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.4.1", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + } + } + }, + "karma-chrome-launcher": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz", + "integrity": "sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ==", + "dev": true, + "requires": { + "which": "^1.2.1" + } + }, + "karma-coverage": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.1.1.tgz", + "integrity": "sha512-oxeOSBVK/jdZsiX03LhHQkO4eISSQb5GbHi6Nsw3Mw7G4u6yUgacBAftnO7q+emPBLMsrNbz1pGIrj+Jb3z17A==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.0.5", + "minimatch": "^3.0.4" + }, + "dependencies": { + "istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "karma-jasmine": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.2.tgz", + "integrity": "sha512-ggi84RMNQffSDmWSyyt4zxzh2CQGwsxvYYsprgyR1j8ikzIduEdOlcLvXjZGwXG/0j41KUXOWsUCBfbEHPWP9g==", + "dev": true, + "requires": { + "jasmine-core": "^3.6.0" + }, + "dependencies": { + "jasmine-core": { + "version": "3.99.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.99.1.tgz", + "integrity": "sha512-Hu1dmuoGcZ7AfyynN3LsfruwMbxMALMka+YtZeGoLuDEySVmVAPaonkNoBRIw/ectu8b9tVQCJNgp4a4knp+tg==", + "dev": true + } + } + }, + "karma-jasmine-html-reporter": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz", + "integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==", + "dev": true, + "requires": {} + }, + "karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "requires": { + "source-map-support": "^0.5.5" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true + }, + "less": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.2.tgz", + "integrity": "sha512-EoQp/Et7OSOVu0aJknJOtlXZsnr8XE8KwuzTHOLeVSEx8pVWUICc8Q0VYRHgzyjX78nMEyC/oztWFbgyhtNfDA==", + "dev": true, + "requires": { + "copy-anything": "^2.0.1", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^2.5.2", + "parse-node-version": "^1.0.1", + "source-map": "~0.6.0", + "tslib": "^2.3.0" + }, + "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "optional": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "less-loader": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-10.2.0.tgz", + "integrity": "sha512-AV5KHWvCezW27GT90WATaDnfXBv99llDbtaj4bshq6DvAihMdNjaPDcUMa6EXKLRF+P2opFenJp89BXg91XLYg==", + "dev": true, + "requires": { + "klona": "^2.0.4" + } + }, + "license-webpack-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "dev": true, + "requires": { + "webpack-sources": "^3.0.0" + } + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true + }, + "loader-utils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", + "integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "log4js": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.4.7.tgz", + "integrity": "sha512-q/9Eyw/hkvQ4e9DNHLbK2AfuDDm5QnNnmF022aamyw4nUnVLQRhvGoryccN5aEI4J/UcA4W36xttBCrlrdzt2g==", + "dev": true, + "requires": { + "date-format": "^4.0.10", + "debug": "^4.3.4", + "flatted": "^3.2.5", + "rfdc": "^1.3.0", + "streamroller": "^3.0.9" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + } + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "dev": true, + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "memfs": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.3.tgz", + "integrity": "sha512-eivjfi7Ahr6eQTn44nvTnR60e4a1Fs1Via2kCR5lHo/kyNoiMWaXCNJ/GpSd0ilXas2JSOl9B5FTIhflXu0hlg==", + "dev": true, + "requires": { + "fs-monkey": "1.0.3" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.5.3.tgz", + "integrity": "sha512-YseMB8cs8U/KCaAGQoqYmfUuhhGW0a9p9XvWXrxVOkE3/IiISTLw4ALNt7JR5B2eYauFM+PQGSbXMDmVbR7Tfw==", + "dev": true, + "requires": { + "schema-utils": "^4.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-json-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", + "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", + "dev": true, + "requires": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true + }, + "needle": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.9.1.tgz", + "integrity": "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==", + "dev": true, + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "dev": true, + "optional": true, + "requires": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "optional": true + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true + }, + "node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "dev": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "node-gyp-build": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", + "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==", + "dev": true, + "optional": true + }, + "node-releases": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz", + "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==", + "dev": true + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-install-checks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", + "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", + "dev": true, + "requires": { + "semver": "^7.1.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "npm-package-arg": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz", + "integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-3.0.0.tgz", + "integrity": "sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ==", + "dev": true, + "requires": { + "glob": "^7.1.6", + "ignore-walk": "^4.0.1", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz", + "integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==", + "dev": true, + "requires": { + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" + } + }, + "npm-registry-fetch": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-12.0.2.tgz", + "integrity": "sha512-Df5QT3RaJnXYuOwtXBXS9BWs+tHH2olvkCLh6jcR/b/u3DvPMlp3J0TvvYwplPKxHMOwfg287PYih9QqaVFoKA==", + "dev": true, + "requires": { + "make-fetch-happen": "^10.0.1", + "minipass": "^3.1.6", + "minipass-fetch": "^1.4.1", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^8.1.5" + }, + "dependencies": { + "@npmcli/fs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.0.tgz", + "integrity": "sha512-DmfBvNXGaetMxj9LTp8NAN9vEidXURrf5ZTslQzEAi/6GbW+4yjaLFQc6Tue5cpZ9Frlk4OBo/Snf1Bh/S7qTQ==", + "dev": true, + "requires": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + } + }, + "@npmcli/move-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.0.tgz", + "integrity": "sha512-UR6D5f4KEGWJV6BGPH3Qb2EtgH+t+1XQ1Tt85c7qicN6cezzuHPdZwwAxqZr4JLtnQu0LZsTza/5gmNmSl8XLg==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "cacache": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.0.tgz", + "integrity": "sha512-Pk4aQkwCW82A4jGKFvcGkQFqZcMspfP9YWq9Pr87/ldDvlWf718zeI6KWCdKt/jeihu6BytHRUicJPB1K2k8EQ==", + "dev": true, + "requires": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^1.1.1" + } + }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "lru-cache": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.10.1.tgz", + "integrity": "sha512-BQuhQxPuRl79J5zSXRP+uNzPOyZw2oFI9JLRQ80XswSvg21KMKNtQza9eF42rfI/3Z40RvzBdXgziEkudzjo8A==", + "dev": true + }, + "make-fetch-happen": { + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.1.4.tgz", + "integrity": "sha512-hU1w68PqfH7FdMgjbiziJoACY0edlbIZ0CyKnpcEruVdCjsUrN+qoenOCIayNqVBK7toSWwbDxvQlrhH0gjRdg==", + "dev": true, + "requires": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.1.1", + "ssri": "^9.0.0" + }, + "dependencies": { + "minipass-fetch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.0.tgz", + "integrity": "sha512-H9U4UVBGXEyyWJnqYDCLp1PwD8XIkJ4akNHp1aGVI+2Ym7wQMlxDKi4IB4JbmyU+pl9pEs/cVrK6cOuvmbK4Sg==", + "dev": true, + "requires": { + "encoding": "^0.1.13", + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + } + } + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "ssri": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.0.tgz", + "integrity": "sha512-Y1Z6J8UYnexKFN1R/hxUaYoY2LVdKEzziPmVAFKiKX8fiwvCJTVzn/xYE9TEWod5OVyNfIHHuVfIEuBClL/uJQ==", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + } + } + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "dev": true, + "requires": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + } + }, + "nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "requires": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "dependencies": { + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true + } + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pacote": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-12.0.3.tgz", + "integrity": "sha512-CdYEl03JDrRO3x18uHjBYA9TyoW8gy+ThVcypcDkxPtKlw76e4ejhYB6i9lJ+/cebbjpqPW/CijjqxwDTts8Ow==", + "dev": true, + "requires": { + "@npmcli/git": "^2.1.0", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^2.0.0", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^3.0.0", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^12.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + } + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "parse5-html-rewriting-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", + "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", + "dev": true, + "requires": { + "parse5": "^6.0.1", + "parse5-sax-parser": "^6.0.1" + } + }, + "parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "requires": { + "parse5": "^6.0.1" + } + }, + "parse5-sax-parser": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", + "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", + "dev": true, + "requires": { + "parse5": "^6.0.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "piscina": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", + "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", + "dev": true, + "requires": { + "eventemitter-asyncresource": "^1.0.0", + "hdr-histogram-js": "^2.0.1", + "hdr-histogram-percentiles-obj": "^3.0.0", + "nice-napi": "^1.0.2" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + } + } + }, + "postcss": { + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz", + "integrity": "sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==", + "dev": true, + "requires": { + "nanoid": "^3.1.30", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.1" + } + }, + "postcss-attribute-case-insensitive": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.0.tgz", + "integrity": "sha512-b4g9eagFGq9T5SWX4+USfVyjIb3liPnjhHHRMP7FMB2kFVpYyfEscV0wP3eaXhKlcHKUut8lt5BGoeylWA/dBQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.2" + } + }, + "postcss-color-functional-notation": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.2.tgz", + "integrity": "sha512-DXVtwUhIk4f49KK5EGuEdgx4Gnyj6+t2jBSEmxvpIK9QI40tWrpS2Pua8Q7iIZWBrki2QOaeUdEaLPPa91K0RQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-hex-alpha": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.3.tgz", + "integrity": "sha512-fESawWJCrBV035DcbKRPAVmy21LpoyiXdPTuHUfWJ14ZRjY7Y7PA6P4g8z6LQGYhU1WAxkTxjIjurXzoe68Glw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-rebeccapurple": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.0.2.tgz", + "integrity": "sha512-SFc3MaocHaQ6k3oZaFwH8io6MdypkUtEy/eXzXEB1vEQlO3S3oDc/FSZA8AsS04Z25RirQhlDlHLh3dn7XewWw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-media": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz", + "integrity": "sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==", + "dev": true, + "requires": {} + }, + "postcss-custom-properties": { + "version": "12.1.7", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.7.tgz", + "integrity": "sha512-N/hYP5gSoFhaqxi2DPCmvto/ZcRDVjE3T1LiAMzc/bg53hvhcHOLpXOHb526LzBBp5ZlAUhkuot/bfpmpgStJg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-selectors": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.0.tgz", + "integrity": "sha512-/1iyBhz/W8jUepjGyu7V1OPcGbc636snN1yXEQCinb6Bwt7KxsiU7/bLQlp8GwAXzCh7cobBU5odNn/2zQWR8Q==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-dir-pseudo-class": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.4.tgz", + "integrity": "sha512-I8epwGy5ftdzNWEYok9VjW9whC4xnelAtbajGv4adql4FIF09rnrxnA9Y8xSHN47y7gqFIv10C5+ImsLeJpKBw==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-double-position-gradients": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.1.tgz", + "integrity": "sha512-jM+CGkTs4FcG53sMPjrrGE0rIvLDdCrqMzgDC5fLI7JHDO7o6QG8C5TQBtExb13hdBdoH9C2QVbG4jo2y9lErQ==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "dev": true, + "requires": {} + }, + "postcss-gap-properties": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.3.tgz", + "integrity": "sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ==", + "dev": true, + "requires": {} + }, + "postcss-image-set-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.6.tgz", + "integrity": "sha512-KfdC6vg53GC+vPd2+HYzsZ6obmPqOk6HY09kttU19+Gj1nC3S3XBVEXDHxkhxTohgZqzbUb94bKXvKDnYWBm/A==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-import": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.2.tgz", + "integrity": "sha512-BJ2pVK4KhUyMcqjuKs9RijV5tatNzNa73e/32aBVE/ejYPe37iH+6vAu9WvqUkB5OAYgLHzbSvzHnorybJCm9g==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "dev": true, + "requires": {} + }, + "postcss-lab-function": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.0.tgz", + "integrity": "sha512-Zb1EO9DGYfa3CP8LhINHCcTTCTLI+R3t7AX2mKsDzdgVQ/GkCpHOTgOr6HBHslP7XDdVbqgHW5vvRPMdVANQ8w==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "dev": true, + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + } + }, + "postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "dev": true, + "requires": {} + }, + "postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "dev": true, + "requires": {} + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "requires": {} + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-nesting": { + "version": "10.1.6", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.6.tgz", + "integrity": "sha512-8C0X0pOOShlgqYkCzB4wlWLyulos+GXFpw7r2+x7g+ROQ1RwN8MlN2NCcpNQScNBPfbjxbjwY8e25raTcEXqmg==", + "dev": true, + "requires": { + "@csstools/selector-specificity": "1.0.0", + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-overflow-shorthand": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.3.tgz", + "integrity": "sha512-CxZwoWup9KXzQeeIxtgOciQ00tDtnylYIlJBBODqkgS/PU2jISuWOL/mYLHmZb9ZhZiCaNKsCRiLp22dZUtNsg==", + "dev": true, + "requires": {} + }, + "postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "dev": true, + "requires": {} + }, + "postcss-place": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.4.tgz", + "integrity": "sha512-MrgKeiiu5OC/TETQO45kV3npRjOFxEHthsqGtkh3I1rPbZSbXGD/lZVi9j13cYh+NA8PIAPyk6sGjT9QbRyvSg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-preset-env": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.2.3.tgz", + "integrity": "sha512-Ok0DhLfwrcNGrBn8sNdy1uZqWRk/9FId0GiQ39W4ILop5GHtjJs8bu1MY9isPwHInpVEPWjb4CEcEaSbBLpfwA==", + "dev": true, + "requires": { + "autoprefixer": "^10.4.2", + "browserslist": "^4.19.1", + "caniuse-lite": "^1.0.30001299", + "css-blank-pseudo": "^3.0.2", + "css-has-pseudo": "^3.0.3", + "css-prefers-color-scheme": "^6.0.2", + "cssdb": "^5.0.0", + "postcss-attribute-case-insensitive": "^5.0.0", + "postcss-color-functional-notation": "^4.2.1", + "postcss-color-hex-alpha": "^8.0.2", + "postcss-color-rebeccapurple": "^7.0.2", + "postcss-custom-media": "^8.0.0", + "postcss-custom-properties": "^12.1.2", + "postcss-custom-selectors": "^6.0.0", + "postcss-dir-pseudo-class": "^6.0.3", + "postcss-double-position-gradients": "^3.0.4", + "postcss-env-function": "^4.0.4", + "postcss-focus-visible": "^6.0.3", + "postcss-focus-within": "^5.0.3", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.2", + "postcss-image-set-function": "^4.0.4", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.0.3", + "postcss-logical": "^5.0.3", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.1.2", + "postcss-overflow-shorthand": "^3.0.2", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.3", + "postcss-pseudo-class-any-link": "^7.0.2", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^5.0.0" + } + }, + "postcss-pseudo-class-any-link": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.4.tgz", + "integrity": "sha512-JxRcLXm96u14N3RzFavPIE9cRPuOqLDuzKeBsqi4oRk4vt8n0A7I0plFs/VXTg7U2n7g/XkQi0OwqTO3VWBfEg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "dev": true, + "requires": {} + }, + "postcss-selector-not": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-5.0.0.tgz", + "integrity": "sha512-/2K3A4TCP9orP4TNS7u3tGdRFVKqz/E6pX3aGnriPG0jU78of8wsUcqE4QAhWEU0d+WnMSF93Ah3F//vUtK+iQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + } + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true, + "optional": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true + }, + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "requires": { + "pify": "^2.3.0" + } + }, + "read-package-json-fast": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "dev": true, + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "regenerator-transform": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", + "dev": true + }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, + "regexpu-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", + "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", + "dev": true, + "requires": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + } + }, + "regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", + "dev": true + }, + "regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "requires": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "resolve-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "dev": true, + "requires": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rxjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "requires": { + "tslib": "^2.1.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sass": { + "version": "1.49.9", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.9.tgz", + "integrity": "sha512-YlYWkkHP9fbwaFRZQRXgDi3mXZShslVmmo+FVK3kHLUELHHEYrCmL1x6IUjC7wLS6VuJSAFXRQS/DxdsC4xL1A==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "sass-loader": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.4.0.tgz", + "integrity": "sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==", + "dev": true, + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + } + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selfsigned": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz", + "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==", + "dev": true, + "requires": { + "node-forge": "^1" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true + }, + "socket.io": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.1.tgz", + "integrity": "sha512-0y9pnIso5a9i+lJmsCdtmTTgJFFSvNQKDnPQRz28mGNnxbmqYg2QPtJTLFxhymFZhAIn50eHAKzJeiNaKr+yUQ==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.2.0", + "socket.io-adapter": "~2.4.0", + "socket.io-parser": "~4.0.4" + } + }, + "socket.io-adapter": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", + "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", + "dev": true + }, + "socket.io-parser": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz", + "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==", + "dev": true, + "requires": { + "@types/component-emitter": "^1.2.10", + "component-emitter": "~1.3.0", + "debug": "~4.3.1" + } + }, + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "socks": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", + "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", + "dev": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.2.0" + } + }, + "socks-proxy-agent": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.0.tgz", + "integrity": "sha512-wWqJhjb32Q6GsrUqzuFkukxb/zzide5quXYcMVpIjxalDBBYy2nqKCFQ/9+Ie4dvOYSQdOk3hUlZSdzZOd3zMQ==", + "dev": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, + "source-map-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.1.tgz", + "integrity": "sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "streamroller": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.9.tgz", + "integrity": "sha512-Y46Aq/ftqFP6Wb6sK79hgnZeRfEVz2F0nquBy4lMftUuJoTiwKa6Y96AWAUGV1F3CjhFark9sQmzL9eDpltkRw==", + "dev": true, + "requires": { + "date-format": "^4.0.10", + "debug": "^4.3.4", + "fs-extra": "^10.1.0" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + } + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "stylus": { + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.56.0.tgz", + "integrity": "sha512-Ev3fOb4bUElwWu4F9P9WjnnaSpc8XB9OFHSFZSKMFL1CE1oM+oFXWEgAqPmmZIyhBihuqIQlFsVTypiiS9RxeA==", + "dev": true, + "requires": { + "css": "^3.0.0", + "debug": "^4.3.2", + "glob": "^7.1.6", + "safer-buffer": "^2.1.2", + "sax": "~1.2.4", + "source-map": "^0.7.3" + } + }, + "stylus-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-6.2.0.tgz", + "integrity": "sha512-5dsDc7qVQGRoc6pvCL20eYgRUxepZ9FpeK28XhdXaIPP6kXr6nI1zAAKFQgP5OBkOfKaURp4WUpJzspg1f01Gg==", + "dev": true, + "requires": { + "fast-glob": "^3.2.7", + "klona": "^2.0.4", + "normalize-path": "^3.0.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true + }, + "tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "terser": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.11.0.tgz", + "integrity": "sha512-uCA9DLanzzWSsN1UirKwylhhRz3aKPInlfmpGfw8VN6jHsAtu8HJtIpeeHHK23rxnE/cDc+yvmq5wqkIC6Kn0A==", + "dev": true, + "requires": { + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.20" + } + }, + "terser-webpack-plugin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", + "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", + "dev": true, + "requires": { + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typed-assert": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", + "dev": true + }, + "typescript": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "dev": true + }, + "ua-parser-js": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", + "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", + "dev": true + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "dev": true + }, + "watchpack": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "webpack": { + "version": "5.70.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.70.0.tgz", + "integrity": "sha512-ZMWWy8CeuTTjCxbeaQI21xSswseF2oNOwc70QSKNePvmxE7XW36i7vpBMYZFAUHPwQiEbNGCEYIOOlyRbdGmxw==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.9.2", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "webpack-dev-middleware": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.0.tgz", + "integrity": "sha512-MouJz+rXAm9B1OTOYaJnn6rtD/lWZPy2ufQCH3BPs8Rloh/Du6Jze4p7AeLYHkVi0giJnYLaSGDC7S+GM9arhg==", + "dev": true, + "requires": { + "colorette": "^2.0.10", + "memfs": "^3.2.2", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "webpack-dev-server": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.7.3.tgz", + "integrity": "sha512-mlxq2AsIw2ag016nixkzUkdyOE8ST2GTy34uKSABp1c4nhjZvH90D5ZRR+UOLSsG4Z3TFahAi72a3ymRtfRm+Q==", + "dev": true, + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/serve-index": "^1.9.1", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.2.2", + "ansi-html-community": "^0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^3.5.2", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "default-gateway": "^6.0.3", + "del": "^6.0.0", + "express": "^4.17.1", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.0", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "portfinder": "^1.0.28", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "spdy": "^4.0.2", + "strip-ansi": "^7.0.0", + "webpack-dev-middleware": "^5.3.0", + "ws": "^8.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true + }, + "webpack-subresource-integrity": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", + "dev": true, + "requires": { + "typed-assert": "^1.0.8" + } + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "dev": true, + "requires": {} + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, + "yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "dev": true + }, + "zone.js": { + "version": "0.11.5", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.5.tgz", + "integrity": "sha512-D1/7VxEuQ7xk6z/kAROe4SUbd9CzxY4zOwVGnGHerd/SgLIVU5f4esDzQUsOCeArn933BZfWMKydH7l7dPEp0g==", + "requires": { + "tslib": "^2.3.0" + } + } + } +} diff --git a/TailwindUIKit/Website/package.json b/TailwindUIKit/Website/package.json new file mode 100644 index 0000000..4356616 --- /dev/null +++ b/TailwindUIKit/Website/package.json @@ -0,0 +1,39 @@ +{ + "name": "website", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test" + }, + "private": true, + "dependencies": { + "@angular/animations": "~13.3.0", + "@angular/common": "~13.3.0", + "@angular/compiler": "~13.3.0", + "@angular/core": "~13.3.0", + "@angular/forms": "~13.3.0", + "@angular/platform-browser": "~13.3.0", + "@angular/platform-browser-dynamic": "~13.3.0", + "@angular/router": "~13.3.0", + "rxjs": "~7.5.0", + "tslib": "^2.3.0", + "zone.js": "~0.11.4" + }, + "devDependencies": { + "@angular-devkit/build-angular": "~13.3.4", + "@angular/cli": "~13.3.4", + "@angular/compiler-cli": "~13.3.0", + "@types/jasmine": "~3.10.0", + "@types/node": "^12.11.1", + "jasmine-core": "~4.0.0", + "karma": "~6.3.0", + "karma-chrome-launcher": "~3.1.0", + "karma-coverage": "~2.1.0", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "~1.7.0", + "typescript": "~4.6.2" + } +} diff --git a/TailwindUIKit/Website/src/app/app.component.css b/TailwindUIKit/Website/src/app/app.component.css new file mode 100644 index 0000000..e69de29 diff --git a/TailwindUIKit/Website/src/app/app.component.html b/TailwindUIKit/Website/src/app/app.component.html new file mode 100644 index 0000000..cac4ecb --- /dev/null +++ b/TailwindUIKit/Website/src/app/app.component.html @@ -0,0 +1,4 @@ + + + + diff --git a/TailwindUIKit/Website/src/app/app.component.spec.ts b/TailwindUIKit/Website/src/app/app.component.spec.ts new file mode 100644 index 0000000..4a5c924 --- /dev/null +++ b/TailwindUIKit/Website/src/app/app.component.spec.ts @@ -0,0 +1,31 @@ +import { TestBed } from '@angular/core/testing'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + AppComponent + ], + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should have as title 'Website'`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app.title).toEqual('Website'); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('.content span')?.textContent).toContain('Website app is running!'); + }); +}); diff --git a/TailwindUIKit/Website/src/app/app.component.ts b/TailwindUIKit/Website/src/app/app.component.ts new file mode 100644 index 0000000..6b39a99 --- /dev/null +++ b/TailwindUIKit/Website/src/app/app.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] +}) +export class AppComponent { + title = 'Website'; +} diff --git a/TailwindUIKit/Website/src/app/app.module.ts b/TailwindUIKit/Website/src/app/app.module.ts new file mode 100644 index 0000000..b5d20ce --- /dev/null +++ b/TailwindUIKit/Website/src/app/app.module.ts @@ -0,0 +1,24 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; + +import { AppComponent } from './app.component'; +import { Hero1Component } from './hero1/hero1.component'; +import { Faq1Component } from './faq1/faq1.component'; +import { Team1Component } from './team1/team1.component'; +import { Testimonials1Component } from './testimonials1/testimonials1.component'; + +@NgModule({ + declarations: [ + AppComponent, + Hero1Component, + Faq1Component, + Team1Component, + Testimonials1Component + ], + imports: [ + BrowserModule + ], + providers: [], + bootstrap: [AppComponent] +}) +export class AppModule { } diff --git a/TailwindUIKit/Website/src/app/faq1/faq1.component.css b/TailwindUIKit/Website/src/app/faq1/faq1.component.css new file mode 100644 index 0000000..e69de29 diff --git a/TailwindUIKit/Website/src/app/faq1/faq1.component.html b/TailwindUIKit/Website/src/app/faq1/faq1.component.html new file mode 100644 index 0000000..0cee631 --- /dev/null +++ b/TailwindUIKit/Website/src/app/faq1/faq1.component.html @@ -0,0 +1,90 @@ +
+ blue pattern background +
+
+

Frequently asked questions

+
+
+
+
+
+

Why should I use your service?

+
+ +
+
    +
  • +

    If you want to choose Pro or Business plan the you can use all payments. You can pay from Paypal, Payoneer, Master Card, Debit Card.

    +
  • +
+
+
+
+
+

What payment method I can use?

+
+ +
+
    +
  • +

    If you want to choose Pro or Business plan the you can use all payments. You can pay from Paypal, Payoneer, Master Card, Debit Card.

    +
  • +
+
+
+
+
+

Is your service safe to use?

+
+ +
+
    +
  • +

    If you want to choose Pro or Business plan the you can use all payments. You can pay from Paypal, Payoneer, Master Card, Debit Card.

    +
  • +
+
+
+
+
+

How to recover password?

+
+ +
+
    +
  • +

    If you want to choose Pro or Business plan the you can use all payments. You can pay from Paypal, Payoneer, Master Card, Debit Card.

    +
  • +
+
+
+
+
diff --git a/TailwindUIKit/Website/src/app/faq1/faq1.component.spec.ts b/TailwindUIKit/Website/src/app/faq1/faq1.component.spec.ts new file mode 100644 index 0000000..37117e4 --- /dev/null +++ b/TailwindUIKit/Website/src/app/faq1/faq1.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Faq1Component } from './faq1.component'; + +describe('Faq1Component', () => { + let component: Faq1Component; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ Faq1Component ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(Faq1Component); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/TailwindUIKit/Website/src/app/faq1/faq1.component.ts b/TailwindUIKit/Website/src/app/faq1/faq1.component.ts new file mode 100644 index 0000000..c6cc51b --- /dev/null +++ b/TailwindUIKit/Website/src/app/faq1/faq1.component.ts @@ -0,0 +1,13 @@ +import { Component, OnInit } from "@angular/core"; + +@Component({ + selector: 'app-faq1', + templateUrl: './faq1.component.html', + styleUrls: ['./faq1.component.css'] +}) +export class Faq1Component implements OnInit { + isToggle; + constructor() {} + + ngOnInit(): void {} +} diff --git a/TailwindUIKit/Website/src/app/hero1/hero1.component.css b/TailwindUIKit/Website/src/app/hero1/hero1.component.css new file mode 100644 index 0000000..e69de29 diff --git a/TailwindUIKit/Website/src/app/hero1/hero1.component.html b/TailwindUIKit/Website/src/app/hero1/hero1.component.html new file mode 100644 index 0000000..6138d95 --- /dev/null +++ b/TailwindUIKit/Website/src/app/hero1/hero1.component.html @@ -0,0 +1,57 @@ + +
+
+
+

+ Reliable and Professional + Plastering + Services in Auckland +

+

subtext goes here

+
+
+ +
+
+
diff --git a/TailwindUIKit/Website/src/app/hero1/hero1.component.spec.ts b/TailwindUIKit/Website/src/app/hero1/hero1.component.spec.ts new file mode 100644 index 0000000..6d44df2 --- /dev/null +++ b/TailwindUIKit/Website/src/app/hero1/hero1.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { Hero1Component } from './hero1.component'; + +describe('Hero1Component', () => { + let component: Hero1Component; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ Hero1Component ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(Hero1Component); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/TailwindUIKit/Website/src/app/hero1/hero1.component.ts b/TailwindUIKit/Website/src/app/hero1/hero1.component.ts new file mode 100644 index 0000000..e45e922 --- /dev/null +++ b/TailwindUIKit/Website/src/app/hero1/hero1.component.ts @@ -0,0 +1,23 @@ + +import { Component, OnInit } from "@angular/core"; + +@Component({ + selector: 'app-hero1', + templateUrl: './hero1.component.html', + styleUrls: ['./hero1.component.css'] +}) + +export class Hero1Component implements OnInit { + show: boolean = false + + showMenu(){ + this.show =! this.show + + } + + +constructor() { + + } + ngOnInit(): void {} +} diff --git a/TailwindUIKit/Website/src/app/team1/team1.component.css b/TailwindUIKit/Website/src/app/team1/team1.component.css new file mode 100644 index 0000000..e69de29 diff --git a/TailwindUIKit/Website/src/app/team1/team1.component.html b/TailwindUIKit/Website/src/app/team1/team1.component.html new file mode 100644 index 0000000..529f1fb --- /dev/null +++ b/TailwindUIKit/Website/src/app/team1/team1.component.html @@ -0,0 +1,246 @@ +
+
+

BUILDING TEAM

+

The Talented People Behind the Scenes of the Organization

+
+
+
+
+
+
+
+
+
+ +
+
+
+
Andres Berlin
+

Chief Executive Officer

+

The CEO's role in raising a company's corporate IQ is to establish an atmosphere that promotes knowledge sharing and collaboration.

+ +
+
+
+
+
+
+
+ +
+
+
+
Silene Tokyo
+

Product Design Head

+

The emphasis on innovation and technology in our companies has resulted in a few of them establishing global benchmarks in product design and development.

+ +
+
+
+
+
+
+
+ +
+
+
+
Johnson Stone
+

Manager Development

+

Our services encompass the assessment and repair of property damage caused by water, fire, smoke, or mold. We can also be a part of the restoration.

+ +
+
+
+
+
+
+
+ +
+
+
+
Dean Jones
+

Principal Software Engineer

+

An avid open-source developer who loves to be creative and inventive. I have 20 years of experience in the field.

+ +
+
+
+
+
+
+
+ +
+
+
+
Rachel Adams
+

Product Design Head

+

Product designer with interests in immersive computing and XR, political ventures, and emerging technologies. Able to take ideas and give them a life.

+ +
+
+
+
+
+
+
+ +
+
+
+
Charles Keith
+

UX Designer

+

A UX designer is the voice of the customer. Our job is to look beyond the business goals. We don't just experience user interface but also questions it.

+ +
+
+
+
+
+
diff --git a/TailwindUIKit/Website/src/app/team1/team1.component.spec.ts b/TailwindUIKit/Website/src/app/team1/team1.component.spec.ts new file mode 100644 index 0000000..9c810e2 --- /dev/null +++ b/TailwindUIKit/Website/src/app/team1/team1.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Team1Component } from './team1.component'; + +describe('Team1Component', () => { + let component: Team1Component; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ Team1Component ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(Team1Component); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/TailwindUIKit/Website/src/app/team1/team1.component.ts b/TailwindUIKit/Website/src/app/team1/team1.component.ts new file mode 100644 index 0000000..2ccd141 --- /dev/null +++ b/TailwindUIKit/Website/src/app/team1/team1.component.ts @@ -0,0 +1,12 @@ +import { Component, OnInit } from "@angular/core"; + +@Component({ + selector: 'app-team1', + templateUrl: './team1.component.html', + styleUrls: ['./team1.component.css'] +}) +export class Team1Component implements OnInit { + constructor() {} + + ngOnInit(): void {} +} diff --git a/TailwindUIKit/Website/src/app/testimonials1/testimonials1.component.css b/TailwindUIKit/Website/src/app/testimonials1/testimonials1.component.css new file mode 100644 index 0000000..e69de29 diff --git a/TailwindUIKit/Website/src/app/testimonials1/testimonials1.component.html b/TailwindUIKit/Website/src/app/testimonials1/testimonials1.component.html new file mode 100644 index 0000000..06797ba --- /dev/null +++ b/TailwindUIKit/Website/src/app/testimonials1/testimonials1.component.html @@ -0,0 +1,148 @@ +
+

Testimonials

+

What our client says

+
+
+
+
+ + + + + + + + + + + +

When our designs need an expert opinion or approval, I know I can rely on your agency Thank you for all your help-I will be recommending you to everyone

+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ profile pictre +

Tom Koch

+

Developer

+
+
+
+
+
+ + + + + + + + + + + +

When our designs need an expert opinion or approval, I know I can rely on your agency Thank you for all your help-I will be recommending you to everyone

+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ profile pictre +

Alan Max

+

Designer

+
+
+
+
+
+ + + + + + + + + + + +

When our designs need an expert opinion or approval, I know I can rely on your agency Thank you for all your help-I will be recommending you to everyone

+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+ profile pictre +

Kera Joo

+

Support

+
+
+ +
+
diff --git a/TailwindUIKit/Website/src/app/testimonials1/testimonials1.component.spec.ts b/TailwindUIKit/Website/src/app/testimonials1/testimonials1.component.spec.ts new file mode 100644 index 0000000..95bcd90 --- /dev/null +++ b/TailwindUIKit/Website/src/app/testimonials1/testimonials1.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Testimonials1Component } from './testimonials1.component'; + +describe('Testimonials1Component', () => { + let component: Testimonials1Component; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ Testimonials1Component ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(Testimonials1Component); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/TailwindUIKit/Website/src/app/testimonials1/testimonials1.component.ts b/TailwindUIKit/Website/src/app/testimonials1/testimonials1.component.ts new file mode 100644 index 0000000..7777ea7 --- /dev/null +++ b/TailwindUIKit/Website/src/app/testimonials1/testimonials1.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-testimonials1', + templateUrl: './testimonials1.component.html', + styleUrls: ['./testimonials1.component.css'] +}) +export class Testimonials1Component implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/TailwindUIKit/Website/src/assets/.gitkeep b/TailwindUIKit/Website/src/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/TailwindUIKit/Website/src/environments/environment.prod.ts b/TailwindUIKit/Website/src/environments/environment.prod.ts new file mode 100644 index 0000000..3612073 --- /dev/null +++ b/TailwindUIKit/Website/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true +}; diff --git a/TailwindUIKit/Website/src/environments/environment.ts b/TailwindUIKit/Website/src/environments/environment.ts new file mode 100644 index 0000000..f56ff47 --- /dev/null +++ b/TailwindUIKit/Website/src/environments/environment.ts @@ -0,0 +1,16 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build` replaces `environment.ts` with `environment.prod.ts`. +// The list of file replacements can be found in `angular.json`. + +export const environment = { + production: false +}; + +/* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ +// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/TailwindUIKit/Website/src/favicon.ico b/TailwindUIKit/Website/src/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..997406ad22c29aae95893fb3d666c30258a09537 GIT binary patch literal 948 zcmV;l155mgP)CBYU7IjCFmI-B}4sMJt3^s9NVg!P0 z6hDQy(L`XWMkB@zOLgN$4KYz;j0zZxq9KKdpZE#5@k0crP^5f9KO};h)ZDQ%ybhht z%t9#h|nu0K(bJ ztIkhEr!*UyrZWQ1k2+YkGqDi8Z<|mIN&$kzpKl{cNP=OQzXHz>vn+c)F)zO|Bou>E z2|-d_=qY#Y+yOu1a}XI?cU}%04)zz%anD(XZC{#~WreV!a$7k2Ug`?&CUEc0EtrkZ zL49MB)h!_K{H(*l_93D5tO0;BUnvYlo+;yss%n^&qjt6fZOa+}+FDO(~2>G z2dx@=JZ?DHP^;b7*Y1as5^uphBsh*s*z&MBd?e@I>-9kU>63PjP&^#5YTOb&x^6Cf z?674rmSHB5Fk!{Gv7rv!?qX#ei_L(XtwVqLX3L}$MI|kJ*w(rhx~tc&L&xP#?cQow zX_|gx$wMr3pRZIIr_;;O|8fAjd;1`nOeu5K(pCu7>^3E&D2OBBq?sYa(%S?GwG&_0-s%_v$L@R!5H_fc)lOb9ZoOO#p`Nn`KU z3LTTBtjwo`7(HA6 z7gmO$yTR!5L>Bsg!X8616{JUngg_@&85%>W=mChTR;x4`P=?PJ~oPuy5 zU-L`C@_!34D21{fD~Y8NVnR3t;aqZI3fIhmgmx}$oc-dKDC6Ap$Gy>a!`A*x2L1v0 WcZ@i?LyX}70000 + + + + Website + + + + + + + + + diff --git a/TailwindUIKit/Website/src/main.ts b/TailwindUIKit/Website/src/main.ts new file mode 100644 index 0000000..c7b673c --- /dev/null +++ b/TailwindUIKit/Website/src/main.ts @@ -0,0 +1,12 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.error(err)); diff --git a/TailwindUIKit/Website/src/polyfills.ts b/TailwindUIKit/Website/src/polyfills.ts new file mode 100644 index 0000000..429bb9e --- /dev/null +++ b/TailwindUIKit/Website/src/polyfills.ts @@ -0,0 +1,53 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes recent versions of Safari, Chrome (including + * Opera), Edge on the desktop, and iOS and Chrome on mobile. + * + * Learn more in https://angular.io/guide/browser-support + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + * because those flags need to be set before `zone.js` being loaded, and webpack + * will put import in the top of bundle, so user need to create a separate file + * in this directory (for example: zone-flags.ts), and put the following flags + * into that file, and then add the following code before importing zone.js. + * import './zone-flags'; + * + * The flags allowed in zone-flags.ts are listed here. + * + * The following flags will work for all browsers. + * + * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + * + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + * + * (window as any).__Zone_enable_cross_context_check = true; + * + */ + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js'; // Included with Angular CLI. + + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ diff --git a/TailwindUIKit/Website/src/styles.css b/TailwindUIKit/Website/src/styles.css new file mode 100644 index 0000000..90d4ee0 --- /dev/null +++ b/TailwindUIKit/Website/src/styles.css @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git a/TailwindUIKit/Website/src/test.ts b/TailwindUIKit/Website/src/test.ts new file mode 100644 index 0000000..00025da --- /dev/null +++ b/TailwindUIKit/Website/src/test.ts @@ -0,0 +1,26 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context(path: string, deep?: boolean, filter?: RegExp): { + (id: string): T; + keys(): string[]; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting(), +); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/TailwindUIKit/Website/tsconfig.app.json b/TailwindUIKit/Website/tsconfig.app.json new file mode 100644 index 0000000..82d91dc --- /dev/null +++ b/TailwindUIKit/Website/tsconfig.app.json @@ -0,0 +1,15 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": [ + "src/main.ts", + "src/polyfills.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/TailwindUIKit/Website/tsconfig.json b/TailwindUIKit/Website/tsconfig.json new file mode 100644 index 0000000..7e00c96 --- /dev/null +++ b/TailwindUIKit/Website/tsconfig.json @@ -0,0 +1,23 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": "./", + "outDir": "./dist/out-tsc", + "sourceMap": true, + "declaration": false, + "downlevelIteration": true, + "experimentalDecorators": true, + "moduleResolution": "node", + "importHelpers": true, + "target": "es2017", + "module": "es2020", + "lib": [ + "es2020", + "dom" + ] + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false + } +} diff --git a/TailwindUIKit/Website/tsconfig.spec.json b/TailwindUIKit/Website/tsconfig.spec.json new file mode 100644 index 0000000..092345b --- /dev/null +++ b/TailwindUIKit/Website/tsconfig.spec.json @@ -0,0 +1,18 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts", + "src/polyfills.ts" + ], + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/TailwindUIKit/package-lock.json b/TailwindUIKit/package-lock.json new file mode 100644 index 0000000..3249f59 --- /dev/null +++ b/TailwindUIKit/package-lock.json @@ -0,0 +1,1308 @@ +{ + "name": "TailwindUIKit", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "autoprefixer": "^10.4.7", + "postcss": "^8.4.14", + "tailwindcss": "^3.0.24" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dependencies": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz", + "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==" + }, + "node_modules/autoprefixer": { + "version": "10.4.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.7.tgz", + "integrity": "sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.20.3", + "caniuse-lite": "^1.0.30001335", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz", + "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001332", + "electron-to-chromium": "^1.4.118", + "escalade": "^3.1.1", + "node-releases": "^2.0.3", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001341", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001341.tgz", + "integrity": "sha512-2SodVrFFtvGENGCv0ChVJIDQ0KPaS1cg7/qtfMaICgeMolDdo/Z2OD32F0Aq9yl6F4YFwGPBS5AaPqNYiW4PoA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + }, + "node_modules/detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "dependencies": { + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.137", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz", + "integrity": "sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==" + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/lilconfig": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", + "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz", + "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-js": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", + "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", + "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", + "dependencies": { + "postcss-selector-parser": "^6.0.6" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.0.24", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.24.tgz", + "integrity": "sha512-H3uMmZNWzG6aqmg9q07ZIRNIawoiEcNFKDfL+YzOPuPsXuDXxJxB9icqzLgdzKNwjG3SAro2h9SYav8ewXNgig==", + "dependencies": { + "arg": "^5.0.1", + "chokidar": "^3.5.3", + "color-name": "^1.1.4", + "detective": "^5.2.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "lilconfig": "^2.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.12", + "postcss-js": "^4.0.0", + "postcss-load-config": "^3.1.4", + "postcss-nested": "5.0.6", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", + "quick-lru": "^5.1.1", + "resolve": "^1.22.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + } + }, + "dependencies": { + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + }, + "acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "requires": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.1.tgz", + "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==" + }, + "autoprefixer": { + "version": "10.4.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.7.tgz", + "integrity": "sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==", + "requires": { + "browserslist": "^4.20.3", + "caniuse-lite": "^1.0.30001335", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz", + "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==", + "requires": { + "caniuse-lite": "^1.0.30001332", + "electron-to-chromium": "^1.4.118", + "escalade": "^3.1.1", + "node-releases": "^2.0.3", + "picocolors": "^1.0.0" + } + }, + "camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" + }, + "caniuse-lite": { + "version": "1.0.30001341", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001341.tgz", + "integrity": "sha512-2SodVrFFtvGENGCv0ChVJIDQ0KPaS1cg7/qtfMaICgeMolDdo/Z2OD32F0Aq9yl6F4YFwGPBS5AaPqNYiW4PoA==" + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + }, + "detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "requires": { + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" + } + }, + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "electron-to-chromium": { + "version": "1.4.137", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz", + "integrity": "sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==" + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "requires": { + "reusify": "^1.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "requires": { + "is-glob": "^4.0.3" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "requires": { + "has": "^1.0.3" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "lilconfig": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", + "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==" + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + }, + "node-releases": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz", + "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==" + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" + }, + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-js": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", + "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", + "requires": { + "camelcase-css": "^2.0.1" + } + }, + "postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "requires": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + } + }, + "postcss-nested": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", + "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", + "requires": { + "postcss-selector-parser": "^6.0.6" + } + }, + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "requires": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + }, + "tailwindcss": { + "version": "3.0.24", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.0.24.tgz", + "integrity": "sha512-H3uMmZNWzG6aqmg9q07ZIRNIawoiEcNFKDfL+YzOPuPsXuDXxJxB9icqzLgdzKNwjG3SAro2h9SYav8ewXNgig==", + "requires": { + "arg": "^5.0.1", + "chokidar": "^3.5.3", + "color-name": "^1.1.4", + "detective": "^5.2.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "lilconfig": "^2.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.12", + "postcss-js": "^4.0.0", + "postcss-load-config": "^3.1.4", + "postcss-nested": "5.0.6", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", + "quick-lru": "^5.1.1", + "resolve": "^1.22.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + } + } +} diff --git a/TailwindUIKit/package.json b/TailwindUIKit/package.json new file mode 100644 index 0000000..b3b7a02 --- /dev/null +++ b/TailwindUIKit/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "autoprefixer": "^10.4.7", + "postcss": "^8.4.14", + "tailwindcss": "^3.0.24" + } +} diff --git a/TailwindUIKit/tailwind.config.js b/TailwindUIKit/tailwind.config.js new file mode 100644 index 0000000..9843c05 --- /dev/null +++ b/TailwindUIKit/tailwind.config.js @@ -0,0 +1,7 @@ +module.exports = { + content: [], + theme: { + extend: {}, + }, + plugins: [], +} From 76af163029c130dc25df11c0a006cc79776a844e Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Fri, 30 Sep 2022 09:14:18 +1000 Subject: [PATCH 49/56] [IntegrationConnectors] Added JIRA and GoogleCalendar connectors --- .../build/.nuke/build.schema.json | 121 ------------------ .../build/.nuke/parameters.json | 3 - .../GoogleCalendarConnector.cs | 106 +++++++++++++++ ...ntegrationConnectors.GoogleCalendar.csproj | 13 ++ .../Model/Country.cs | 10 ++ .../Model/Creator.cs | 7 + .../Model/EndDate.cs | 9 ++ .../Model/GoogleCalendar.cs | 17 +++ .../Model/GoogleCalendarEvent.cs | 16 +++ .../Model/StartDate.cs | 9 ++ .../IntegrationConnectors.JIRA.csproj | 13 ++ .../JiraConnector.cs | 95 ++++++++++++++ .../Model/Assignee.cs | 7 + .../Model/BacklogItem.cs | 27 ++++ .../IntegrationConnectors.JIRA/Model/Field.cs | 6 + .../Model/Fields.cs | 43 +++++++ .../Model/FixVersion.cs | 7 + .../IntegrationConnectors.JIRA/Model/Issue.cs | 10 ++ .../Model/IssueType.cs | 7 + .../Model/JqlQueryResult.cs | 12 ++ .../Model/Parent.cs | 8 ++ .../Model/Priority.cs | 7 + .../Model/RaidType.cs | 7 + .../Model/Sprint.cs | 10 ++ .../Model/SprintsResult.cs | 15 +++ .../Model/Status.cs | 7 + 26 files changed, 468 insertions(+), 124 deletions(-) delete mode 100644 IntegrationConnectors/IntegrationConnectors/build/.nuke/build.schema.json delete mode 100644 IntegrationConnectors/IntegrationConnectors/build/.nuke/parameters.json create mode 100644 IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/GoogleCalendarConnector.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/IntegrationConnectors.GoogleCalendar.csproj create mode 100644 IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/Country.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/Creator.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/EndDate.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/GoogleCalendar.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/GoogleCalendarEvent.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/StartDate.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.JIRA/IntegrationConnectors.JIRA.csproj create mode 100644 IntegrationConnectors/src/IntegrationConnectors.JIRA/JiraConnector.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Assignee.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/BacklogItem.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Field.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Fields.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/FixVersion.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Issue.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/IssueType.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/JqlQueryResult.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Parent.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Priority.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/RaidType.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Sprint.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/SprintsResult.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Status.cs diff --git a/IntegrationConnectors/IntegrationConnectors/build/.nuke/build.schema.json b/IntegrationConnectors/IntegrationConnectors/build/.nuke/build.schema.json deleted file mode 100644 index f32731a..0000000 --- a/IntegrationConnectors/IntegrationConnectors/build/.nuke/build.schema.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Build Schema", - "$ref": "#/definitions/build", - "definitions": { - "build": { - "type": "object", - "properties": { - "Configuration": { - "type": "string", - "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)", - "enum": [ - "Debug", - "Release" - ] - }, - "Continue": { - "type": "boolean", - "description": "Indicates to continue a previously failed build attempt" - }, - "Help": { - "type": "boolean", - "description": "Shows the help text for this build assembly" - }, - "Host": { - "type": "string", - "description": "Host for execution. Default is 'automatic'", - "enum": [ - "AppVeyor", - "AzurePipelines", - "Bamboo", - "Bitrise", - "GitHubActions", - "GitLab", - "Jenkins", - "Rider", - "SpaceAutomation", - "TeamCity", - "Terminal", - "TravisCI", - "VisualStudio", - "VSCode" - ] - }, - "NoLogo": { - "type": "boolean", - "description": "Disables displaying the NUKE logo" - }, - "NugetApiKey": { - "type": "string" - }, - "NugetUrl": { - "type": "string" - }, - "Partition": { - "type": "string", - "description": "Partition to use on CI" - }, - "Plan": { - "type": "boolean", - "description": "Shows the execution plan (HTML)" - }, - "Profile": { - "type": "array", - "description": "Defines the profiles to load", - "items": { - "type": "string" - } - }, - "Root": { - "type": "string", - "description": "Root directory during build execution" - }, - "Skip": { - "type": "array", - "description": "List of targets to be skipped. Empty list skips all dependencies", - "items": { - "type": "string", - "enum": [ - "Clean", - "Compile", - "Pack", - "Push", - "Restore", - "Versioning" - ] - } - }, - "Solution": { - "type": "string", - "description": "Path to a solution file that is automatically loaded" - }, - "Target": { - "type": "array", - "description": "List of targets to be invoked. Default is '{default_target}'", - "items": { - "type": "string", - "enum": [ - "Clean", - "Compile", - "Pack", - "Push", - "Restore", - "Versioning" - ] - } - }, - "Verbosity": { - "type": "string", - "description": "Logging verbosity during build execution. Default is 'Normal'", - "enum": [ - "Minimal", - "Normal", - "Quiet", - "Verbose" - ] - } - } - } - } -} \ No newline at end of file diff --git a/IntegrationConnectors/IntegrationConnectors/build/.nuke/parameters.json b/IntegrationConnectors/IntegrationConnectors/build/.nuke/parameters.json deleted file mode 100644 index 0c59dcb..0000000 --- a/IntegrationConnectors/IntegrationConnectors/build/.nuke/parameters.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "$schema": "./build.schema.json" -} diff --git a/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/GoogleCalendarConnector.cs b/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/GoogleCalendarConnector.cs new file mode 100644 index 0000000..3aaf596 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/GoogleCalendarConnector.cs @@ -0,0 +1,106 @@ +using System.Text.Json; +using IntegrationConnectors.Common; +using IntegrationConnectors.GoogleCalendar.Model; + +namespace IntegrationConnectors.GoogleCalendar +{ + public class GoogleCalendarConnector : HttpConnector + { + readonly string _apiKey; + public GoogleCalendarConnector(string apiKey) : base(string.Empty, string.Empty, AuthenticationType.None) + { + _apiKey = apiKey; + } + + public async Task> GetPublicHolidaysAsync(Country country) + { + var calendarJson = await GetAsync($"https://www.googleapis.com/calendar/v3/calendars/{GetCountryCode(country)}%23holiday%40group.v.calendar.google.com/events?key={_apiKey}"); + var calendar = JsonSerializer.Deserialize(calendarJson)?.items; + return calendar; + } + + public async Task> GetCalendarEventsAsync(string calendarId) + { + var calendarJson = await GetAsync($"https://www.googleapis.com/calendar/v3/calendars/{calendarId}%40group.calendar.google.com/events?key={_apiKey}"); + var calendar = JsonSerializer.Deserialize(calendarJson)?.items; + return calendar; + } + + private string GetCountryCode(Country country) + { + switch (country) + { + case Country.Australia: + return "en.australian"; + break; + case Country.Pakistan: + return "en.pk"; + break; + case Country.Russia: + return "en.russian"; + break; + case Country.Philippines: + return "en.philippines"; + break; + default: + return string.Empty; + break; + } + } + + + /* + Here is updated on January 2020 list ISO 3166-2 to google calendar name: + IMPORTANT! For all not listed countries ISO 3166-2 is used so i.e. for Albania it is al. + + {"au", "australian"}, + {"at", "austrian"}, + {"br", "brazilian"}, + {"bg", "bulgarian"}, + {"ca", "canadian"}, + {"cn", "china"}, + {"hr", "croatian"}, + {"cz", "czech"}, + {"dk", "danish"}, + {"fi", "finnish"}, + {"fr", "french"}, + {"de", "german"}, + {"gr", "greek"}, + {"hk", "hong_kong"}, + {"hu", "hungarian"}, + {"in", "indian"}, + {"id", "indonesian"}, + {"ie", "irish"}, + {"il", "jewish"}, + {"it", "italian"}, + {"jp", "japanese"}, + {"lv", "latvian"}, + {"lt", "lithuanian"}, + {"my", "malaysia"}, + {"mx", "mexican"}, + {"nl", "dutch"}, + {"nz", "new_zealand"}, + {"no", "norwegian"}, + {"ph", "philippines"}, + {"pl", "polish"}, + {"pt", "portuguese"}, + {"ro", "romanian"}, + {"ru", "russian"}, + {"sa", "saudiarabian"}, + {"sg", "singapore"}, + {"sk", "slovak"}, + {"si", "slovenian"}, + {"kr", "south_korea"}, + {"es", "spain"}, + {"se", "swedish"}, + {"tw", "taiwan"}, + {"tl", "thai"}, + {"tr", "turkish"}, + {"ua", "ukrainian"}, + {"us", "usa"}, + {"vn", "vietnamese"} + + + */ + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/IntegrationConnectors.GoogleCalendar.csproj b/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/IntegrationConnectors.GoogleCalendar.csproj new file mode 100644 index 0000000..c614b1e --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/IntegrationConnectors.GoogleCalendar.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/Country.cs b/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/Country.cs new file mode 100644 index 0000000..89ad373 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/Country.cs @@ -0,0 +1,10 @@ +namespace IntegrationConnectors.GoogleCalendar.Model +{ + public enum Country + { + Australia, + Pakistan, + Russia, + Philippines + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/Creator.cs b/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/Creator.cs new file mode 100644 index 0000000..5c08cd3 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/Creator.cs @@ -0,0 +1,7 @@ +namespace IntegrationConnectors.GoogleCalendar.Model +{ + public class Creator + { + public string displayName { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/EndDate.cs b/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/EndDate.cs new file mode 100644 index 0000000..8bc580e --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/EndDate.cs @@ -0,0 +1,9 @@ +namespace IntegrationConnectors.GoogleCalendar.Model +{ + public class EndDate + { + public DateTime date { get; set; } + public DateTime dateTime { get; set; } + public string timeZone { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/GoogleCalendar.cs b/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/GoogleCalendar.cs new file mode 100644 index 0000000..c6b5e3a --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/GoogleCalendar.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IntegrationConnectors.GoogleCalendar.Model +{ + public class GoogleCalendar + { + public string summary { get; set; } + public string timeZone { get; set; } + public DateTime updated { get; set; } + public List items { get; set; } + + } +} diff --git a/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/GoogleCalendarEvent.cs b/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/GoogleCalendarEvent.cs new file mode 100644 index 0000000..7f63507 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/GoogleCalendarEvent.cs @@ -0,0 +1,16 @@ + +namespace IntegrationConnectors.GoogleCalendar.Model +{ + public class GoogleCalendarEvent + { + public string status { get; set; } + public DateTime created { get; set; } + public DateTime updated { get; set; } + public string summary { get; set; } + public string description { get; set; } + public StartDate start { get; set; } + public EndDate end { get; set; } + public Creator creator { get; set; } + + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/StartDate.cs b/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/StartDate.cs new file mode 100644 index 0000000..fa2f172 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.GoogleCalendar/Model/StartDate.cs @@ -0,0 +1,9 @@ +namespace IntegrationConnectors.GoogleCalendar.Model +{ + public class StartDate + { + public DateTime date { get; set; } + public DateTime dateTime { get; set; } + public string timeZone { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.JIRA/IntegrationConnectors.JIRA.csproj b/IntegrationConnectors/src/IntegrationConnectors.JIRA/IntegrationConnectors.JIRA.csproj new file mode 100644 index 0000000..7196b4f --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.JIRA/IntegrationConnectors.JIRA.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/IntegrationConnectors/src/IntegrationConnectors.JIRA/JiraConnector.cs b/IntegrationConnectors/src/IntegrationConnectors.JIRA/JiraConnector.cs new file mode 100644 index 0000000..8bd54ad --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.JIRA/JiraConnector.cs @@ -0,0 +1,95 @@ +using IntegrationConnectors.Common; +using IntegrationConnectors.JIRA.Model; +using JiraReporting.Model; +using JiraReporting.Report; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace IntegrationConnectors.JIRA +{ + + public class JiraConnector : HttpConnector + { + public JiraConnector(string baseUrl, string apiKey, AuthenticationType authType) : base(baseUrl, apiKey, authType) + { + } + + public async Task> GetBacklogItemsAsync(string jql) + { + var latestBacklog = new List(); + + var jsonSerializerOptions = new JsonSerializerOptions() + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + PropertyNameCaseInsensitive = true, + //MaxDepth = 1 + }; + + var increment = 100; + var startAt = 0; + var finishAt = 1; + + while (startAt <= finishAt) + { + var query = $@"{{ + ""jql"": ""{jql}"", + ""maxResults"": {increment}, + ""startAt"": {startAt} + }}"; + + var response = await PostAsync($"{_url}/rest/api/2/search", query); + + var jqlQueryResult = JsonSerializer.Deserialize(response, jsonSerializerOptions); + + Console.WriteLine($"Processing {startAt} of {jqlQueryResult.Total} {DateTime.Now}"); + + startAt += increment; + finishAt = jqlQueryResult.Total - 1; + + foreach (var issue in jqlQueryResult.Issues) + { + var issueType = issue.Fields.IssueType.Name; + + var row = new BacklogItem + { + Date = DateTime.Now.Date, + Sprint = issue.Fields.Sprints?.OrderByDescending(s => s.StartDate).FirstOrDefault().Name, + IssueId = issue.Key, + IssueTitle = issue.Fields.Summary, + EpicId = issue.Fields.Parent?.Key, + EpicTitle = issue.Fields.Parent?.Fields.Summary, + IssueType = issueType, + Priority = issue.Fields.Priority?.Value, + Status = issue.Fields.Status.Name, + Points = Convert.ToInt32(issue.Fields.Points), + AssignedTo = issue.Fields.Assignee == null ? "Unassigned" : issue.Fields.Assignee.DisplayName, + FixVersion = issue.Fields.FixVersions.LastOrDefault()?.Name + }; + + latestBacklog.Add(row); + } + } + + return latestBacklog; + } + + public async Task> GetSprintsAsync(int boardId) + { + var increment = 50; + var startAt = 0; + var isLast = false; + var result = new List(); + + while (!isLast) + { + var response = await GetAsync($"{_url}/rest/agile/latest/board/{boardId}/sprint?startAt={startAt}&maxResults=50"); + var sprintsResult = JsonSerializer.Deserialize(response, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); + isLast = sprintsResult.IsLast; + result.AddRange(sprintsResult.Values); + startAt += increment; + } + + return result; + } + } +} diff --git a/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Assignee.cs b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Assignee.cs new file mode 100644 index 0000000..094a988 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Assignee.cs @@ -0,0 +1,7 @@ +namespace JiraReporting.Model +{ + public class Assignee + { + public string DisplayName { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/BacklogItem.cs b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/BacklogItem.cs new file mode 100644 index 0000000..21c47ba --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/BacklogItem.cs @@ -0,0 +1,27 @@ +using System; +using System.Text.Json.Serialization; + +namespace JiraReporting.Report +{ + public class BacklogItem + { + public BacklogItem() + { + } + + + public string EpicId { get; set; } + public string EpicTitle { get; set; } + public string IssueType { get; set; } + public string Sprint { get; set; } + public string Status { get; set; } + public int Points { get; set; } + public string AssignedTo { get; set; } + public DateTime Date { get; set; } + public string IssueId { get; set; } + public string IssueTitle { get; set; } + public string Priority { get; set; } + public string FixVersion { get; set; } + + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Field.cs b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Field.cs new file mode 100644 index 0000000..8ebf8dd --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Field.cs @@ -0,0 +1,6 @@ +namespace JiraReporting.Model +{ + public class Field + { + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Fields.cs b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Fields.cs new file mode 100644 index 0000000..1947739 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Fields.cs @@ -0,0 +1,43 @@ +using JiraReporting.Model; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace JiraReporting.Model +{ + public class Fields + { + public string Summary { get; set; } + public Parent Parent { get; set; } + public Status Status { get; set; } + public IssueType IssueType { get; set; } + public Assignee Assignee { get; set; } + + public Priority Priority { get; set; } + + //TODO: Make this customizable + [JsonPropertyName("customfield_10020")] + public List Sprints { get; set; } + + [JsonPropertyName("customfield_10026")] + public double? Points { get; set; } + + [JsonPropertyName("fixVersions")] + public List FixVersions { get; set; } + + /* + "fixVersions": [ + { + "self": "https://humanforce.atlassian.net/rest/api/2/version/10613", + "id": "10613", + "description": "", + "name": "5.0.16", + "archived": false, + "released": true, + "releaseDate": "2022-08-25" + } + ], + + */ + + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/FixVersion.cs b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/FixVersion.cs new file mode 100644 index 0000000..9e38956 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/FixVersion.cs @@ -0,0 +1,7 @@ +namespace JiraReporting.Model +{ + public class FixVersion + { + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Issue.cs b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Issue.cs new file mode 100644 index 0000000..d6fca6a --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Issue.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace JiraReporting.Model +{ + public class Issue + { + public Fields Fields { get; set; } + public string Key { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/IssueType.cs b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/IssueType.cs new file mode 100644 index 0000000..772b861 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/IssueType.cs @@ -0,0 +1,7 @@ +namespace JiraReporting.Model +{ + public class IssueType + { + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/JqlQueryResult.cs b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/JqlQueryResult.cs new file mode 100644 index 0000000..1f9cabf --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/JqlQueryResult.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace JiraReporting.Model +{ + internal class JqlQueryResult + { + public int StartAt { get; set; } + public int MaxResults { get; set; } + public int Total { get; set; } + public List Issues { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Parent.cs b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Parent.cs new file mode 100644 index 0000000..2c73abb --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Parent.cs @@ -0,0 +1,8 @@ +namespace JiraReporting.Model +{ + public class Parent + { + public Fields Fields { get; set; } + public string Key { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Priority.cs b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Priority.cs new file mode 100644 index 0000000..f751114 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Priority.cs @@ -0,0 +1,7 @@ +namespace JiraReporting.Model +{ + public class Priority + { + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/RaidType.cs b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/RaidType.cs new file mode 100644 index 0000000..6d275cd --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/RaidType.cs @@ -0,0 +1,7 @@ +namespace JiraReporting.Model +{ + public class RaidType + { + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Sprint.cs b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Sprint.cs new file mode 100644 index 0000000..4cb695f --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Sprint.cs @@ -0,0 +1,10 @@ +namespace JiraReporting.Model +{ + public class Sprint + { + public string Name { get; set; } + public string State { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/SprintsResult.cs b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/SprintsResult.cs new file mode 100644 index 0000000..c6c8dbd --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/SprintsResult.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using JiraReporting.Model; + +namespace IntegrationConnectors.JIRA.Model +{ + internal class SprintsResult + { + public bool IsLast { get; set; } + public List Values { get; set; } + } +} diff --git a/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Status.cs b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Status.cs new file mode 100644 index 0000000..ff58725 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.JIRA/Model/Status.cs @@ -0,0 +1,7 @@ +namespace JiraReporting.Model +{ + public class Status + { + public string Name { get; set; } + } +} \ No newline at end of file From aafdf35214600643a2805d30094d33f5982ada95 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Tue, 4 Oct 2022 08:15:01 +1000 Subject: [PATCH 50/56] [Tools] migrated JIRA Reporting logic to its own integration connector --- Tools/src/JiraReporting/JiraReporting.csproj | 1 + Tools/src/JiraReporting/Model/Assignee.cs | 7 -- Tools/src/JiraReporting/Model/Field.cs | 6 - Tools/src/JiraReporting/Model/Fields.cs | 25 ---- Tools/src/JiraReporting/Model/Issue.cs | 10 -- Tools/src/JiraReporting/Model/IssueType.cs | 7 -- .../src/JiraReporting/Model/JqlQueryResult.cs | 12 -- Tools/src/JiraReporting/Model/Parent.cs | 8 -- Tools/src/JiraReporting/Model/Priority.cs | 7 -- Tools/src/JiraReporting/Model/RaidType.cs | 7 -- Tools/src/JiraReporting/Model/Sprint.cs | 10 -- Tools/src/JiraReporting/Model/Status.cs | 7 -- Tools/src/JiraReporting/Program.cs | 111 +----------------- .../PublishProfiles/FolderProfile.pubxml | 19 +++ Tools/src/JiraReporting/Report/BacklogItem.cs | 26 ---- 15 files changed, 25 insertions(+), 238 deletions(-) delete mode 100644 Tools/src/JiraReporting/Model/Assignee.cs delete mode 100644 Tools/src/JiraReporting/Model/Field.cs delete mode 100644 Tools/src/JiraReporting/Model/Fields.cs delete mode 100644 Tools/src/JiraReporting/Model/Issue.cs delete mode 100644 Tools/src/JiraReporting/Model/IssueType.cs delete mode 100644 Tools/src/JiraReporting/Model/JqlQueryResult.cs delete mode 100644 Tools/src/JiraReporting/Model/Parent.cs delete mode 100644 Tools/src/JiraReporting/Model/Priority.cs delete mode 100644 Tools/src/JiraReporting/Model/RaidType.cs delete mode 100644 Tools/src/JiraReporting/Model/Sprint.cs delete mode 100644 Tools/src/JiraReporting/Model/Status.cs create mode 100644 Tools/src/JiraReporting/Properties/PublishProfiles/FolderProfile.pubxml delete mode 100644 Tools/src/JiraReporting/Report/BacklogItem.cs diff --git a/Tools/src/JiraReporting/JiraReporting.csproj b/Tools/src/JiraReporting/JiraReporting.csproj index ab3081f..d70b043 100644 --- a/Tools/src/JiraReporting/JiraReporting.csproj +++ b/Tools/src/JiraReporting/JiraReporting.csproj @@ -14,6 +14,7 @@ +
diff --git a/Tools/src/JiraReporting/Model/Assignee.cs b/Tools/src/JiraReporting/Model/Assignee.cs deleted file mode 100644 index 094a988..0000000 --- a/Tools/src/JiraReporting/Model/Assignee.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace JiraReporting.Model -{ - public class Assignee - { - public string DisplayName { get; set; } - } -} \ No newline at end of file diff --git a/Tools/src/JiraReporting/Model/Field.cs b/Tools/src/JiraReporting/Model/Field.cs deleted file mode 100644 index 8ebf8dd..0000000 --- a/Tools/src/JiraReporting/Model/Field.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace JiraReporting.Model -{ - public class Field - { - } -} \ No newline at end of file diff --git a/Tools/src/JiraReporting/Model/Fields.cs b/Tools/src/JiraReporting/Model/Fields.cs deleted file mode 100644 index b7af47f..0000000 --- a/Tools/src/JiraReporting/Model/Fields.cs +++ /dev/null @@ -1,25 +0,0 @@ -using JiraReporting.Model; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace JiraReporting.Model -{ - public class Fields - { - public string Summary { get; set; } - public Parent Parent { get; set; } - public Status Status { get; set; } - public IssueType IssueType { get; set; } - public Assignee Assignee { get; set; } - - public Priority Priority { get; set; } - - //TODO: Make this customizable - [JsonPropertyName("customfield_10020")] - public List Sprints { get; set; } - - [JsonPropertyName("customfield_10026")] - public double? Points { get; set; } - - } -} \ No newline at end of file diff --git a/Tools/src/JiraReporting/Model/Issue.cs b/Tools/src/JiraReporting/Model/Issue.cs deleted file mode 100644 index d6fca6a..0000000 --- a/Tools/src/JiraReporting/Model/Issue.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace JiraReporting.Model -{ - public class Issue - { - public Fields Fields { get; set; } - public string Key { get; set; } - } -} \ No newline at end of file diff --git a/Tools/src/JiraReporting/Model/IssueType.cs b/Tools/src/JiraReporting/Model/IssueType.cs deleted file mode 100644 index 772b861..0000000 --- a/Tools/src/JiraReporting/Model/IssueType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace JiraReporting.Model -{ - public class IssueType - { - public string Name { get; set; } - } -} \ No newline at end of file diff --git a/Tools/src/JiraReporting/Model/JqlQueryResult.cs b/Tools/src/JiraReporting/Model/JqlQueryResult.cs deleted file mode 100644 index 1f9cabf..0000000 --- a/Tools/src/JiraReporting/Model/JqlQueryResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; - -namespace JiraReporting.Model -{ - internal class JqlQueryResult - { - public int StartAt { get; set; } - public int MaxResults { get; set; } - public int Total { get; set; } - public List Issues { get; set; } - } -} \ No newline at end of file diff --git a/Tools/src/JiraReporting/Model/Parent.cs b/Tools/src/JiraReporting/Model/Parent.cs deleted file mode 100644 index 2c73abb..0000000 --- a/Tools/src/JiraReporting/Model/Parent.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace JiraReporting.Model -{ - public class Parent - { - public Fields Fields { get; set; } - public string Key { get; set; } - } -} \ No newline at end of file diff --git a/Tools/src/JiraReporting/Model/Priority.cs b/Tools/src/JiraReporting/Model/Priority.cs deleted file mode 100644 index f751114..0000000 --- a/Tools/src/JiraReporting/Model/Priority.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace JiraReporting.Model -{ - public class Priority - { - public string Value { get; set; } - } -} \ No newline at end of file diff --git a/Tools/src/JiraReporting/Model/RaidType.cs b/Tools/src/JiraReporting/Model/RaidType.cs deleted file mode 100644 index 6d275cd..0000000 --- a/Tools/src/JiraReporting/Model/RaidType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace JiraReporting.Model -{ - public class RaidType - { - public string Value { get; set; } - } -} \ No newline at end of file diff --git a/Tools/src/JiraReporting/Model/Sprint.cs b/Tools/src/JiraReporting/Model/Sprint.cs deleted file mode 100644 index 626e464..0000000 --- a/Tools/src/JiraReporting/Model/Sprint.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace JiraReporting.Model -{ - public class Sprint - { - public string Name { get; set; } - public string State { get; set; } - public string StartDate { get; set; } - public string EndDate { get; set; } - } -} \ No newline at end of file diff --git a/Tools/src/JiraReporting/Model/Status.cs b/Tools/src/JiraReporting/Model/Status.cs deleted file mode 100644 index ff58725..0000000 --- a/Tools/src/JiraReporting/Model/Status.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace JiraReporting.Model -{ - public class Status - { - public string Name { get; set; } - } -} \ No newline at end of file diff --git a/Tools/src/JiraReporting/Program.cs b/Tools/src/JiraReporting/Program.cs index 0ec0dc2..6c0cc4c 100644 --- a/Tools/src/JiraReporting/Program.cs +++ b/Tools/src/JiraReporting/Program.cs @@ -2,15 +2,13 @@ using Hangfire; using Hangfire.MemoryStorage; using IntegrationConnectors.Common; -using JiraReporting.Model; +using IntegrationConnectors.JIRA; using JiraReporting.Report; using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; using System.Text.Json; -using System.Text.Json.Serialization; namespace JiraReporting { @@ -23,11 +21,7 @@ static void Main(string[] args) Parser.Default.ParseArguments(args) .WithParsed(o => { - //ExecuteJob(o.JiraEndpoint, o.JiraProject, o.JiraUsername, o.JiraAuthenticationToken, o.PowerBiDatasetEndpoint); ExecuteJob(o.JiraEndpoint, o.JQL, o.JiraUsername, o.JiraAuthenticationToken); - //RecurringJob.AddOrUpdate("JiraReportJob", - // () => ExecuteJob(o.JiraEndpoint, o.JiraProject, o.JiraUsername, o.JiraAuthenticationToken, o.PowerBiDatasetEndpoint), - // Cron.Hourly); }); using var server = new BackgroundJobServer(); @@ -42,109 +36,14 @@ public static void ExecuteJob(string jiraEndpoint, string jql, string jiraUserna var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{jiraUsername}:{jiraAuthenticationToken}")); - HttpConnector httpConnector = new("", credentials, AuthenticationType.Basic); - - var jsonSerializerOptions = new JsonSerializerOptions() - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - PropertyNameCaseInsensitive = true, - //MaxDepth = 1 - }; - - - //Checkpoint Backlog - List checkpointBacklog = null; - var checkpointDate = DateTime.Now.AddDays(-7).ToString(dateFormat); - var checkpointFile = $"{Environment.CurrentDirectory}\\report_{checkpointDate}.json"; - - if (File.Exists(checkpointFile)) - { - string checkpointBacklogJson = File.ReadAllText(checkpointFile); - checkpointBacklog = JsonSerializer.Deserialize>(checkpointBacklogJson, jsonSerializerOptions); - } - - //Latest Backlog - - var latestBacklog = new List(); - - var increment = 100; - var startAt = 0; - var finishAt = 1; - - while (startAt <= finishAt) - { - var query = $@"{{ - ""jql"": ""{jql}"", - ""maxResults"": {increment}, - ""startAt"": {startAt} - }}"; - - var response = httpConnector.PostAsync($"{jiraEndpoint}/rest/api/2/search", query).Result; - var jqlQueryResult = JsonSerializer.Deserialize(response, jsonSerializerOptions); - - Console.WriteLine($"Processing {startAt} of {jqlQueryResult.Total} {DateTime.Now}"); - - startAt += increment; - finishAt = jqlQueryResult.Total - 1; - - foreach (var issue in jqlQueryResult.Issues) - { - var issueType = issue.Fields.IssueType.Name; - - var row = new BacklogItem - { - Date = DateTime.Now.Date, - Sprint = issue.Fields.Sprints?.OrderByDescending(s => s.StartDate).FirstOrDefault().Name, - IssueId = issue.Key, - IssueTitle = issue.Fields.Summary, - EpicId = issue.Fields.Parent?.Key, - EpicTitle = issue.Fields.Parent?.Fields.Summary, - IssueType = issueType, - Priority = issue.Fields.Priority?.Value, - Status = issue.Fields.Status.Name, - Points = Convert.ToInt32(issue.Fields.Points), - AssignedTo = issue.Fields.Assignee == null ? "Unassigned" : issue.Fields.Assignee.DisplayName - }; - - latestBacklog.Add(row); - } - } - - List changedStories = new(); - List changedBugs = new(); - List changedRaids = new(); - - if (checkpointBacklog != null) - { - var newItems = latestBacklog.Where(pbi => !checkpointBacklog.Exists(cpbi => cpbi.IssueId.Equals(pbi.IssueId))).ToList(); - var newStories = newItems.Where(i => i.IssueType.Equals("Story")).ToList(); - var newBugs = newItems.Where(i => i.IssueType.Equals("Bug")).ToList(); - var newRaids = newItems.Where(i => i.IssueType.Contains("RAID")).ToList(); - - var changedItems = latestBacklog.Where(pbi => checkpointBacklog.Exists(cpbi => cpbi.IssueId.Equals(pbi.IssueId)) && - checkpointBacklog.Count(cpbi => !cpbi.Status.Equals(pbi.Status)) > 0).ToList(); - changedStories = changedItems.Where(i => i.IssueType.Equals("Story")).ToList(); - changedBugs = changedItems.Where(i => i.IssueType.Equals("Bug")).ToList(); - changedRaids = changedItems.Where(i => i.IssueType.Contains("RAID") || - i.IssueType.Contains("Dependency") || - i.IssueType.Contains("Impediment") || - i.IssueType.Contains("Risk") || - i.IssueType.Contains("Assumption")).ToList(); - } - + var jiraConnector = new JiraConnector(jiraEndpoint, credentials, AuthenticationType.Basic); + List latestBacklog = jiraConnector.GetBacklogItemsAsync(jql).Result; var outputFile = $"{Environment.CurrentDirectory}\\report_{DateTime.Now.Date.ToString(dateFormat)}"; - - ExcelHelper.Export(latestBacklog, null, $"{outputFile}.xlsx", jiraEndpoint); - //ExcelHelper.Export(changedStories, null, $"ChangedStories.xlsx", jiraEndpoint); - //ExcelHelper.Export(changedBugs, null, $"ChangedBugs.xlsx", jiraEndpoint); - //ExcelHelper.Export(changedRaids, null, $"ChangedRaids.xlsx", jiraEndpoint); - File.WriteAllText($"{outputFile}.json", JsonSerializer.Serialize(latestBacklog)); + ExcelHelper.Export(latestBacklog, null, $"{outputFile}.xlsx", jiraEndpoint); - //var result = httpConnector.PostAsync(powerBiDatasetEndpoint, JsonSerializer.Serialize(latestBacklog)).Result; - - Console.WriteLine($"Jira Report stopped {DateTime.Now}"); + Console.WriteLine($"Jira Report finished {DateTime.Now}"); } } } diff --git a/Tools/src/JiraReporting/Properties/PublishProfiles/FolderProfile.pubxml b/Tools/src/JiraReporting/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..69db3e7 --- /dev/null +++ b/Tools/src/JiraReporting/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,19 @@ + + + + + Release + Any CPU + C:\Dev\ToolRelease\JiraReporting + FileSystem + <_TargetId>Folder + net6.0 + win-x86 + true + true + false + false + + \ No newline at end of file diff --git a/Tools/src/JiraReporting/Report/BacklogItem.cs b/Tools/src/JiraReporting/Report/BacklogItem.cs deleted file mode 100644 index 1fdddc9..0000000 --- a/Tools/src/JiraReporting/Report/BacklogItem.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Text.Json.Serialization; - -namespace JiraReporting.Report -{ - public class BacklogItem - { - public BacklogItem() - { - } - - - public string EpicId { get; set; } - public string EpicTitle { get; set; } - public string IssueType { get; set; } - public string Sprint { get; set; } - public string Status { get; set; } - public int Points { get; set; } - public string AssignedTo { get; set; } - public DateTime Date { get; set; } - public string IssueId { get; set; } - public string IssueTitle { get; set; } - public string Priority { get; set; } - - } -} \ No newline at end of file From b7c182115cb3b440550a24163280f98333f1c3f8 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Tue, 4 Oct 2022 11:30:31 +1000 Subject: [PATCH 51/56] [Tools] Added TCP Server/Client --- TcpServerClient/Tcp.Client/Program.cs | 20 ++++++ TcpServerClient/Tcp.Client/Tcp.Client.csproj | 10 +++ TcpServerClient/TcpServerClient.sln | 31 ++++++++ TcpServerClient/TcpServerClient/Program.cs | 71 +++++++++++++++++++ .../TcpServerClient/Tcp.Server.csproj | 14 ++++ 5 files changed, 146 insertions(+) create mode 100644 TcpServerClient/Tcp.Client/Program.cs create mode 100644 TcpServerClient/Tcp.Client/Tcp.Client.csproj create mode 100644 TcpServerClient/TcpServerClient.sln create mode 100644 TcpServerClient/TcpServerClient/Program.cs create mode 100644 TcpServerClient/TcpServerClient/Tcp.Server.csproj diff --git a/TcpServerClient/Tcp.Client/Program.cs b/TcpServerClient/Tcp.Client/Program.cs new file mode 100644 index 0000000..fa951cd --- /dev/null +++ b/TcpServerClient/Tcp.Client/Program.cs @@ -0,0 +1,20 @@ +using System.Net.Sockets; +using System.Net; +using System.Text; + +var ipEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8888); + +var key = Console.ReadKey(); +while (key.Key != ConsoleKey.Escape) +{ + using TcpClient client = new(); + await client.ConnectAsync(ipEndPoint); + await using NetworkStream stream = client.GetStream(); + + var message = $"{key.Key}"; + var dateTimeBytes = Encoding.UTF8.GetBytes(message); + await stream.WriteAsync(dateTimeBytes); + Console.WriteLine($"\"{message}\" sent at {DateTime.Now.ToString("O")}"); + Console.WriteLine(); + key = Console.ReadKey(); +} diff --git a/TcpServerClient/Tcp.Client/Tcp.Client.csproj b/TcpServerClient/Tcp.Client/Tcp.Client.csproj new file mode 100644 index 0000000..74abf5c --- /dev/null +++ b/TcpServerClient/Tcp.Client/Tcp.Client.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + diff --git a/TcpServerClient/TcpServerClient.sln b/TcpServerClient/TcpServerClient.sln new file mode 100644 index 0000000..ec3ce49 --- /dev/null +++ b/TcpServerClient/TcpServerClient.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32922.545 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tcp.Server", "TcpServerClient\Tcp.Server.csproj", "{4F6C216C-C550-4D74-8C87-F20A9F1C46BE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tcp.Client", "Tcp.Client\Tcp.Client.csproj", "{2B4DC95A-EFA3-4C1C-8763-FF237FC40909}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4F6C216C-C550-4D74-8C87-F20A9F1C46BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4F6C216C-C550-4D74-8C87-F20A9F1C46BE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F6C216C-C550-4D74-8C87-F20A9F1C46BE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4F6C216C-C550-4D74-8C87-F20A9F1C46BE}.Release|Any CPU.Build.0 = Release|Any CPU + {2B4DC95A-EFA3-4C1C-8763-FF237FC40909}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2B4DC95A-EFA3-4C1C-8763-FF237FC40909}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2B4DC95A-EFA3-4C1C-8763-FF237FC40909}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2B4DC95A-EFA3-4C1C-8763-FF237FC40909}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9EC1F424-11B3-41A2-85A6-AD8C047D441B} + EndGlobalSection +EndGlobal diff --git a/TcpServerClient/TcpServerClient/Program.cs b/TcpServerClient/TcpServerClient/Program.cs new file mode 100644 index 0000000..cab6db4 --- /dev/null +++ b/TcpServerClient/TcpServerClient/Program.cs @@ -0,0 +1,71 @@ +//https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/tcp/tcp-services + +using System.Net.Sockets; +using System.Net; +using System.Text; +using InputSimulatorStandard; +using InputSimulatorStandard.Native; + +var ipEndPoint = new IPEndPoint(IPAddress.Any, 8888); +TcpListener listener = new(ipEndPoint); + +try +{ + + Console.WriteLine($"Server starts"); + var inputSimulator = new InputSimulator(); + + listener.Start(); + + while (true) + { + using TcpClient handler = await listener.AcceptTcpClientAsync(); + await using NetworkStream stream = handler.GetStream(); + + var buffer = new byte[1_024]; + int received = await stream.ReadAsync(buffer); + var message = Encoding.UTF8.GetString(buffer, 0, received); + + /* + https://www.twoplayergames.org/game/minibattles-2-6-players + GAME CONTROLS: + + Player 1 : C + Player 2 : N + Player 3 : Q + Player 4 : P + Player 5 : Left Arrow + Player 6 : Right Arrow + */ + + switch (message) + { + case "C": + inputSimulator.Keyboard.KeyPress(VirtualKeyCode.VK_C); + break; + case "N": + inputSimulator.Keyboard.KeyPress(VirtualKeyCode.VK_N); + break; + case "Q": + inputSimulator.Keyboard.KeyPress(VirtualKeyCode.VK_Q); + break; + case "P": + inputSimulator.Keyboard.KeyPress(VirtualKeyCode.VK_P); + break; + case "LeftArrow": + inputSimulator.Keyboard.KeyPress(VirtualKeyCode.LEFT); + break; + case "RightArrow": + inputSimulator.Keyboard.KeyPress(VirtualKeyCode.RIGHT); + break; + default: + break; + } + + Console.WriteLine($"\"{message}\" received at {DateTime.Now.ToString("O")}"); + } +} +finally +{ + listener.Stop(); +} \ No newline at end of file diff --git a/TcpServerClient/TcpServerClient/Tcp.Server.csproj b/TcpServerClient/TcpServerClient/Tcp.Server.csproj new file mode 100644 index 0000000..5dfd3c9 --- /dev/null +++ b/TcpServerClient/TcpServerClient/Tcp.Server.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + From 1e79159baac05a564b058cb5f2ad9911976d487c Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Wed, 5 Oct 2022 08:53:45 +1000 Subject: [PATCH 52/56] [IntegrationConnectors] Bitbucket changes --- .../BitbucketConnector.cs | 28 +++++++++++-------- .../Model/BitBucketLinkHtml.cs | 7 +++++ .../Model/BitBucketLinks.cs | 7 +++++ .../Model/BitBucketPullRequest.cs | 12 ++++++++ .../Model/BitBucketPullRequests.cs | 1 + 5 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketLinkHtml.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketLinks.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketPullRequest.cs diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/BitbucketConnector.cs b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/BitbucketConnector.cs index 26f9525..fad80d4 100644 --- a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/BitbucketConnector.cs +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/BitbucketConnector.cs @@ -14,28 +14,34 @@ public async Task> GetBranchesAsync(string company, string { var pageNumber = 1; var allBranches = new List(); - - var response = await GetAsync($"{_url}/2.0/repositories/{company}/{repository}/refs/branches?page={pageNumber}&pagelen=100"); - var branches = JsonSerializer.Deserialize(response, _jsonSerializerOptions); - allBranches.AddRange(branches.Values); + var branches = new BitbucketBranches() { Values = new List() { new BitbucketBranch() } }; while (branches.Values.Count > 0) { - pageNumber++; - - response = await GetAsync($"{_url}/2.0/repositories/{company}/{repository}/refs/branches?page={pageNumber}&pagelen=100"); + var response = await GetAsync($"{_url}/2.0/repositories/{company}/{repository}/refs/branches?page={pageNumber}&pagelen=100"); branches = JsonSerializer.Deserialize(response, _jsonSerializerOptions); allBranches.AddRange(branches.Values); + pageNumber++; } return allBranches.OrderByDescending(b => b.Target.Date).ToList(); } - public async Task GetPullRequestsAsync(string company, string repository) + public async Task> GetPullRequestsAsync(string company, string repository) { - var response = await GetAsync($"{_url}/2.0/repositories/{company}/{repository}/pullrequests?page=1"); - var pullRequests = JsonSerializer.Deserialize(response, _jsonSerializerOptions); - return pullRequests; + var pageNumber = 1; + var allPullRequests = new List(); + var pullRequests = new BitBucketPullRequests() { Values = new List() { new BitBucketPullRequest() } }; + + while (pullRequests.Values.Count > 0) + { + var response = await GetAsync($"{_url}/2.0/repositories/{company}/{repository}/pullrequests?page={pageNumber}"); + pullRequests = JsonSerializer.Deserialize(response, _jsonSerializerOptions); + allPullRequests.AddRange(pullRequests.Values); + pageNumber++; + } + + return allPullRequests.OrderByDescending(pr => pr.Created).ToList(); } } } \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketLinkHtml.cs b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketLinkHtml.cs new file mode 100644 index 0000000..eb759bb --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketLinkHtml.cs @@ -0,0 +1,7 @@ +namespace IntegrationConnectors.Bitbucket +{ + public class BitBucketLinkHtml + { + public string Href { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketLinks.cs b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketLinks.cs new file mode 100644 index 0000000..190002e --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketLinks.cs @@ -0,0 +1,7 @@ +namespace IntegrationConnectors.Bitbucket +{ + public class BitBucketLinks + { + public BitBucketLinkHtml Html { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketPullRequest.cs b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketPullRequest.cs new file mode 100644 index 0000000..bd7732c --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketPullRequest.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace IntegrationConnectors.Bitbucket +{ + public class BitBucketPullRequest + { + public string Title { get; set; } + [JsonPropertyName("created_on")] + public DateTime Created { get; set; } + public BitBucketLinks Links { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketPullRequests.cs b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketPullRequests.cs index fa21361..5390d0a 100644 --- a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketPullRequests.cs +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketPullRequests.cs @@ -2,5 +2,6 @@ { public class BitBucketPullRequests { + public List Values { get; set; } } } \ No newline at end of file From 7ed0aafdfa70f16f72331b099244af0d49f12037 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Thu, 6 Oct 2022 09:44:15 +1000 Subject: [PATCH 53/56] [IntegrationConnectors] BitBucket Connector changes --- .../Model/BitBucketDestination.cs | 7 +++++++ .../Model/BitBucketPullRequest.cs | 1 + .../Model/BitBucketRepository.cs | 7 +++++++ .../Model/BitbucketBranch.cs | 2 ++ 4 files changed, 17 insertions(+) create mode 100644 IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketDestination.cs create mode 100644 IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketRepository.cs diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketDestination.cs b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketDestination.cs new file mode 100644 index 0000000..9cbe8d9 --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketDestination.cs @@ -0,0 +1,7 @@ +namespace IntegrationConnectors.Bitbucket +{ + public class BitBucketDestination + { + public BitBucketRepository Repository { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketPullRequest.cs b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketPullRequest.cs index bd7732c..24d34c7 100644 --- a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketPullRequest.cs +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketPullRequest.cs @@ -8,5 +8,6 @@ public class BitBucketPullRequest [JsonPropertyName("created_on")] public DateTime Created { get; set; } public BitBucketLinks Links { get; set; } + public BitBucketDestination Destination { get; set; } } } \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketRepository.cs b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketRepository.cs new file mode 100644 index 0000000..9b13d7f --- /dev/null +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitBucketRepository.cs @@ -0,0 +1,7 @@ +namespace IntegrationConnectors.Bitbucket +{ + public class BitBucketRepository + { + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitbucketBranch.cs b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitbucketBranch.cs index deec867..3a07a60 100644 --- a/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitbucketBranch.cs +++ b/IntegrationConnectors/src/IntegrationConnectors.Bitbucket/Model/BitbucketBranch.cs @@ -6,5 +6,7 @@ public class BitbucketBranch public Target Target { get; set; } + public BitBucketLinks Links { get; set; } + } } \ No newline at end of file From b0f25f8a4314604d021877ffd51c660c1ec4a2d6 Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Tue, 11 Oct 2022 09:23:11 +1000 Subject: [PATCH 54/56] Added Code Katas --- CodeKata/CSharp/CodeKata.CSharp.csproj | 4 +- .../Benchmark/Kata3_BenchmarkHelper.cs | 33 ++++++++++ CodeKata/CSharp/Helpers/Kata4/JobCreated.cs | 15 +++++ CodeKata/CSharp/Kata3_Iteration_Benchmark.cs | 6 ++ CodeKata/CSharp/Kata4_AWS_SQS.cs | 64 +++++++++++++++++++ .../CSharp/Properties/launchSettings.json | 6 ++ 6 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 CodeKata/CSharp/Helpers/Benchmark/Kata3_BenchmarkHelper.cs create mode 100644 CodeKata/CSharp/Helpers/Kata4/JobCreated.cs create mode 100644 CodeKata/CSharp/Kata3_Iteration_Benchmark.cs create mode 100644 CodeKata/CSharp/Kata4_AWS_SQS.cs diff --git a/CodeKata/CSharp/CodeKata.CSharp.csproj b/CodeKata/CSharp/CodeKata.CSharp.csproj index 0e4ee56..55ac5f4 100644 --- a/CodeKata/CSharp/CodeKata.CSharp.csproj +++ b/CodeKata/CSharp/CodeKata.CSharp.csproj @@ -2,10 +2,12 @@ Exe - net5.0 + net6.0 + + diff --git a/CodeKata/CSharp/Helpers/Benchmark/Kata3_BenchmarkHelper.cs b/CodeKata/CSharp/Helpers/Benchmark/Kata3_BenchmarkHelper.cs new file mode 100644 index 0000000..b62d07b --- /dev/null +++ b/CodeKata/CSharp/Helpers/Benchmark/Kata3_BenchmarkHelper.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; + +namespace CodeKata.CSharp.Helpers.Benchmark +{ + [MemoryDiagnoser(false)] + public class Kata3_BenchmarkHelper + { + private static readonly Random Rng = new(100); + + public int Size { get; set; } = 100; + + private List _items; + + [GlobalSetup] + public void Setup() + { + _items = Enumerable.Range(1, Size).Select(x => Rng.Next()).ToList(); + } + + [Benchmark] + public void ForEach() + { + foreach (var item in _items) + { + } + } + } +} diff --git a/CodeKata/CSharp/Helpers/Kata4/JobCreated.cs b/CodeKata/CSharp/Helpers/Kata4/JobCreated.cs new file mode 100644 index 0000000..ad9ab35 --- /dev/null +++ b/CodeKata/CSharp/Helpers/Kata4/JobCreated.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace CodeKata.CSharp.Helpers.Kata4 +{ + internal class JobCreated + { + [JsonPropertyName("id")] + public int Id { get; set; } + } +} diff --git a/CodeKata/CSharp/Kata3_Iteration_Benchmark.cs b/CodeKata/CSharp/Kata3_Iteration_Benchmark.cs new file mode 100644 index 0000000..9d4acbb --- /dev/null +++ b/CodeKata/CSharp/Kata3_Iteration_Benchmark.cs @@ -0,0 +1,6 @@ +using System; +using BenchmarkDotNet.Running; +using CodeKata.CSharp.Helpers.Benchmark; + +BenchmarkRunner.Run(); +Console.ReadLine(); \ No newline at end of file diff --git a/CodeKata/CSharp/Kata4_AWS_SQS.cs b/CodeKata/CSharp/Kata4_AWS_SQS.cs new file mode 100644 index 0000000..f56785a --- /dev/null +++ b/CodeKata/CSharp/Kata4_AWS_SQS.cs @@ -0,0 +1,64 @@ +/* + * AWS CLI + * aws --endpoint-url=http://localhost:4566 sqs create-queue --queue-name myQueue + * aws --endpoint-url=http://localhost:4566 sqs list-queues + * aws --endpoint-url=http://localhost:4566 sqs delete-queue --queue-url +*/ + +using Amazon.SQS; +using Amazon.SQS.Model; +using CodeKata.CSharp.Helpers.Kata4; +using System; +using System.Text.Json; +using System.Collections.Generic; +using System.Net; +using System.Threading; + +var config = new AmazonSQSConfig(); +config.ServiceURL = "http://localhost:4566"; +var sqsClient = new AmazonSQSClient(config); +var queueName = "myQueue"; +var queue = await sqsClient.GetQueueUrlAsync(queueName); + +//Add 10 items to the queue +for (int i = 0; i < 100; i++) +{ + var payload = new JobCreated() { Id = i }; + + var request = new SendMessageRequest() + { + QueueUrl = queue.QueueUrl, + MessageBody = JsonSerializer.Serialize(payload) + }; + + await sqsClient.SendMessageAsync(request); +} + + +var ct = new CancellationToken(); + +while (!ct.IsCancellationRequested) +{ + var receiveRequest = new ReceiveMessageRequest() + { + QueueUrl = queue.QueueUrl, + MessageAttributeNames = new List() { "All" }, + AttributeNames = new List() { "All" } + }; + + var response = await sqsClient.ReceiveMessageAsync(receiveRequest); + if (response.HttpStatusCode != HttpStatusCode.OK) + { + //handle error + } + + foreach (var msg in response.Messages) + { + Console.WriteLine(msg.Body); + //Once the message is processed we need to remove it from the queue, so it's not reprocessed. + await sqsClient.DeleteMessageAsync(queue.QueueUrl, msg.ReceiptHandle); + } +} + + +Console.ReadLine(); \ No newline at end of file diff --git a/CodeKata/CSharp/Properties/launchSettings.json b/CodeKata/CSharp/Properties/launchSettings.json index 1c42e96..831853c 100644 --- a/CodeKata/CSharp/Properties/launchSettings.json +++ b/CodeKata/CSharp/Properties/launchSettings.json @@ -5,6 +5,12 @@ }, "Kata2_EncryptionUsingCertificate.cs": { "commandName": "Project" + }, + "Kata3_Iteration_Benchmark.cs": { + "commandName": "Project" + }, + "Kata4_AWS_SQS.cs": { + "commandName": "Project" } } } \ No newline at end of file From 1a16fe37fd88011145e2c0e0db243a1eb439ad6d Mon Sep 17 00:00:00 2001 From: leandromonaco Date: Thu, 20 Oct 2022 09:04:18 +1000 Subject: [PATCH 55/56] [Tools] EnvironmentInitializer Improvements --- .../tools/EnvironmentInitializer/Helper.cs | 96 +++++++++++++------ .../tools/EnvironmentInitializer/Program.cs | 59 +++++++----- 2 files changed, 101 insertions(+), 54 deletions(-) diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/Helper.cs b/MicroserviceTemplate/tools/EnvironmentInitializer/Helper.cs index b3f0879..6670e38 100644 --- a/MicroserviceTemplate/tools/EnvironmentInitializer/Helper.cs +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/Helper.cs @@ -7,54 +7,82 @@ namespace EnvironmentInitializer { public static class Helper { - public static string RunCmdCommand(string command, string? directory = null, bool waitForExit = true) + public static void UpdateTargetPropertyJson(string targetJson, string newKmsKeyId, string newKmsPublicKey) { - try + var targetConfiguration = File.ReadAllText(targetJson); + JsonNode? jsonNode = JsonSerializer.Deserialize(targetConfiguration); + jsonNode!["ModuleConfiguration"]!["Infrastructure"]!["Kms"]!["SigningKeyId"] = newKmsKeyId; + jsonNode!["ModuleConfiguration"]!["Infrastructure"]!["Kms"]!["PublicKey"] = newKmsPublicKey; + File.WriteAllText(targetJson, JsonSerializer.Serialize(jsonNode, new JsonSerializerOptions() { WriteIndented = true })); + } + + public static void UpdateTargetPropertyXml(string targetXml, string newKmsKeyId, string newKmsPublicKey) + { + var xmlDocument = new XmlDocument { - var p = new Process(); - if (!string.IsNullOrEmpty(directory)) - { - p.StartInfo.WorkingDirectory = directory; - } - p.StartInfo.UseShellExecute = false; - p.StartInfo.RedirectStandardOutput = true; - p.StartInfo.FileName = "cmd.exe"; - p.StartInfo.Arguments = $"/C {command}"; - p.Start(); - if (waitForExit) - { - var output = p.StandardOutput.ReadToEnd(); - p.WaitForExit(); - return output; - } + PreserveWhitespace = true //to preserve formatting this must be done before loading so that the whitespace doesn't get thrown away at load time + }; - return string.Empty; + xmlDocument.Load(targetXml); + + XmlNode? node = xmlDocument.SelectSingleNode("//add[@key='amazonKmsSigningKeyId']"); + if (node != null && node.Attributes != null) + { + node.Attributes["value"]!.Value = newKmsKeyId; } - catch + + node = xmlDocument.SelectSingleNode("//add[@key='amazonKmsPublicKey']"); + if (node != null && node.Attributes != null) { - return string.Empty; + node.Attributes["value"]!.Value = newKmsPublicKey; } + + xmlDocument.Save(targetXml); } - public static string RunPowerShellCommand(string command, string? directory = null, bool waitForExit = true) + public static string? RunCommand(ConsoleMode consoleMode, string command, string? directory = null, bool waitForExit = true) { + var fileName = string.Empty; + var arguments = string.Empty; + try { - var p = new Process(); + switch (consoleMode) + { + case ConsoleMode.CommandPrompt: + fileName = "cmd.exe"; + arguments = $"/C {command}"; + break; + case ConsoleMode.Powershell: + fileName = "powershell.exe"; + arguments = $" -c {command}"; + break; + default: + break; + } + + + var process = new Process(); if (!string.IsNullOrEmpty(directory)) { - p.StartInfo.WorkingDirectory = directory; + process.StartInfo.WorkingDirectory = directory; } - p.StartInfo.UseShellExecute = false; - p.StartInfo.RedirectStandardOutput = true; - p.StartInfo.FileName = "powershell.exe"; - p.StartInfo.Arguments = $" -c {command}"; - p.Start(); + process.StartInfo.FileName = fileName; + process.StartInfo.Arguments = arguments; + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.CreateNoWindow = true; + process.Start(); if (waitForExit) { - var output = p.StandardOutput.ReadToEnd(); - p.WaitForExit(); + var output = string.Empty; + while (!process.StandardOutput.EndOfStream) + { + output = process.StandardOutput.ReadLine(); + Console.WriteLine(output); + } + return output; } @@ -96,4 +124,10 @@ public static void KillProcess(string name) } } } + + public enum ConsoleMode + { + CommandPrompt, + Powershell + } } \ No newline at end of file diff --git a/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs b/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs index a35b463..a77b873 100644 --- a/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs +++ b/MicroserviceTemplate/tools/EnvironmentInitializer/Program.cs @@ -1,6 +1,8 @@ using EnvironmentInitializer; using Microsoft.Extensions.Configuration; +Console.ForegroundColor = ConsoleColor.White; + var debugEnabled = args.Length > 0 && !string.IsNullOrEmpty(args[0]) && args[0].ToLower().Equals("--debug"); var exitEnabled = args.Length > 0 && !string.IsNullOrEmpty(args[0]) && args[0].ToLower().Equals("--exit"); @@ -22,11 +24,11 @@ Helper.KillProcess("ServiceName.API"); Helper.KillProcess("Authentication.API"); Helper.KillProcess("FeatureManagement.API"); -Helper.KillProcess("Analytics.API"); +Helper.KillProcess("Analytics.API"); Helper.KillProcess("Mock.API"); -Helper.RunPowerShellCommand(@"docker kill $(docker ps -q)"); -Helper.RunPowerShellCommand(@"docker rm --force $(docker ps -a -q)"); -Helper.RunPowerShellCommand(@"docker network prune --force"); +Helper.RunCommand(ConsoleMode.Powershell, @"docker kill $(docker ps -q)"); +Helper.RunCommand(ConsoleMode.Powershell, @"docker rm --force $(docker ps -a -q)"); +Helper.RunCommand(ConsoleMode.Powershell, @"docker network prune --force"); if (exitEnabled) { @@ -34,44 +36,53 @@ return; } +//TODO: Make this a parameter var tyeYmlFolder = @"C:\Dev\GitHub\Workbench\MicroserviceTemplate"; if (debugEnabled) { Console.WriteLine("Running Tye in debug mode... do not forget to attach the debugger!"); - Helper.RunPowerShellCommand(@"tye run --port 10000 --dashboard --debug *", tyeYmlFolder, false); + Helper.RunCommand(ConsoleMode.Powershell, @"tye run --port 10000 --dashboard --debug *", tyeYmlFolder, false); } else { - Console.WriteLine("Running Tye..."); - Helper.RunPowerShellCommand(@"tye run --port 10000 --dashboard", tyeYmlFolder, false); + Console.WriteLine("Spining up new docker containers..."); + Helper.RunCommand(ConsoleMode.Powershell, @"tye run --port 10000 --dashboard", tyeYmlFolder, false); } -Console.WriteLine("Spining up new docker containers..."); + var sourceJson = string.Empty; -var cognitoDockerState = string.Empty; -var dynamoDbDockerState = string.Empty; -while (!cognitoDockerState.Equals("running") && - !dynamoDbDockerState.Equals("running")) + +//Cognito +Console.WriteLine("Waiting for Cognito container to be in RUNNING state"); +var containerState = string.Empty; +while (!containerState.Equals("running")) +{ + sourceJson = Helper.RunCommand(ConsoleMode.CommandPrompt, @"docker container ls --filter ""name = cognito*"" --format=""{{json .}}"""); + containerState = Helper.GetJsonPropertyValue("State", sourceJson!); + cognitoContainerId = Helper.GetJsonPropertyValue("ID", sourceJson!); +} + +//DynamoDB +Console.WriteLine("Waiting for DynamoDB container to be in RUNNING state"); +containerState = string.Empty; +while (!containerState.Equals("running")) { - Thread.Sleep(60000); - sourceJson = Helper.RunCmdCommand(@"docker container ls --filter ""name = cognito*"" --format=""{{json .}}"""); - cognitoDockerState = Helper.GetJsonPropertyValue("State", sourceJson); - cognitoContainerId = Helper.GetJsonPropertyValue("ID", sourceJson); - sourceJson = Helper.RunCmdCommand(@"docker container ls --filter ""name = dynamodb*"" --format=""{{json .}}"""); - dynamoDbDockerState = Helper.GetJsonPropertyValue("State", sourceJson); + sourceJson = Helper.RunCommand(ConsoleMode.CommandPrompt, @"docker container ls --filter ""name = dynamodb*"" --format=""{{json .}}"""); + containerState = Helper.GetJsonPropertyValue("State", sourceJson!); } -Console.WriteLine("Starting local environment configuration..."); +//Once the container is running, we wait 40 seconds for DynamoDB to warm up +Console.WriteLine("DynamoDB is warming up..."); +Thread.Sleep(40000); //DynamoDB -Console.WriteLine("Configuring DynamoDB..."); foreach (var dynamoDbTable in dynamoDbTables) { if (!string.IsNullOrEmpty(dynamoDbTable.TableName)) { Console.WriteLine($"Creating DynamoDB table ({dynamoDbTable.TableName})"); - Helper.RunCmdCommand($"aws --endpoint-url={dynamoDbLocalUrl} dynamodb create-table --table-name {dynamoDbTable.TableName} --attribute-definitions {dynamoDbTable.AttributeDefinitions} --key-schema {dynamoDbTable.KeySchema} --billing-mode PAY_PER_REQUEST"); + Helper.RunCommand(ConsoleMode.CommandPrompt, $"aws --endpoint-url={dynamoDbLocalUrl} dynamodb create-table --table-name {dynamoDbTable.TableName} --attribute-definitions {dynamoDbTable.AttributeDefinitions} --key-schema {dynamoDbTable.KeySchema} --billing-mode PAY_PER_REQUEST --region ap-southeast-2"); Console.WriteLine($"DynamoDB table ({dynamoDbTable.TableName}) has been created."); } } @@ -81,6 +92,8 @@ Directory.CreateDirectory(cognitoLocalDbFolder); File.Copy($@"{Directory.GetCurrentDirectory()}\CognitoLocalDb\clients.json", $@"{cognitoLocalDbFolder}\clients.json", true); File.Copy($@"{Directory.GetCurrentDirectory()}\CognitoLocalDb\user-pool-test.json", $@"{cognitoLocalDbFolder}\user-pool-test.json", true); -Helper.RunCmdCommand($@"docker container restart {cognitoContainerId}"); +Helper.RunCommand(ConsoleMode.CommandPrompt, $@"docker container restart {cognitoContainerId} -t 0"); -Console.WriteLine("Environment configuration is done."); \ No newline at end of file +Console.ForegroundColor = ConsoleColor.Green; +Console.WriteLine("Environment configuration is done."); +Console.ForegroundColor = ConsoleColor.White; \ No newline at end of file From 587713112a8a43d20fcbab1f724d969f21dfcd6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jan 2023 21:41:38 +0000 Subject: [PATCH 56/56] Bump json5 from 2.2.1 to 2.2.3 in /TeamHub/src/TeamHub.Web Bumps [json5](https://github.com/json5/json5) from 2.2.1 to 2.2.3. - [Release notes](https://github.com/json5/json5/releases) - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md) - [Commits](https://github.com/json5/json5/compare/v2.2.1...v2.2.3) --- updated-dependencies: - dependency-name: json5 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- TeamHub/src/TeamHub.Web/package-lock.json | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/TeamHub/src/TeamHub.Web/package-lock.json b/TeamHub/src/TeamHub.Web/package-lock.json index 32eb3e9..50c886d 100644 --- a/TeamHub/src/TeamHub.Web/package-lock.json +++ b/TeamHub/src/TeamHub.Web/package-lock.json @@ -3251,9 +3251,9 @@ } }, "node_modules/babel-loader/node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { "minimist": "^1.2.0" @@ -6976,9 +6976,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { "json5": "lib/cli.js" @@ -14051,9 +14051,9 @@ }, "dependencies": { "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "requires": { "minimist": "^1.2.0" @@ -16766,9 +16766,9 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "jsonc-parser": {