diff --git a/.github/workflows/netMicroservices.yml b/.github/workflows/netMicroservices.yml new file mode 100644 index 0000000..55fcb4d --- /dev/null +++ b/.github/workflows/netMicroservices.yml @@ -0,0 +1,33 @@ +name: .NET SwiftParcel Microservice CI/CD + +on: [push, pull_request] + +jobs: + build-and-test: + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + matrix: + project: [ + 'SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api/', + 'SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api', + 'SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api', + 'SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api', + 'SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator', + 'SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api', + 'SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api', + 'SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api', + 'SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway' + ] + steps: + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '6.0.x' + - name: Build and Test ${{ matrix.project }} + run: | + cd ${{ matrix.project }} + dotnet restore + dotnet build --no-restore + # dotnet test --no-build --if-present --verbosity normal diff --git a/.gitignore b/.gitignore index 53da14b..04edf77 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,16 @@ bld/ [Ll]og/ [Ll]ogs/ +# VSCode files + +*.vscode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot @@ -399,4 +409,7 @@ FodyWeavers.xsd *.msp # JetBrains Rider -*.sln.iml \ No newline at end of file +*.sln.iml + +# Ignore the specific dackend development folder +./SwiftParcel.Web/backend/** diff --git a/SwiftParcel.API.Gateway/scripts/build.sh b/SwiftParcel.API.Gateway/scripts/build.sh new file mode 100755 index 0000000..2966177 --- /dev/null +++ b/SwiftParcel.API.Gateway/scripts/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cd ../src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api +dotnet build -c release diff --git a/SwiftParcel.API.Gateway/scripts/start.sh b/SwiftParcel.API.Gateway/scripts/start.sh new file mode 100755 index 0000000..83d27d7 --- /dev/null +++ b/SwiftParcel.API.Gateway/scripts/start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=Development +cd ../src/SwiftParcel.API.Gateway +dotnet run diff --git a/SwiftParcel.API.Gateway/scripts/test.sh b/SwiftParcel.API.Gateway/scripts/test.sh new file mode 100755 index 0000000..9fc7433 --- /dev/null +++ b/SwiftParcel.API.Gateway/scripts/test.sh @@ -0,0 +1,2 @@ +#!/bin/bash +dotnet test diff --git a/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Infrastructure/CorrelationContext.cs b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Infrastructure/CorrelationContext.cs new file mode 100644 index 0000000..09fdcac --- /dev/null +++ b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Infrastructure/CorrelationContext.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.API.Gateway.Infrastructure +{ + internal class CorrelationContext + { + public string CorrelationId { get; set; } + public string SpanContext { get; set; } + public UserContext User { get; set; } + public string ResourceId { get; set; } + public string TraceId { get; set; } + public string ConnectionId { get; set; } + public string Name { get; set; } + public DateTime CreatedAt { get; set; } + + public class UserContext + { + public string Id { get; set; } + public bool IsAuthenticated { get; set; } + public string Role { get; set; } + public IDictionary Claims { get; set; } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Infrastructure/CorrelationContextBuilder.cs b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Infrastructure/CorrelationContextBuilder.cs new file mode 100644 index 0000000..fb175cc --- /dev/null +++ b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Infrastructure/CorrelationContextBuilder.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Ntrada; +using Ntrada.Extensions.RabbitMq; +using OpenTracing; + +namespace SwiftParcel.API.Gateway.Infrastructure +{ + internal sealed class CorrelationContextBuilder : IContextBuilder + { + private readonly IServiceProvider _serviceProvider; + + public CorrelationContextBuilder(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public object Build(ExecutionData executionData) + { + var tracer = _serviceProvider.GetService(); + var spanContext = tracer is null ? string.Empty : + tracer.ActiveSpan is null ? string.Empty : tracer.ActiveSpan.Context.ToString(); + + var name = string.Empty; + if (executionData.Route.Config is {} && + executionData.Route.Config.TryGetValue("routing_key", out var routingKey)) + { + name = routingKey ?? string.Empty; + } + + if (string.IsNullOrWhiteSpace(name)) + { + name = $"{executionData.Context.Request.Method} {executionData.Context.Request.Path}"; + } + + return new CorrelationContext + { + CorrelationId = executionData.RequestId, + User = new CorrelationContext.UserContext + { + Id = executionData.UserId, + Claims = executionData.Claims, + Role = executionData.Claims.FirstOrDefault(c => c.Key == ClaimTypes.Role).Value, + IsAuthenticated = !string.IsNullOrWhiteSpace(executionData.UserId) + }, + ResourceId = executionData.ResourceId, + TraceId = executionData.TraceId, + ConnectionId = executionData.Context.Connection.Id, + Name = name, + CreatedAt = DateTime.UtcNow, + SpanContext = spanContext + }; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Infrastructure/HttpRequestHook.cs b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Infrastructure/HttpRequestHook.cs new file mode 100644 index 0000000..bbcd89c --- /dev/null +++ b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Infrastructure/HttpRequestHook.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Ntrada; +using Ntrada.Extensions.RabbitMq; +using Ntrada.Hooks; + +namespace SwiftParcel.API.Gateway.Infrastructure +{ + internal sealed class HttpRequestHook : IHttpRequestHook + { + private readonly IContextBuilder _contextBuilder; + + public HttpRequestHook(IContextBuilder contextBuilder) + { + _contextBuilder = contextBuilder; + } + + + public Task InvokeAsync(HttpRequestMessage request, ExecutionData data) + { + var context = JsonConvert.SerializeObject(_contextBuilder.Build(data)); + request.Headers.TryAddWithoutValidation("Correlation-Context", context); + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Infrastructure/SpanContextBuilder.cs b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Infrastructure/SpanContextBuilder.cs new file mode 100644 index 0000000..6f87f97 --- /dev/null +++ b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Infrastructure/SpanContextBuilder.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Ntrada; +using Ntrada.Extensions.RabbitMq; +using OpenTracing; + +namespace SwiftParcel.API.Gateway.Infrastructure +{ + internal sealed class SpanContextBuilder : ISpanContextBuilder + { + private readonly IServiceProvider _serviceProvider; + + public SpanContextBuilder(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public string Build(ExecutionData executionData) + { + var tracer = _serviceProvider.GetService(); + var spanContext = tracer is null ? string.Empty : + tracer.ActiveSpan is null ? string.Empty : tracer.ActiveSpan.Context.ToString(); + + return spanContext; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Program.cs b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Program.cs new file mode 100644 index 0000000..652d244 --- /dev/null +++ b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Program.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey; +using Convey.Logging; +using Convey.Metrics.AppMetrics; +using Convey.Security; +using Ntrada; +using Ntrada.Extensions.RabbitMq; +using Ntrada.Hooks; +using SwiftParcel.API.Gateway.Infrastructure; + +namespace SwiftParcel.API.Gateway +{ + public static class Program + { + public static Task Main(string[] args) + => CreateHostBuilder(args).Build().RunAsync(); + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.ConfigureAppConfiguration(builder => + { + const string extension = "yml"; + var ntradaConfig = Environment.GetEnvironmentVariable("NTRADA_CONFIG"); + var configPath = args?.FirstOrDefault() ?? ntradaConfig ?? $"ntrada.{extension}"; + if (!configPath.EndsWith($".{extension}")) + { + configPath += $".{extension}"; + } + + builder.AddYamlFile(configPath, false); + }) + .ConfigureServices(services => services.AddNtrada() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddConvey() + .AddMetrics() + .AddSecurity()) + .Configure(app => app.UseNtrada()) + .UseLogging(); + }); + } +} \ No newline at end of file diff --git a/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Properties/launchSettings.json b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Properties/launchSettings.json new file mode 100644 index 0000000..1fd68b5 --- /dev/null +++ b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/Properties/launchSettings.json @@ -0,0 +1,28 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:61158", + "sslPort": 44393 + } + }, + "profiles": { + "SwiftParcel.API.Gateway": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7082;http://localhost:5292", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/SwiftParcel.API.Gateway.csproj b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/SwiftParcel.API.Gateway.csproj new file mode 100644 index 0000000..cf04d93 --- /dev/null +++ b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/SwiftParcel.API.Gateway.csproj @@ -0,0 +1,31 @@ + + + + net6.0 + disable + enable + + + + + + + + + + + + + + + + + + + + Always + + + + + diff --git a/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/appsettings.Development.json b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/appsettings.Development.json new file mode 100644 index 0000000..ff66ba6 --- /dev/null +++ b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/appsettings.json b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/appsettings.json new file mode 100644 index 0000000..45342e4 --- /dev/null +++ b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/appsettings.json @@ -0,0 +1,27 @@ +{ + "logger": { + "excludePaths": ["/", "/ping", "/metrics"], + "console": { + "enabled": true + }, + "file": { + "enabled": true, + "path": "logs/logs.txt", + "interval": "day" + }, + "seq": { + "enabled": true, + "url": "http://localhost:5341", + "apiKey": "secret" + } + }, + "metrics": { + "enabled": true, + "influxEnabled": false, + "prometheusEnabled": true, + "influxUrl": "http://localhost:8086", + "database": "swiftparcel", + "env": "local", + "interval": 5 + } +} \ No newline at end of file diff --git a/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/ntrada.yml b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/ntrada.yml new file mode 100644 index 0000000..1111bfa --- /dev/null +++ b/SwiftParcel.API.Gateway/src/SwiftParcel.API.Gateway/ntrada.yml @@ -0,0 +1,458 @@ +auth: + enabled: true + global: false + claims: + role: http://schemas.microsoft.com/ws/2008/06/identity/claims/role + +http: + retries: 2 + interval: 2.0 + exponential: true + +useForwardedHeaders: true +passQueryString: true +forwardRequestHeaders: true +forwardResponseHeaders: true +generateRequestId: true +generateTraceId: true +useLocalUrl: true +loadBalancer: + enabled: false + url: localhost:9999 + +extensions: + customErrors: + includeExceptionMessage: true + + cors: + allowCredentials: true + allowedOrigins: + - 'http://localhost:3001' + allowedMethods: + - GET + - POST + - PUT + - DELETE + - OPTIONS + allowedHeaders: + - '*' + exposedHeaders: + - Request-ID + - Resource-ID + - Trace-ID + - Total-Count + + jwt: + issuerSigningKey: eiquief5phee9pazo0Faegaez9gohThailiur5woy2befiech1oarai4aiLi6ahVecah3ie9Aiz6Peij + validIssuer: swiftparcel + validateAudience: false + validateIssuer: true + validateLifetime: true + + swagger: + name: SwiftParcel + reDocEnabled: false + title: SwiftParcel API + version: v1 + routePrefix: docs + includeSecurity: true + + tracing: + serviceName: api-gateway + udpHost: localhost + udpPort: 6831 + maxPacketSize: 0 + sampler: const + useEmptyTracer: false + +modules: + home: + routes: + - upstream: / + method: GET + use: return_value + returnValue: Welcome to SwiftParcel API. + + + availability: + path: availability + routes: + - upstream: /resources + method: GET + use: downstream + downstream: availability-service/resources + auth: true + + - upstream: /resources/{resourceId} + method: GET + use: downstream + downstream: availability-service/resources/{resourceId} + auth: true + + - upstream: /resources + method: POST + use: downstream + downstream: availability-service/resources + auth: true + + - upstream: /resources/{resourceId}/reservations/{dateTime} + method: POST + use: downstream + downstream: availability-service/resources/{resourceId}/reservations/{dateTime} + auth: true + bind: + - resourceId:{resourceId} + - customerId:@user_id + - dateTime:{dateTime} + + - upstream: /resources/{resourceId}/reservations/{dateTime} + method: DELETE + use: downstream + downstream: availability-service/resources/{resourceId}/reservations/{dateTime} + auth: true + bind: + - resourceId:{resourceId} + - dateTime:{dateTime} + + - upstream: /resources/{resourceId} + method: DELETE + use: downstream + downstream: availability-service/resources/{resourceId} + auth: true + bind: + - resourceId:{resourceId} + + services: + availability-service: + localUrl: localhost:5001 + url: availability-service + + + identity: + path: identity + routes: + - upstream: /users/{userId} + method: GET + use: downstream + downstream: identity-service/users/{userId} + auth: true + claims: + role: admin + + - upstream: /me + method: GET + use: downstream + downstream: identity-service/me + auth: true + + - upstream: /sign-up + method: POST + use: downstream + downstream: identity-service/sign-up + auth: false + resourceId: + property: userId + generate: true + + - upstream: /sign-in + method: POST + use: downstream + downstream: identity-service/sign-in + auth: false + responseHeaders: + content-type: application/json + + services: + identity-service: + localUrl: localhost:5004 + url: identity-service + + orders: + path: orders + routes: + - upstream: / + method: GET + use: downstream + downstream: orders-service/orders?customerId=@user_id + auth: true + + - upstream: /{orderId} + method: GET + use: downstream + downstream: orders-service/orders/{orderId} + auth: true + + - upstream: / + method: POST + use: downstream + downstream: orders-service/orders + auth: true + resourceId: + property: orderId + generate: true + bind: + - customerId:@user_id + + - upstream: /{orderId} + method: DELETE + use: downstream + downstream: orders-service/orders/{orderId} + auth: true + + - upstream: /{orderId}/parcels/{parcelId} + method: POST + use: downstream + downstream: orders-service/orders/{orderId}/parcels/{parcelId} + auth: true + bind: + - customerId:@user_id + - orderId:{orderId} + - parcelId:{parcelId} + + - upstream: /{orderId}/parcels/{parcelId} + method: DELETE + use: downstream + downstream: orders-service/orders/{orderId}/parcels/{parcelId} + auth: true + + - upstream: /{orderId}/vehicles/{vehicleId} + method: POST + use: downstream + downstream: orders-service/orders/{orderId}/couriers/{courierId} + auth: true + bind: + - orderId:{orderId} + - vehicleId:{courierId} + - customerId:@user_id + + - upstream: /{orderId}/approve + method: PUT + use: downstream + downstream: orders-service/orders/{orderId}/approve + auth: true + bind: + - orderId:{orderId} + + - upstream: /{orderId}/cancel + method: PUT + use: downstream + downstream: orders-service/orders/{orderId}/cancel + auth: true + bind: + - orderId:{orderId} + + services: + orders-service: + localUrl: localhost:5006 + url: orders-service + + customers: + path: customers + routes: + - upstream: / + method: GET + use: downstream + downstream: customers-service/customers + auth: true + claims: + role: admin + + - upstream: /me + method: GET + use: downstream + downstream: customers-service/customers/@user_id + auth: true + + - upstream: /{customerId} + method: GET + use: downstream + downstream: customers-service/customers/{customerId} + auth: true + claims: + role: admin + + - upstream: /{customerId}/state + method: GET + use: downstream + downstream: customers-service/customers/{customerId}/state + auth: true + claims: + role: admin + + - upstream: / + method: POST + use: downstream + downstream: customers-service/customers + bind: + - customerId:@user_id + auth: true + payload: create_customer + schema: create_customer.schema + + - upstream: /{customerId}/state/{state} + method: PUT + use: downstream + downstream: customers-service/customers/{customerId}/state/{state} + bind: + - customerId:{customerId} + - state:{state} + auth: true + claims: + role: admin + + services: + customers-service: + localUrl: localhost:5002 + url: customers-service + + + + parcels: + path: parcels + routes: + - upstream: / + method: GET + use: downstream + downstream: parcels-service/parcels?customerId=@user_id + auth: true + + - upstream: /{parcelId} + method: GET + use: downstream + downstream: parcels-service/parcels/{parcelId} + auth: true + + - upstream: /volume + method: GET + use: downstream + downstream: parcels-service/parcels/volume + auth: true + + - upstream: / + method: POST + use: downstream + downstream: parcels-service/parcels + auth: true + resourceId: + property: parcelId + generate: true + bind: + - customerId:@user_id + + - upstream: /{parcelId} + method: DELETE + use: downstream + downstream: parcels-service/parcels/{parcelId} + auth: true + + services: + parcels-service: + localUrl: localhost:5007 + url: parcels-service + + + pricing: + path: pricing + routes: + - upstream: / + method: GET + use: downstream + downstream: pricing-service/pricing?customerId=@user_id + auth: true + + services: + pricing-service: + localUrl: localhost:5008 + url: pricing-service + + + couriers: + path: /couriers + routes: + - upstream: / + method: GET + use: downstream + downstream: couriers-service/couriers + auth: true + onSuccess: + data: response.data.items + + - upstream: /{courierId} + method: GET + use: downstream + downstream: courier-service/couriers/{courierId} + auth: true + + - upstream: / + method: POST + use: downstream + downstream: couriers-service/couriers + auth: true + resourceId: + property: courierId + generate: true + + - upstream: /{courierId} + method: PUT + use: downstream + downstream: couriers-service/couriers/{courierId} + auth: true + + - upstream: /{courierId} + method: DELETE + use: downstream + downstream: couriers-service/couriers/{courierId} + auth: true + + services: + couriers-service: + localUrl: localhost:5009 + url: couriers-service + + deliveries: + path: deliveries + routes: + - upstream: /{deliveryId} + method: GET + use: downstream + downstream: deliveries-service/deliveries/{deliveryId} + auth: true + + - upstream: / + method: POST + use: downstream + downstream: deliveries-service/deliveries + auth: true + resourceId: + property: deliveryId + generate: true + + - upstream: /{deliveryId}/fail + method: POST + use: downstream + downstream: deliveries-service/deliveries/{deliveryId}/fail + auth: true + bind: + - deliveryId:{deliveryId} + + - upstream: /{deliveryId}/complete + method: POST + use: downstream + downstream: deliveries-service/deliveries/{deliveryId}/complete + auth: true + bind: + - deliveryId:{deliveryId} + + - upstream: /{deliveryId}/registrations + method: POST + use: downstream + downstream: deliveries-service/deliveries/{deliveryId}/registrations + auth: true + bind: + - deliveryId:{deliveryId} + + services: + deliveries-service: + localUrl: localhost:5003 + url: deliveries-service + diff --git a/SwiftParcel.Services.Availability/Dockerfile b/SwiftParcel.Services.Availability/Dockerfile new file mode 100644 index 0000000..86c04ac --- /dev/null +++ b/SwiftParcel.Services.Availability/Dockerfile @@ -0,0 +1,13 @@ +# Use the .NET 6.0 SDK image to build the project +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build +WORKDIR /app +COPY . . +RUN dotnet publish src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api -c Release -o out + +# Use the .NET 6.0 ASP.NET Core runtime image to run the application +FROM mcr.microsoft.com/dotnet/aspnet:6.0 +WORKDIR /app +COPY --from=build /app/out . +ENV ASPNETCORE_URLS=http://*:80 +ENV ASPNETCORE_ENVIRONMENT=docker +ENTRYPOINT ["dotnet", "SwiftParcel.Services.Availability.Api.dll"] diff --git a/SwiftParcel.Services.Availability/SwiftParcel.Services.Availability.sln b/SwiftParcel.Services.Availability/SwiftParcel.Services.Availability.sln new file mode 100644 index 0000000..149a0bc --- /dev/null +++ b/SwiftParcel.Services.Availability/SwiftParcel.Services.Availability.sln @@ -0,0 +1,60 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C44E5479-0C41-40F0-84BA-E5A06D4BC533}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Availability.Api", "SwiftParcel.Services.Availability.Api", "{B4AE571C-917B-4907-B0D1-8324BA9FBFA1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Availability.Api", "src\SwiftParcel.Services.Availability.Api\SwiftParcel.Services.Availability.Api\SwiftParcel.Services.Availability.Api.csproj", "{BA27F09B-07CE-4122-9A44-972CA9181680}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Availability.Application", "SwiftParcel.Services.Availability.Application", "{19442C8B-BB45-4E14-A889-87946B75B8D4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Availability.Application", "src\SwiftParcel.Services.Availability.Application\SwiftParcel.Services.Availability.Application\SwiftParcel.Services.Availability.Application.csproj", "{7B7B650E-4AAC-4D47-B3BB-E0673E5DAAD5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Availability.Core", "SwiftParcel.Services.Availability.Core", "{17839C45-ED2C-495B-AF68-1FA1563720E0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Availability.Core", "src\SwiftParcel.Services.Availability.Core\SwiftParcel.Services.Availability.Core\SwiftParcel.Services.Availability.Core.csproj", "{A83AF713-B7EA-4A82-9DAC-B8FBA9296677}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Availability.Infrastructure", "SwiftParcel.Services.Availability.Infrastructure", "{CFC539F6-7296-47EE-BF03-9D1FF036FC2D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Availability.Infrastructure", "src\SwiftParcel.Services.Availability.Infrastructure\SwiftParcel.Services.Availability.Infrastructure\SwiftParcel.Services.Availability.Infrastructure.csproj", "{1BCADEBD-CC4C-4780-979A-07A42DACD7F3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BA27F09B-07CE-4122-9A44-972CA9181680}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA27F09B-07CE-4122-9A44-972CA9181680}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA27F09B-07CE-4122-9A44-972CA9181680}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA27F09B-07CE-4122-9A44-972CA9181680}.Release|Any CPU.Build.0 = Release|Any CPU + {7B7B650E-4AAC-4D47-B3BB-E0673E5DAAD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7B7B650E-4AAC-4D47-B3BB-E0673E5DAAD5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B7B650E-4AAC-4D47-B3BB-E0673E5DAAD5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7B7B650E-4AAC-4D47-B3BB-E0673E5DAAD5}.Release|Any CPU.Build.0 = Release|Any CPU + {A83AF713-B7EA-4A82-9DAC-B8FBA9296677}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A83AF713-B7EA-4A82-9DAC-B8FBA9296677}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A83AF713-B7EA-4A82-9DAC-B8FBA9296677}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A83AF713-B7EA-4A82-9DAC-B8FBA9296677}.Release|Any CPU.Build.0 = Release|Any CPU + {1BCADEBD-CC4C-4780-979A-07A42DACD7F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1BCADEBD-CC4C-4780-979A-07A42DACD7F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1BCADEBD-CC4C-4780-979A-07A42DACD7F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1BCADEBD-CC4C-4780-979A-07A42DACD7F3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {B4AE571C-917B-4907-B0D1-8324BA9FBFA1} = {C44E5479-0C41-40F0-84BA-E5A06D4BC533} + {BA27F09B-07CE-4122-9A44-972CA9181680} = {B4AE571C-917B-4907-B0D1-8324BA9FBFA1} + {19442C8B-BB45-4E14-A889-87946B75B8D4} = {C44E5479-0C41-40F0-84BA-E5A06D4BC533} + {7B7B650E-4AAC-4D47-B3BB-E0673E5DAAD5} = {19442C8B-BB45-4E14-A889-87946B75B8D4} + {17839C45-ED2C-495B-AF68-1FA1563720E0} = {C44E5479-0C41-40F0-84BA-E5A06D4BC533} + {A83AF713-B7EA-4A82-9DAC-B8FBA9296677} = {17839C45-ED2C-495B-AF68-1FA1563720E0} + {CFC539F6-7296-47EE-BF03-9D1FF036FC2D} = {C44E5479-0C41-40F0-84BA-E5A06D4BC533} + {1BCADEBD-CC4C-4780-979A-07A42DACD7F3} = {CFC539F6-7296-47EE-BF03-9D1FF036FC2D} + EndGlobalSection +EndGlobal diff --git a/SwiftParcel.Services.Availability/scripts/build.sh b/SwiftParcel.Services.Availability/scripts/build.sh new file mode 100755 index 0000000..0542588 --- /dev/null +++ b/SwiftParcel.Services.Availability/scripts/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=Development +cd src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api +dotnet build -c release \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/scripts/start.sh b/SwiftParcel.Services.Availability/scripts/start.sh new file mode 100755 index 0000000..590ce66 --- /dev/null +++ b/SwiftParcel.Services.Availability/scripts/start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=Development +cd ../src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api +dotnet run \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/scripts/test.sh b/SwiftParcel.Services.Availability/scripts/test.sh new file mode 100644 index 0000000..0eb2bcf --- /dev/null +++ b/SwiftParcel.Services.Availability/scripts/test.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=Development +cd src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api +dotnet test \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api/Program.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api/Program.cs new file mode 100644 index 0000000..fbe60f7 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api/Program.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey; +using Convey.Secrets.Vault; +using Convey.Logging; +using Convey.Types; +using Convey.WebApi; +using Convey.WebApi.CQRS; +using Microsoft.AspNetCore; +using Microsoft.Extensions.DependencyInjection; +using SwiftParcel.Services.Availability.Application; +using SwiftParcel.Services.Availability.Application.Commands; +using SwiftParcel.Services.Availability.Application.DTO; +using SwiftParcel.Services.Availability.Application.Qeries; +using SwiftParcel.Services.Availability.Infrastructure; + + + +namespace src.SwiftParcel.Services.Availability.Api +{ + public class Program + { + public static async Task Main(string[] args) + => await CreateWebHostBuilder(args) + .Build() + .RunAsync(); + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) + => WebHost.CreateDefaultBuilder(args) + .ConfigureServices(services => services + .AddConvey() + .AddWebApi() + .AddApplication() + .AddInfrastructure() + .Build()) + .Configure(app => app + .UseInfrastructure() + .UseDispatcherEndpoints(endpoints => endpoints + .Get("", ctx => ctx.Response.WriteAsync(ctx.RequestServices.GetService().Name)) + .Get>("resources") + .Get("resources/{resourceId}") + .Post("resources", + afterDispatch: (cmd, ctx) => ctx.Response.Created($"resources/{cmd.ResourceId}")) + .Post("resources/{resourceId}/reservations/{dateTime}") + .Delete("resources/{resourceId}/reservations/{dateTime}") + .Delete("resources/{resourceId}"))) + .UseLogging() + .UseVault(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api/Properties/launchSettings.json b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api/Properties/launchSettings.json new file mode 100644 index 0000000..fa51b65 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:6002", + "sslPort": 44381 + } + }, + "profiles": { + "SwiftParcel.Services.Availability.Api": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5001", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": false, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api.csproj b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api.csproj new file mode 100644 index 0000000..e42217d --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + disable + enable + + + + + + + + + + + + + + + diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api/appsettings.Development.json b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api/appsettings.Development.json new file mode 100644 index 0000000..ff66ba6 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api/appsettings.json b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api/appsettings.json new file mode 100644 index 0000000..53e051f --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api/appsettings.json @@ -0,0 +1,202 @@ +{ + "app": { + "name": "SwiftParcel Availability Service", + "service": "availability-service", + "version": "1" + }, + "consul": { + "enabled": true, + "url": "http://localhost:8500", + "service": "availability-service", + "address": "docker.for.win.localhost", + "port": "5001", + "pingEnabled": false, + "pingEndpoint": "ping", + "pingInterval": 3, + "removeAfterInterval": 3 + }, + "fabio": { + "enabled": true, + "url": "http://localhost:9999", + "service": "availability-service" + }, + "httpClient": { + "type": "fabio", + "retries": 3, + "services": { + "customers": "customers-service" + }, + "requestMasking": { + "enabled": true, + "maskTemplate": "*****" + } + }, + "logger": { + "level": "information", + "excludePaths": ["/", "/ping", "/metrics"], + "excludeProperties": [ + "api_key", + "access_key", + "ApiKey", + "ApiSecret", + "ClientId", + "ClientSecret", + "ConnectionString", + "Password", + "Email", + "Login", + "Secret", + "Token" + ], + "console": { + "enabled": true + }, + "elk": { + "enabled": false, + "url": "http://localhost:9200" + }, + "file": { + "enabled": true, + "path": "logs/logs.txt", + "interval": "day" + }, + "seq": { + "enabled": true, + "url": "http://localhost:5341", + "apiKey": "secret" + }, + "tags": {} + }, + "jaeger": { + "enabled": true, + "serviceName": "availability", + "udpHost": "localhost", + "udpPort": 6831, + "maxPacketSize": 0, + "sampler": "const", + "excludePaths": ["/", "/ping", "/metrics"] + }, + "jwt": { + "certificate": { + "location": "certs/localhost.cer" + }, + "validIssuer": "swiftparcel", + "validateAudience": false, + "validateIssuer": true, + "validateLifetime": true + }, + "metrics": { + "enabled": true, + "influxEnabled": false, + "prometheusEnabled": true, + "influxUrl": "http://localhost:8086", + "database": "swiftparcel", + "env": "local", + "interval": 5 + }, + "mongo": { + "connectionString": "mongodb+srv://andrii-courier-db-user:piotr-amadeusz-andrii-2023@cluster0.br51nsv.mongodb.net/?retryWrites=true&w=majority", + "database": "availability-service", + "seed": false + }, + "outbox": { + "enabled": true, + "type": "sequential", + "expiry": 3600, + "intervalMilliseconds": 2000, + "inboxCollection": "inbox", + "outboxCollection": "outbox", + "disableTransactions": true + }, + "rabbitMq": { + "connectionName": "availability-service", + "retries": 3, + "retryInterval": 2, + "conventionsCasing": "snakeCase", + "logger": { + "enabled": true + }, + "username": "guest", + "password": "guest", + "virtualHost": "/", + "port": 5672, + "hostnames": [ + "localhost" + ], + "requestedConnectionTimeout": "00:00:30", + "requestedHeartbeat": "00:01:00", + "socketReadTimeout": "00:00:30", + "socketWriteTimeout": "00:00:30", + "continuationTimeout": "00:00:20", + "handshakeContinuationTimeout": "00:00:10", + "networkRecoveryInterval": "00:00:05", + "exchange": { + "declare": true, + "durable": true, + "autoDelete": false, + "type": "topic", + "name": "availability" + }, + "queue": { + "declare": true, + "durable": true, + "exclusive": false, + "autoDelete": false, + "template": "availability-service/{{exchange}}.{{message}}" + }, + "context": { + "enabled": true, + "header": "message_context" + }, + "spanContextHeader": "span_context" + }, + "redis": { + "connectionString": "localhost", + "instance": "availability:" + }, + "swagger": { + "enabled": true, + "reDocEnabled": false, + "name": "v1", + "title": "API", + "version": "v1", + "routePrefix": "docs", + "includeSecurity": true + }, + "security": { + "certificate": { + "header": "Certificate" + } + }, + "vault": { + "enabled": false, + "url": "http://127.0.0.1:8200", + "authType": "userpass", + "token": "secret", + "username": "user", + "password": "piotr-amadeusz-andrii-2023", + "dbusername": "andrii-courier-db-user", + "kv": { + "enabled": true, + "engineVersion": 2, + "mountPoint": "secret", + "path": "identity-service/settings" + }, + "pki": { + "enabled": false, + "roleName": "identity-service", + "commonName": "identity-service.swiftparcel.com" + }, + "lease": { + "mongo": { + "type": "database", + "roleName": "identity-service", + "enabled": true, + "autoRenewal": true, + "templates": { + "connectionString": "mongodb+srv://andrii-courier-db-user:piotr-amadeusz-andrii-2023@cluster0.br51nsv.mongodb.net/?retryWrites=true&w=majority" + } + } + } + } +} diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/AddResource.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/AddResource.cs new file mode 100644 index 0000000..40e66c2 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/AddResource.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.Availability.Application.Commands +{ + public class AddResource : ICommand + { + public Guid ResourceId { get; } + public IEnumerable Tags { get; } + + public AddResource(Guid resourceId, IEnumerable tags) + => (ResourceId, Tags) = (resourceId == Guid.Empty ? Guid.NewGuid() : resourceId, + tags ?? Enumerable.Empty()); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/DeleteResource.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/DeleteResource.cs new file mode 100644 index 0000000..ac2d7ea --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/DeleteResource.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.Availability.Application.Commands +{ + public class DeleteResource: ICommand + { + public Guid ResourceId { get; } + + public DeleteResource(Guid resourceId) + => ResourceId = resourceId; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/Handlers/AddResourceHandler.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/Handlers/AddResourceHandler.cs new file mode 100644 index 0000000..aa18c2b --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/Handlers/AddResourceHandler.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using SwiftParcel.Services.Availability.Application.Exceptions; +using SwiftParcel.Services.Availability.Application.Services; +using SwiftParcel.Services.Availability.Core.Entities; +using SwiftParcel.Services.Availability.Core.Repositories; + +namespace SwiftParcel.Services.Availability.Application.Commands.Handlers +{ + public class AddResourceHandler : ICommandHandler + { + private readonly IResourcesRepository _repository; + private readonly IEventProcessor _eventProcessor; + + public AddResourceHandler(IResourcesRepository repository, IEventProcessor eventProcessor) + { + _repository = repository; + _eventProcessor = eventProcessor; + } + + public async Task HandleAsync(AddResource command, CancellationToken cancellationToken) + { + if (await _repository.ExistsAsync(command.ResourceId)) + { + throw new ResourceAlreadyExistsException(command.ResourceId); + } + + var resource = Resource.Create(command.ResourceId, command.Tags); + await _repository.AddAsync(resource); + await _eventProcessor.ProcessAsync(resource.Events); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/Handlers/DeleteResourceHandler.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/Handlers/DeleteResourceHandler.cs new file mode 100644 index 0000000..b714794 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/Handlers/DeleteResourceHandler.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using SwiftParcel.Services.Availability.Application.Exceptions; +using SwiftParcel.Services.Availability.Application.Services; +using SwiftParcel.Services.Availability.Core.Repositories; + +namespace SwiftParcel.Services.Availability.Application.Commands.Handlers +{ + public class DeleteResourceHandler: ICommandHandler + { + private readonly IResourcesRepository _repository; + private readonly IEventProcessor _eventProcessor; + + public DeleteResourceHandler(IResourcesRepository repository, IEventProcessor eventProcessor) + { + _repository = repository; + _eventProcessor = eventProcessor; + } + + public async Task HandleAsync(DeleteResource command, CancellationToken cancellationToken) + { + var resource = await _repository.GetAsync(command.ResourceId); + + if (resource is null) + { + throw new ResourceNotFoundException(command.ResourceId); + } + + resource.Delete(); + await _repository.DeleteAsync(resource.Id); + await _eventProcessor.ProcessAsync(resource.Events); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/Handlers/ReleaseResourceReservationHandler.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/Handlers/ReleaseResourceReservationHandler.cs new file mode 100644 index 0000000..d84522a --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/Handlers/ReleaseResourceReservationHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using SwiftParcel.Services.Availability.Application.Exceptions; +using SwiftParcel.Services.Availability.Application.Services; +using SwiftParcel.Services.Availability.Core.Repositories; + +namespace SwiftParcel.Services.Availability.Application.Commands.Handlers +{ + public class ReleaseResourceReservationHandler : ICommandHandler + { + private readonly IResourcesRepository _repository; + private readonly IEventProcessor _eventProcessor; + + public ReleaseResourceReservationHandler(IResourcesRepository repository, IEventProcessor eventProcessor) + { + _repository = repository; + _eventProcessor = eventProcessor; + } + + public async Task HandleAsync(ReleaseResourceReservation command, CancellationToken cancellationToken) + { + var resource = await _repository.GetAsync(command.ResourceId); + + if (resource is null) + { + throw new ResourceNotFoundException(command.ResourceId); + } + + var reservation = resource.Reservations.FirstOrDefault(r => r.DateTime.Date == command.DateTime.Date); + resource.ReleaseReservation(reservation); + await _repository.UpdateAsync(resource); + await _eventProcessor.ProcessAsync(resource.Events); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/Handlers/ReserveResourceHandler.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/Handlers/ReserveResourceHandler.cs new file mode 100644 index 0000000..9593415 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/Handlers/ReserveResourceHandler.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using SwiftParcel.Services.Availability.Application.Exceptions; +using SwiftParcel.Services.Availability.Application.Services; +using SwiftParcel.Services.Availability.Application.Services.Clients; +using SwiftParcel.Services.Availability.Core.Repositories; + +namespace SwiftParcel.Services.Availability.Application.Commands.Handlers +{ + public class ReserveResourceHandler : ICommandHandler + { + private readonly IResourcesRepository _repository; + private readonly ICustomersServiceClient _customersServiceClient; + private readonly IEventProcessor _eventProcessor; + private readonly IAppContext _appContext; + + public ReserveResourceHandler(IResourcesRepository repository, ICustomersServiceClient customersServiceClient, + IEventProcessor eventProcessor, IAppContext appContext) + { + _repository = repository; + _customersServiceClient = customersServiceClient; + _eventProcessor = eventProcessor; + _appContext = appContext; + } + + public async Task HandleAsync(ReserveResource command, CancellationToken cancellationToken) + { + var identity = _appContext.Identity; + if (identity.IsAuthenticated && identity.Id != command.CustomerId && !identity.IsAdmin) + { + throw new UnauthorizedResourceAccessException(command.ResourceId, identity.Id); + } + + var resource = await _repository.GetAsync(command.ResourceId); + if (resource is null) + { + throw new ResourceNotFoundException(command.ResourceId); + } + + var customerState = await _customersServiceClient.GetStateAsync(command.CustomerId); + if (customerState is null) + { + throw new CustomerNotFoundException(command.CustomerId); + } + + if (!customerState.IsValid) + { + throw new InvalidCustomerStateException(command.ResourceId, customerState?.State); + } + + var reservation = new Core.ValueObjects.Reservation(command.DateTime, command.Priority); + resource.AddReservation(reservation); + await _repository.UpdateAsync(resource); + await _eventProcessor.ProcessAsync(resource.Events); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/ReleaseResourceReservation.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/ReleaseResourceReservation.cs new file mode 100644 index 0000000..6ec1157 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/ReleaseResourceReservation.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.Availability.Application.Commands +{ + public class ReleaseResourceReservation: ICommand + { + public Guid ResourceId { get; } + public DateTime DateTime { get; } + + public ReleaseResourceReservation(Guid resourceId, DateTime dateTime) + => (ResourceId, DateTime) = (resourceId, dateTime); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/ReserveResource.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/ReserveResource.cs new file mode 100644 index 0000000..a637b17 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Commands/ReserveResource.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.Availability.Application.Commands +{ + public class ReserveResource : ICommand + { + public Guid ResourceId { get; } + public DateTime DateTime { get; } + public int Priority { get; } + public Guid CustomerId { get; } + + public ReserveResource(Guid resourceId, DateTime dateTime, int priority, Guid customerId) + => (ResourceId, DateTime, Priority, CustomerId) = (resourceId, dateTime, priority, customerId); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/ContractAttribute.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/ContractAttribute.cs new file mode 100644 index 0000000..05819a3 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/ContractAttribute.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Application +{ + public class ContractAttribute : Attribute + { + + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/DTO/CustomerStateDto.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/DTO/CustomerStateDto.cs new file mode 100644 index 0000000..0818044 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/DTO/CustomerStateDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Application.DTO +{ + public class CustomerStateDto + { + public string State { get; set; } + public bool IsValid => State.Equals("valid", StringComparison.InvariantCultureIgnoreCase); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/DTO/ReservationDto.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/DTO/ReservationDto.cs new file mode 100644 index 0000000..e4447ca --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/DTO/ReservationDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Application.DTO +{ + public class ReservationDto + { + public DateTime DateTime { get; set; } + public int Priority { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/DTO/ResourceDto.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/DTO/ResourceDto.cs new file mode 100644 index 0000000..ee34012 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/DTO/ResourceDto.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Application.DTO +{ + public class ResourceDto + { + public Guid Id { get; set; } + public IEnumerable Tags { get; set; } + public IEnumerable Reservations { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/External/CourierDeleted.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/External/CourierDeleted.cs new file mode 100644 index 0000000..c9db53e --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/External/CourierDeleted.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; + +namespace SwiftParcel.Services.Availability.Application.Events.External +{ + [Message("couriers")] + public class CourierDeleted : IEvent + { + public Guid CourierId { get; } + + public CourierDeleted(Guid courierId) + => CourierId = courierId; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/External/CustomerCreated.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/External/CustomerCreated.cs new file mode 100644 index 0000000..bb1ae03 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/External/CustomerCreated.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; + +namespace SwiftParcel.Services.Availability.Application.Events.External +{ + [Message("customers")] + public class CustomerCreated : IEvent + { + public Guid CustomerId { get; } + + public CustomerCreated(Guid customerId) + => CustomerId = customerId; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/External/Handlers/CourierDeleatedHandler.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/External/Handlers/CourierDeleatedHandler.cs new file mode 100644 index 0000000..e173d48 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/External/Handlers/CourierDeleatedHandler.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using Convey.CQRS.Events; +using SwiftParcel.Services.Availability.Application.Commands; + +namespace SwiftParcel.Services.Availability.Application.Events.External.Handlers +{ + public class CourierDeletedHandler : IEventHandler + { + private readonly ICommandDispatcher _dispatcher; + + public CourierDeletedHandler(ICommandDispatcher dispatcher) + { + _dispatcher = dispatcher; + } + + public Task HandleAsync(CourierDeleted @event, CancellationToken cancellationToken) => _dispatcher.SendAsync(new DeleteResource(@event.CourierId)); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/External/Handlers/CustomerCreatedHandler.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/External/Handlers/CustomerCreatedHandler.cs new file mode 100644 index 0000000..f1e402c --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/External/Handlers/CustomerCreatedHandler.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Availability.Application.Events.External.Handlers +{ + public class CustomerCreatedHandler : IEventHandler + { + // Customer data could be saved into custom DB depending on the business requirements. + // Given the asynchronous nature of events, this would result in eventual consistency. + public Task HandleAsync(CustomerCreated @event, CancellationToken cancellationToken) + => Task.CompletedTask; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/IDomainEventHandler.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/IDomainEventHandler.cs new file mode 100644 index 0000000..98aabea --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/IDomainEventHandler.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Availability.Core.Events; + +namespace SwiftParcel.Services.Availability.Application.Events +{ + public interface IDomainEventHandler where T : class, IDomainEvent + { + Task HandleAsync(T @event); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/Rejected/AddResourceRejected.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/Rejected/AddResourceRejected.cs new file mode 100644 index 0000000..93b7a23 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/Rejected/AddResourceRejected.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Availability.Application.Events.Rejected +{ + public class AddResourceRejected : IRejectedEvent + { + public Guid ResourceId { get; } + public string Reason { get; } + public string Code { get; } + + public AddResourceRejected(Guid resourceId, string reason, string code) + => (ResourceId, Reason, Code) = (resourceId, reason, code); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/Rejected/DeleteResourceRejected.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/Rejected/DeleteResourceRejected.cs new file mode 100644 index 0000000..988e445 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/Rejected/DeleteResourceRejected.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Availability.Application.Events.Rejected +{ + public class DeleteResourceRejected : IRejectedEvent + { + public Guid ResourceId { get; } + public string Reason { get; } + public string Code { get; } + + public DeleteResourceRejected(Guid resourceId, string reason, string code) + => (ResourceId, Reason, Code) = (resourceId, reason, code); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/Rejected/ReleaseResourceRejected.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/Rejected/ReleaseResourceRejected.cs new file mode 100644 index 0000000..32922e3 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/Rejected/ReleaseResourceRejected.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Availability.Application.Events.Rejected +{ + public class ReleaseResourceRejected : IRejectedEvent + { + public Guid ResourceId { get; } + public DateTime DateTime { get; } + public string Reason { get; } + public string Code { get; } + + public ReleaseResourceRejected(Guid resourceId, DateTime dateTime, string reason, string code) + => (ResourceId, DateTime, Reason, Code) = (resourceId, dateTime, reason, code); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/Rejected/ReleaseResourceReservationRejected.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/Rejected/ReleaseResourceReservationRejected.cs new file mode 100644 index 0000000..52b8106 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/Rejected/ReleaseResourceReservationRejected.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Availability.Application.Events.Rejected +{ + public class ReleaseResourceReservationRejected : IRejectedEvent + { + public Guid ResourceId { get; } + public DateTime DateTime { get; } + public string Reason { get; } + public string Code { get; } + + public ReleaseResourceReservationRejected(Guid resourceId, DateTime dateTime, string reason, string code) + => (ResourceId, DateTime, Reason, Code) = (resourceId, dateTime, reason, code); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/ResourceAdded.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/ResourceAdded.cs new file mode 100644 index 0000000..fa04771 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/ResourceAdded.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Availability.Application.Events +{ + public class ResourceAdded : IEvent + { + public Guid ResourceId { get; } + + public ResourceAdded(Guid resourceId) + => ResourceId = resourceId; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/ResourceDeleted.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/ResourceDeleted.cs new file mode 100644 index 0000000..ed41750 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/ResourceDeleted.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Availability.Application.Events +{ + public class ResourceDeleted : IEvent + { + public Guid ResourceId { get; } + + public ResourceDeleted(Guid resourceId) + => ResourceId = resourceId; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/ResourceReservationCanceled.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/ResourceReservationCanceled.cs new file mode 100644 index 0000000..0526b19 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/ResourceReservationCanceled.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Availability.Application.Events +{ + public class ResourceReservationCanceled : IEvent + { + public Guid ResourceId { get; } + public DateTime DateTime { get; } + + public ResourceReservationCanceled(Guid resourceId, DateTime dateTime) + => (ResourceId, DateTime) = (resourceId, dateTime); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/ResourceReservationReleased.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/ResourceReservationReleased.cs new file mode 100644 index 0000000..c13dbaa --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/ResourceReservationReleased.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Availability.Application.Events +{ + public class ResourceReservationReleased : IEvent + { + public Guid ResourceId { get; } + public DateTime DateTime { get; } + + public ResourceReservationReleased(Guid resourceId, DateTime dateTime) + => (ResourceId, DateTime) = (resourceId, dateTime); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/ResourceReserved.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/ResourceReserved.cs new file mode 100644 index 0000000..6d78f2c --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Events/ResourceReserved.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Availability.Application.Events +{ + public class ResourceReserved : IEvent + { + public Guid ResourceId { get; } + public DateTime DateTime { get; } + + public ResourceReserved(Guid resourceId, DateTime dateTime) + => (ResourceId, DateTime) = (resourceId, dateTime); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/AppException.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/AppException.cs new file mode 100644 index 0000000..1aa80e0 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/AppException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Application.Exceptions +{ + public class AppException : Exception + { + public virtual string Code { get; } + + protected AppException(string message) : base(message) + { + + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/CustomerNotFoundException.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/CustomerNotFoundException.cs new file mode 100644 index 0000000..b5aaafc --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/CustomerNotFoundException.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Application.Exceptions +{ + public class CustomerNotFoundException : AppException + { + public override string Code { get; } = "customer_not_found"; + public Guid Id { get; } + + public CustomerNotFoundException(Guid id) : base($"Customer with id: {id} was not found.") + => Id = id; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/InvalidCustomerStateException.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/InvalidCustomerStateException.cs new file mode 100644 index 0000000..24e645e --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/InvalidCustomerStateException.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Application.Exceptions +{ + public class InvalidCustomerStateException : AppException + { + public override string Code { get; } = "invalid_customer_state"; + public Guid Id { get; } + public string State { get; } + + public InvalidCustomerStateException(Guid id, string state) + : base($"Customer with id: {id} has invalid state: {state}.") + => (Id, State) = (id, state); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/ResourceAlreadyExistsException.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/ResourceAlreadyExistsException.cs new file mode 100644 index 0000000..8632022 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/ResourceAlreadyExistsException.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Application.Exceptions +{ + public class ResourceAlreadyExistsException : AppException + { + public override string Code { get; } = "resource_already_exists"; + public Guid Id { get; } + + public ResourceAlreadyExistsException(Guid id) : base($"Resource with id: {id} already exists.") + => Id = id; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/ResourceNotFoundException.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/ResourceNotFoundException.cs new file mode 100644 index 0000000..4459bc0 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/ResourceNotFoundException.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Application.Exceptions +{ + public class ResourceNotFoundException : AppException + { + public override string Code { get; } = "resource_not_found"; + public Guid Id { get; } + + public ResourceNotFoundException(Guid id) : base($"Resource with id: {id} was not found.") + => Id = id; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/UnauthorizedResourceAccessException.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/UnauthorizedResourceAccessException.cs new file mode 100644 index 0000000..c3aceb0 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Exceptions/UnauthorizedResourceAccessException.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Application.Exceptions +{ + public class UnauthorizedResourceAccessException : AppException + { + public override string Code { get; } = "unauthorized_resource_access"; + public Guid ResourceId { get; } + public Guid CustomerId { get; } + + public UnauthorizedResourceAccessException(Guid resourceId, Guid customerId) + : base($"Unauthorized access to resource: '{resourceId}' by customer: '{customerId}'") + => (ResourceId, CustomerId) = (resourceId, customerId); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Extensions.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Extensions.cs new file mode 100644 index 0000000..750ae83 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Extensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey; +using Convey.CQRS.Commands; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Availability.Application +{ + public static class Extensions + { + public static IConveyBuilder AddApplication(this IConveyBuilder builder) + => builder + .AddCommandHandlers() + .AddEventHandlers() + .AddInMemoryCommandDispatcher() + .AddInMemoryEventDispatcher(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/IAppContext.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/IAppContext.cs new file mode 100644 index 0000000..24cc14e --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/IAppContext.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Application +{ + public interface IAppContext + { + string RequestId { get; } + IIdentityContext Identity { get; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/IIdentityContext.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/IIdentityContext.cs new file mode 100644 index 0000000..525b909 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/IIdentityContext.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Application +{ + public interface IIdentityContext + { + Guid Id { get; } + string Role { get; } + bool IsAuthenticated { get; } + bool IsAdmin { get; } + IDictionary Claims { get; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Qeries/GetResource.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Qeries/GetResource.cs new file mode 100644 index 0000000..e2675c6 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Qeries/GetResource.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using SwiftParcel.Services.Availability.Application.DTO; + +namespace SwiftParcel.Services.Availability.Application.Qeries +{ + public class GetResource : IQuery + { + public Guid ResourceId { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Qeries/GetResources.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Qeries/GetResources.cs new file mode 100644 index 0000000..4fdd304 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Qeries/GetResources.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using SwiftParcel.Services.Availability.Application.DTO; + +namespace SwiftParcel.Services.Availability.Application.Qeries +{ + public class GetResources : IQuery> + { + public IEnumerable Tags { get; set; } + public bool MatchAllTags { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Services/Clients/ICustomersServiceClient.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Services/Clients/ICustomersServiceClient.cs new file mode 100644 index 0000000..2ee8143 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Services/Clients/ICustomersServiceClient.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Availability.Application.DTO; + +namespace SwiftParcel.Services.Availability.Application.Services.Clients +{ + public interface ICustomersServiceClient + { + Task GetStateAsync(Guid id); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Services/IEventMapper.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Services/IEventMapper.cs new file mode 100644 index 0000000..1de92ee --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Services/IEventMapper.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using SwiftParcel.Services.Availability.Core.Events; + +namespace SwiftParcel.Services.Availability.Application.Services +{ + public interface IEventMapper + { + IEvent Map(IDomainEvent @event); + IEnumerable MapAll(IEnumerable events); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Services/IEventProcessor.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Services/IEventProcessor.cs new file mode 100644 index 0000000..2e48a2c --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Services/IEventProcessor.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Availability.Core.Events; + +namespace SwiftParcel.Services.Availability.Application.Services +{ + public interface IEventProcessor + { + Task ProcessAsync(IEnumerable events); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Services/IMessageBrocker.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Services/IMessageBrocker.cs new file mode 100644 index 0000000..f7d125c --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/Services/IMessageBrocker.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Availability.Application.Services +{ + public interface IMessageBrocker + { + Task PublishAsync(params IEvent[] events); + Task PublishAsync(IEnumerable events); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application.csproj b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application.csproj new file mode 100644 index 0000000..e0f5454 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application/SwiftParcel.Services.Availability.Application.csproj @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + net6.0 + enable + disable + + + diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Entities/AggregateId.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Entities/AggregateId.cs new file mode 100644 index 0000000..65bf8bf --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Entities/AggregateId.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Availability.Core.Exceptions; + +namespace SwiftParcel.Services.Availability.Core.Entities +{ + public class AggregateId: IEquatable + { + public Guid Value { get; } + + public AggregateId() : this(Guid.NewGuid()) + { + + } + + public AggregateId(Guid value) + { + if (value == Guid.Empty) + { + throw new InvalidAggregateIdException(value); + } + + Value = value; + } + + public bool Equals(AggregateId other) + { + if (ReferenceEquals(null, other)) return false; + return ReferenceEquals(this, other) || Value.Equals(other.Value); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((AggregateId) obj); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public static implicit operator Guid(AggregateId id) + => id.Value; + + public static implicit operator AggregateId(Guid id) + => new AggregateId(id); + + public override string ToString() => Value.ToString(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Entities/AggregateRoot.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Entities/AggregateRoot.cs new file mode 100644 index 0000000..4d29705 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Entities/AggregateRoot.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Availability.Core.Events; + +namespace SwiftParcel.Services.Availability.Core.Entities +{ + public abstract class AggregateRoot + { + private readonly List _events = new List(); + public IEnumerable Events => _events; + public AggregateId Id {get; protected set;} + public int Version {get; protected set;} + + // here 'event' is not an C# event, but verbatim + protected void AddEvent(IDomainEvent @event) + { + if(!_events.Any()) + { + Version++; + } + + _events.Add(@event); + } + + public void ClearEvents() => _events.Clear(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Entities/Resource.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Entities/Resource.cs new file mode 100644 index 0000000..23fa332 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Entities/Resource.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Availability.Core.Events; +using SwiftParcel.Services.Availability.Core.Exceptions; +using SwiftParcel.Services.Availability.Core.ValueObjects; + +namespace SwiftParcel.Services.Availability.Core.Entities +{ + public class Resource: AggregateRoot + { + private ISet _tags = new HashSet(); + private ISet _reservations = new HashSet(); + + public IEnumerable Tags + { + get => _tags; + private set => _tags = new HashSet(value); + } + + public IEnumerable Reservations + { + get => _reservations; + private set => _reservations = new HashSet(value); + } + + public Resource(Guid id, IEnumerable tags, IEnumerable reservations = null, + int version = 0) + { + ValidateTags(tags); + Id = id; + Tags = tags; + Reservations = reservations ?? Enumerable.Empty(); + Version = version; + } + + private static void ValidateTags(IEnumerable tags) + { + if (tags is null || !tags.Any()) + { + throw new MissingResourceTagsException(); + } + + if (tags.Any(string.IsNullOrWhiteSpace)) + { + throw new InvalidResourceTagsException(); + } + } + + public static Resource Create(Guid id, IEnumerable tags, IEnumerable reservations = null) + { + var resource = new Resource(id, tags, reservations); + resource.AddEvent(new ResourceCreated(resource)); + return resource; + } + + public void AddReservation(Reservation reservation) + { + var hasCollidingReservation = _reservations.Any(HasTheSameReservationDate); + if (hasCollidingReservation) + { + var collidingReservation = _reservations.First(HasTheSameReservationDate); + if (collidingReservation.Priority >= reservation.Priority) + { + throw new CannotExpropriateReservationException(Id, reservation.DateTime.Date); + } + + if (_reservations.Remove(collidingReservation)) + { + AddEvent(new ReservationCanceled(this, collidingReservation)); + } + } + + if (_reservations.Add(reservation)) + { + AddEvent(new ReservationAdded(this, reservation)); + } + + bool HasTheSameReservationDate(Reservation r) => r.DateTime.Date == reservation.DateTime.Date; + } + + public void ReleaseReservation(Reservation reservation) + { + if (!_reservations.Remove(reservation)) + { + return; + } + + AddEvent(new ReservationReleased(this, reservation)); + } + + public void Delete() + { + foreach (var reservation in Reservations) + { + AddEvent(new ReservationCanceled(this, reservation)); + } + + AddEvent(new ResourceDeleted(this)); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/IDomainEvent.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/IDomainEvent.cs new file mode 100644 index 0000000..494927b --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/IDomainEvent.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Core.Events +{ + public interface IDomainEvent + { + + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/ReservationAdded.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/ReservationAdded.cs new file mode 100644 index 0000000..81cfa6d --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/ReservationAdded.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Availability.Core.Entities; +using SwiftParcel.Services.Availability.Core.ValueObjects; + +namespace SwiftParcel.Services.Availability.Core.Events +{ + public class ReservationAdded : IDomainEvent + { + public Resource Resource { get; } + public Reservation Reservation { get; } + + public ReservationAdded(Resource resource, Reservation reservation) + => (Resource, Reservation) = (resource, reservation); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/ReservationCanceled.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/ReservationCanceled.cs new file mode 100644 index 0000000..883367c --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/ReservationCanceled.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Availability.Core.Entities; +using SwiftParcel.Services.Availability.Core.ValueObjects; + +namespace SwiftParcel.Services.Availability.Core.Events +{ + public class ReservationCanceled : IDomainEvent + { + public Resource Resource { get; } + public Reservation Reservation { get; } + + public ReservationCanceled(Resource resource, Reservation reservation) + => (Resource, Reservation) = (resource, reservation); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/ReservationReleased.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/ReservationReleased.cs new file mode 100644 index 0000000..f92ef96 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/ReservationReleased.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Availability.Core.Entities; +using SwiftParcel.Services.Availability.Core.ValueObjects; + +namespace SwiftParcel.Services.Availability.Core.Events +{ + public class ReservationReleased : IDomainEvent + { + public Resource Resource { get; } + public Reservation Reservation { get; } + + public ReservationReleased(Resource resource, Reservation reservation) + => (Resource, Reservation) = (resource, reservation); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/ResourceCreated.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/ResourceCreated.cs new file mode 100644 index 0000000..b072e6d --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/ResourceCreated.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Availability.Core.Entities; + +namespace SwiftParcel.Services.Availability.Core.Events +{ + public class ResourceCreated : IDomainEvent + { + public Resource Resource { get; } + + public ResourceCreated(Resource resource) + => Resource = resource; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/ResourceDeleted.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/ResourceDeleted.cs new file mode 100644 index 0000000..7a69ada --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Events/ResourceDeleted.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Availability.Core.Entities; + +namespace SwiftParcel.Services.Availability.Core.Events +{ + public class ResourceDeleted : IDomainEvent + { + public Resource Resource { get; } + + public ResourceDeleted(Resource resource) + => Resource = resource; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Exceptions/CannotExpropriateReservationException.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Exceptions/CannotExpropriateReservationException.cs new file mode 100644 index 0000000..e6f3d45 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Exceptions/CannotExpropriateReservationException.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Core.Exceptions +{ + public class CannotExpropriateReservationException : DomainException + { + public override string Code { get; } = "cannot_expropriate_reservation"; + + public Guid ResourceId { get; } + public DateTime DateTime { get; } + + public CannotExpropriateReservationException(Guid resourceId, DateTime dateTime) + : base($"Cannot expropriate resource {resourceId} reservation at {dateTime}") + => (ResourceId, DateTime) = (resourceId, dateTime); + + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Exceptions/DomainException.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Exceptions/DomainException.cs new file mode 100644 index 0000000..1032746 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Exceptions/DomainException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Core.Exceptions +{ + public class DomainException : Exception + { + public virtual string Code { get; } + + protected DomainException(string message) : base(message) + { + + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Exceptions/InvalidAggregateIdException.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Exceptions/InvalidAggregateIdException.cs new file mode 100644 index 0000000..ce2b5a7 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Exceptions/InvalidAggregateIdException.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Core.Exceptions +{ + public class InvalidAggregateIdException : DomainException + { + public override string Code { get; } = "invalid_aggregate_id"; + public Guid Id { get; } + + public InvalidAggregateIdException(Guid id) : base($"Invalid aggregate id: {id}") + => Id = id; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Exceptions/InvalidResourceTagsException.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Exceptions/InvalidResourceTagsException.cs new file mode 100644 index 0000000..6e3ccec --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Exceptions/InvalidResourceTagsException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Core.Exceptions +{ + public class InvalidResourceTagsException: DomainException + { + public override string Code { get; } = "invalid_resource_tags"; + + public InvalidResourceTagsException() : base("Resource tags are invalid.") + { + + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Exceptions/MissingResourceTagsException.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Exceptions/MissingResourceTagsException.cs new file mode 100644 index 0000000..d1ba507 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Exceptions/MissingResourceTagsException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Core.Exceptions +{ + public class MissingResourceTagsException : DomainException + { + public override string Code { get; } = "missing_resource_tags"; + + public MissingResourceTagsException() : base("Resource tags are missing.") + { + + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Repositories/IResourcesRepository.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Repositories/IResourcesRepository.cs new file mode 100644 index 0000000..2e9b423 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/Repositories/IResourcesRepository.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Availability.Core.Entities; + +namespace SwiftParcel.Services.Availability.Core.Repositories +{ + public interface IResourcesRepository + { + Task GetAsync(AggregateId id); + Task ExistsAsync(AggregateId id); + Task AddAsync(Resource resource); + Task UpdateAsync(Resource resource); + Task DeleteAsync(AggregateId id); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core.csproj b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core.csproj new file mode 100644 index 0000000..640868e --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + disable + + + diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/ValueObjects/Reservation.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/ValueObjects/Reservation.cs new file mode 100644 index 0000000..99bc34e --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Core/SwiftParcel.Services.Availability.Core/ValueObjects/Reservation.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Core.ValueObjects +{ + public struct Reservation: IEquatable + { + public DateTime DateTime { get; } + + public int Priority { get; } + + public Reservation(DateTime dateTime, int priority) + => (DateTime, Priority) = (dateTime, priority); + + public bool Equals(Reservation reservation) + => Priority.Equals(reservation.Priority) && DateTime.Date.Equals(reservation.DateTime.Date); + + public override bool Equals(object obj) + => obj is Reservation reservation && Equals(reservation); + + public override int GetHashCode() + => DateTime.Date.GetHashCode(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Contexts/AppContext.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Contexts/AppContext.cs new file mode 100644 index 0000000..ed6604a --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Contexts/AppContext.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Availability.Application; + +namespace SwiftParcel.Services.Availability.Infrastructure.Contexts +{ + internal sealed class AppContext : IAppContext + { + public string RequestId { get; } + public IIdentityContext Identity { get; } + + internal AppContext() : this(Guid.NewGuid().ToString("N"), IdentityContext.Empty) + { + } + + internal AppContext(CorrelationContext context) : this(context.CorrelationId, + context.User is null ? IdentityContext.Empty : new IdentityContext(context.User)) + { + } + + internal AppContext(string requestId, IIdentityContext identity) + { + RequestId = requestId; + Identity = identity; + } + + internal static IAppContext Empty => new AppContext(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Contexts/AppContextFactory.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Contexts/AppContextFactory.cs new file mode 100644 index 0000000..43dcbd7 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Contexts/AppContextFactory.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.MessageBrokers; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using SwiftParcel.Services.Availability.Application; + +namespace SwiftParcel.Services.Availability.Infrastructure.Contexts +{ + internal sealed class AppContextFactory : IAppContextFactory + { + private readonly ICorrelationContextAccessor _contextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + + public AppContextFactory(ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor) + { + _contextAccessor = contextAccessor; + _httpContextAccessor = httpContextAccessor; + } + + public IAppContext Create() + { + if (_contextAccessor.CorrelationContext is {}) + { + var payload = JsonConvert.SerializeObject(_contextAccessor.CorrelationContext); + + return string.IsNullOrWhiteSpace(payload) + ? AppContext.Empty + : new AppContext(JsonConvert.DeserializeObject(payload)); + } + + var context = _httpContextAccessor.GetCorrelationContext(); + + return context is null ? AppContext.Empty : new AppContext(context); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Contexts/CorrelationContext.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Contexts/CorrelationContext.cs new file mode 100644 index 0000000..deb7825 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Contexts/CorrelationContext.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Infrastructure.Contexts +{ + internal sealed class CorrelationContext + { + public string CorrelationId { get; set; } + public string SpanContext { get; set; } + public UserContext User { get; set; } + public string ResourceId { get; set; } + public string TraceId { get; set; } + public string ConnectionId { get; set; } + public string Name { get; set; } + public DateTime CreatedAt { get; set; } + + public class UserContext + { + public string Id { get; set; } + public bool IsAuthenticated { get; set; } + public string Role { get; set; } + public IDictionary Claims { get; set; } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Contexts/IAppContextFactory.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Contexts/IAppContextFactory.cs new file mode 100644 index 0000000..a3c2d3d --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Contexts/IAppContextFactory.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Availability.Application; + +namespace SwiftParcel.Services.Availability.Infrastructure.Contexts +{ + internal interface IAppContextFactory + { + IAppContext Create(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Contexts/IdentityContext.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Contexts/IdentityContext.cs new file mode 100644 index 0000000..5ef0cd5 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Contexts/IdentityContext.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Availability.Application; + +namespace SwiftParcel.Services.Availability.Infrastructure.Contexts +{ + internal sealed class IdentityContext : IIdentityContext + { + public Guid Id { get; } + public string Role { get; } = string.Empty; + public bool IsAuthenticated { get; } + public bool IsAdmin { get; } + public IDictionary Claims { get; } = new Dictionary(); + + internal IdentityContext() + { + } + + internal IdentityContext(CorrelationContext.UserContext context) + : this(context.Id, context.Role, context.IsAuthenticated, context.Claims) + { + } + + internal IdentityContext(string id, string role, bool isAuthenticated, IDictionary claims) + { + Id = Guid.TryParse(id, out var userId) ? userId : Guid.Empty; + Role = role ?? string.Empty; + IsAuthenticated = isAuthenticated; + IsAdmin = Role.Equals("admin", StringComparison.InvariantCultureIgnoreCase); + Claims = claims ?? new Dictionary(); + } + + internal static IIdentityContext Empty => new IdentityContext(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs new file mode 100644 index 0000000..fa4a8a8 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; +using Convey.Types; + + +namespace SwiftParcel.Services.Availability.Infrastructure.Decorators +{ + internal sealed class OutboxCommandHandlerDecorator : ICommandHandler + where TCommand : class, ICommand + { + private readonly ICommandHandler _handler; + private readonly IMessageOutbox _outbox; + private readonly string _messageId; + private readonly bool _enabled; + + public OutboxCommandHandlerDecorator(ICommandHandler handler, IMessageOutbox outbox, + OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) + { + _handler = handler; + _outbox = outbox; + _enabled = outboxOptions.Enabled; + + var messageProperties = messagePropertiesAccessor.MessageProperties; + _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId) + ? Guid.NewGuid().ToString("N") + : messageProperties.MessageId; + } + + public Task HandleAsync(TCommand command, CancellationToken cancellationToken) + => _enabled + ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(command)) + : _handler.HandleAsync(command); + } + +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs new file mode 100644 index 0000000..ef43bbe --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; + +namespace SwiftParcel.Services.Availability.Infrastructure.Decorators +{ + internal sealed class OutboxEventHandlerDecorator : IEventHandler + where TEvent : class, IEvent + { + private readonly IEventHandler _handler; + private readonly IMessageOutbox _outbox; + private readonly string _messageId; + private readonly bool _enabled; + + public OutboxEventHandlerDecorator(IEventHandler handler, IMessageOutbox outbox, + OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) + { + _handler = handler; + _outbox = outbox; + _enabled = outboxOptions.Enabled; + + var messageProperties = messagePropertiesAccessor.MessageProperties; + _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId) + ? Guid.NewGuid().ToString("N") + : messageProperties.MessageId; + } + + public Task HandleAsync(TEvent @event, CancellationToken cancellationToken) + => _enabled + ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(@event)) + : _handler.HandleAsync(@event); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Exceptions/ExceptionToMessageMapper.cs new file mode 100644 index 0000000..3eca324 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.MessageBrokers.RabbitMQ; +using SwiftParcel.Services.Availability.Application.Commands; +using SwiftParcel.Services.Availability.Application.Events.Rejected; +using SwiftParcel.Services.Availability.Application.Exceptions; +using SwiftParcel.Services.Availability.Core.Exceptions; + +namespace SwiftParcel.Services.Availability.Infrastructure.Exceptions +{ + public class ExceptionToMessageMapper : IExceptionToMessageMapper + { + public object Map(Exception exception, object message) + => exception switch + { + MissingResourceTagsException ex => new AddResourceRejected(Guid.Empty, ex.Message, + ex.Code), + InvalidResourceTagsException ex => new AddResourceRejected(Guid.Empty, ex.Message, + ex.Code), + ResourceAlreadyExistsException ex => new AddResourceRejected(ex.Id, ex.Message, + ex.Code), + CannotExpropriateReservationException ex => message switch + { + ReserveResource command => new ReleaseResourceReservationRejected(command.ResourceId, command.DateTime, + ex.Message, ex.Code), + _ => null + }, + CustomerNotFoundException ex => message switch + { + ReserveResource command => new ReleaseResourceReservationRejected(command.ResourceId, command.DateTime, + ex.Message, ex.Code), + _ => null + }, + InvalidCustomerStateException ex => message switch + { + ReserveResource command => new ReleaseResourceReservationRejected(command.ResourceId, command.DateTime, + ex.Message, ex.Code), + _ => null + }, + ResourceNotFoundException ex => message switch + { + DeleteResource command => new DeleteResourceRejected(command.ResourceId, + ex.Message, ex.Code), + ReserveResource command => new ReleaseResourceReservationRejected(command.ResourceId, command.DateTime, + ex.Message, ex.Code), + ReleaseResourceReservation command => new ReleaseResourceRejected(command.ResourceId, command.DateTime, + ex.Message, ex.Code), + _ => null + }, + UnauthorizedResourceAccessException ex => message switch + { + ReserveResource command => new ReleaseResourceReservationRejected(command.ResourceId, command.DateTime, + ex.Message, ex.Code), + _ => null + }, + _ => null + }; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Exceptions/ExceptionToResponseMapper.cs new file mode 100644 index 0000000..5c1d3c2 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Convey; +using Convey.WebApi.Exceptions; +using SwiftParcel.Services.Availability.Application.Exceptions; +using SwiftParcel.Services.Availability.Core.Exceptions; + +namespace SwiftParcel.Services.Availability.Infrastructure.Exceptions +{ + internal sealed class ExceptionToResponseMapper : IExceptionToResponseMapper + { + private static readonly ConcurrentDictionary Codes = new ConcurrentDictionary(); + + public ExceptionResponse Map(Exception exception) + => exception switch + { + DomainException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message}, + HttpStatusCode.BadRequest), + AppException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message}, + HttpStatusCode.BadRequest), + _ => new ExceptionResponse(new {code = "error", reason = "There was an error."}, + HttpStatusCode.BadRequest) + }; + + private static string GetCode(Exception exception) + { + var type = exception.GetType(); + if (Codes.TryGetValue(type, out var code)) + { + return code; + } + + var exceptionCode = exception switch + { + DomainException domainException when !string.IsNullOrWhiteSpace(domainException.Code) => domainException + .Code, + AppException appException when !string.IsNullOrWhiteSpace(appException.Code) => appException.Code, + _ => exception.GetType().Name.Underscore().Replace("_exception", string.Empty) + }; + + Codes.TryAdd(type, exceptionCode); + + return exceptionCode; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Extensions.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Extensions.cs new file mode 100644 index 0000000..cedfea1 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Extensions.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Convey; +using Convey.CQRS.Commands; +using Convey.CQRS.Events; +using Convey.CQRS.Queries; +using Convey.Discovery.Consul; +using Convey.Docs.Swagger; +using Convey.HTTP; +using Convey.LoadBalancing.Fabio; +using Convey.MessageBrokers; +using Convey.MessageBrokers.CQRS; +using Convey.MessageBrokers.Outbox; +using Convey.MessageBrokers.Outbox.Mongo; +using Convey.MessageBrokers.RabbitMQ; +using Convey.Metrics.AppMetrics; +using Convey.Persistence.MongoDB; +using Convey.Persistence.Redis; +using Convey.Security; +using Convey.Tracing.Jaeger; +using Convey.Tracing.Jaeger.RabbitMQ; +using Convey.WebApi; +using Convey.WebApi.CQRS; +using Convey.WebApi.Security; +using Convey.WebApi.Swagger; +using Elasticsearch.Net; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using SwiftParcel.Services.Availability.Application; +using SwiftParcel.Services.Availability.Application.Commands; +using SwiftParcel.Services.Availability.Application.Events; +using SwiftParcel.Services.Availability.Application.Events.External; +using SwiftParcel.Services.Availability.Application.Services; +using SwiftParcel.Services.Availability.Application.Services.Clients; +using SwiftParcel.Services.Availability.Core.Repositories; +using SwiftParcel.Services.Availability.Infrastructure.Contexts; +using SwiftParcel.Services.Availability.Infrastructure.Decorators; +using SwiftParcel.Services.Availability.Infrastructure.Exceptions; +using SwiftParcel.Services.Availability.Infrastructure.Jeager; +using SwiftParcel.Services.Availability.Infrastructure.Logging; +using SwiftParcel.Services.Availability.Infrastructure.Metrics; +using SwiftParcel.Services.Availability.Infrastructure.Mongo.Documents; +using SwiftParcel.Services.Availability.Infrastructure.Mongo.Repositories; +using SwiftParcel.Services.Availability.Infrastructure.Services; +using SwiftParcel.Services.Availability.Infrastructure.Services.Clients; + +namespace SwiftParcel.Services.Availability.Infrastructure +{ + public static class Extensions + { + public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + { + builder.Services.AddSingleton(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddSingleton(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(ctx => ctx.GetRequiredService().Create()); + builder.Services.AddHostedService(); + builder.Services.AddSingleton(); + builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>)); + builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>)); + builder.Services.Scan(s => s.FromAssemblies(AppDomain.CurrentDomain.GetAssemblies()) + .AddClasses(c => c.AssignableTo(typeof(IDomainEventHandler<>))) + .AsImplementedInterfaces() + .WithTransientLifetime()); + + return builder + .AddErrorHandler() + .AddQueryHandlers() + .AddInMemoryQueryDispatcher() + .AddHttpClient() + .AddConsul() + .AddFabio() + .AddRabbitMq(plugins: p => p.AddJaegerRabbitMqPlugin()) + .AddMessageOutbox(o => o.AddMongo()) + .AddExceptionToMessageMapper() + .AddMongo() + .AddRedis() + .AddMetrics() + .AddJaeger() + .AddJaegerDecorators() + .AddHandlersLogging() + .AddMongoRepository("resources") + .AddWebApiSwaggerDocs() + .AddCertificateAuthentication() + .AddSecurity(); + } + + public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app) + { + app.UseErrorHandler() + .UseSwaggerDocs() + .UseJaeger() + .UseConvey() + .UsePublicContracts() + .UseMetrics() + .UseMiddleware() + .UseCertificateAuthentication() + .UseRabbitMq() + .SubscribeCommand() + .SubscribeCommand() + .SubscribeCommand() + .SubscribeCommand() + .SubscribeEvent() + .SubscribeEvent(); + + return app; + } + + internal static CorrelationContext GetCorrelationContext(this IHttpContextAccessor accessor) + => accessor.HttpContext?.Request.Headers.TryGetValue("Correlation-Context", out var json) is true + ? JsonConvert.DeserializeObject(json.FirstOrDefault()) + : null; + + internal static IDictionary GetHeadersToForward(this IMessageProperties messageProperties) + { + const string sagaHeader = "Saga"; + if (messageProperties?.Headers is null || !messageProperties.Headers.TryGetValue(sagaHeader, out var saga)) + { + return null; + } + + return saga is null + ? null + : new Dictionary + { + [sagaHeader] = saga + }; + } + + internal static string GetSpanContext(this IMessageProperties messageProperties, string header) + { + if (messageProperties is null) + { + return string.Empty; + } + + if (messageProperties.Headers.TryGetValue(header, out var span) && span is byte[] spanBytes) + { + return Encoding.UTF8.GetString(spanBytes); + } + + return string.Empty; + } + } +} diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Jeager/Extensions.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Jeager/Extensions.cs new file mode 100644 index 0000000..baf0ed8 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Jeager/Extensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey; +using Convey.CQRS.Commands; +using Microsoft.Extensions.DependencyInjection; + +namespace SwiftParcel.Services.Availability.Infrastructure.Jeager +{ + internal static class Extensions + { + public static IConveyBuilder AddJaegerDecorators(this IConveyBuilder builder) + { + builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(JaegerCommandHandlerDecorator<>)); + + return builder; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Jeager/JaegerCommandHandlerDecorator.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Jeager/JaegerCommandHandlerDecorator.cs new file mode 100644 index 0000000..eebe637 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Jeager/JaegerCommandHandlerDecorator.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using OpenTracing; +using OpenTracing.Tag; + +namespace SwiftParcel.Services.Availability.Infrastructure.Jeager +{ + public class JaegerCommandHandlerDecorator : ICommandHandler + where TCommand : class, ICommand + { + private readonly ICommandHandler _handler; + private readonly ITracer _tracer; + + public JaegerCommandHandlerDecorator(ICommandHandler handler, ITracer tracer) + { + _handler = handler; + _tracer = tracer; + } + + public async Task HandleAsync(TCommand command, CancellationToken cancellationToken) + { + var commandName = ToUnderscoreCase(command.GetType().Name); + using var scope = BuildScope(commandName); + var span = scope.Span; + + try + { + span.Log($"Handling a message: {commandName}"); + await _handler.HandleAsync(command); + span.Log($"Handled a message: {commandName}"); + } + catch (Exception ex) + { + span.Log(ex.Message); + span.SetTag(Tags.Error, true); + throw; + } + } + + private IScope BuildScope(string commandName) + { + var scope = _tracer + .BuildSpan($"handling-{commandName}") + .WithTag("message-type", commandName); + + if (_tracer.ActiveSpan is {}) + { + scope.AddReference(References.ChildOf, _tracer.ActiveSpan.Context); + } + + return scope.StartActive(true); + } + + private static string ToUnderscoreCase(string str) + => string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x : x.ToString())) + .ToLowerInvariant(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Logging/Extensions.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Logging/Extensions.cs new file mode 100644 index 0000000..76124ea --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Logging/Extensions.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey; +using Convey.Logging.CQRS; +using Microsoft.Extensions.DependencyInjection; +using SwiftParcel.Services.Availability.Application.Commands; + +namespace SwiftParcel.Services.Availability.Infrastructure.Logging +{ + internal static class Extensions + { + public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + { + var assembly = typeof(AddResource).Assembly; + + builder.Services.AddSingleton(new MessageToLogTemplateMapper()); + + return builder + .AddCommandHandlersLogging(assembly) + .AddEventHandlersLogging(assembly); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Logging/MessageToLogTemplateMapper.cs new file mode 100644 index 0000000..55986bf --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.Logging.CQRS; +using SwiftParcel.Services.Availability.Application.Commands; +using SwiftParcel.Services.Availability.Application.Events.External; +using SwiftParcel.Services.Availability.Application.Exceptions; + +namespace SwiftParcel.Services.Availability.Infrastructure.Logging +{ + internal sealed class MessageToLogTemplateMapper : IMessageToLogTemplateMapper + { + private static IReadOnlyDictionary MessageTemplates + => new Dictionary + { + { + typeof(AddResource), + new HandlerLogTemplate + { + After = "Added a resource with id: {ResourceId}. ", + OnError = new Dictionary + { + { typeof(ResourceAlreadyExistsException), "Resource with id: {ResourceId} already exists."} + } + } + }, + {typeof(DeleteResource), new HandlerLogTemplate { After = "Deleted a resource with id: {ResourceId}."}}, + {typeof(ReleaseResourceReservation), new HandlerLogTemplate { After = "Released a resource with id: {ResourceId}."}}, + {typeof(ReserveResource), new HandlerLogTemplate { After = "Reserved a resource with id: {ResourceId} " + + "priority: {Priority}, date: {DateTime}."}}, + {typeof(CourierDeleted), new HandlerLogTemplate{ Before = "Courier with id: {CourierId} has been deleted."}}, + }; + + public HandlerLogTemplate Map(TMessage message) where TMessage : class + { + var key = message.GetType(); + return MessageTemplates.TryGetValue(key, out var template) ? template : null; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Metrics/CustomMetricsMiddleware.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Metrics/CustomMetricsMiddleware.cs new file mode 100644 index 0000000..573f66b --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Metrics/CustomMetricsMiddleware.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using App.Metrics; +using App.Metrics.Counter; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using SwiftParcel.Services.Availability.Application.Commands; +using SwiftParcel.Services.Availability.Application.Qeries; + +namespace SwiftParcel.Services.Availability.Infrastructure.Metrics +{ + public class CustomMetricsMiddleware : IMiddleware + { + private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly bool _enabled; + + private readonly IDictionary _metrics = new Dictionary + { + [GetKey("GET", "/resources")] = Query(typeof(GetResources).Name), + [GetKey("POST", "/resources")] = Command(typeof(AddResource).Name) + }; + + public CustomMetricsMiddleware(IServiceScopeFactory serviceScopeFactory, MetricsOptions options) + { + _serviceScopeFactory = serviceScopeFactory; + _enabled = options.Enabled; + } + + public Task InvokeAsync(HttpContext context, RequestDelegate next) + { + if (!_enabled) + { + return next(context); + } + + var request = context.Request; + if (!_metrics.TryGetValue(GetKey(request.Method, request.Path.ToString()), out var metrics)) + { + return next(context); + } + + using var scope = _serviceScopeFactory.CreateScope(); + var metricsRoot = scope.ServiceProvider.GetRequiredService(); + metricsRoot.Measure.Counter.Increment(metrics); + + return next(context); + } + + private static string GetKey(string method, string path) + => $"{method}:{path}"; + + private static CounterOptions Command(string command) + => new CounterOptions + { + Name = "commands", + Tags = new MetricTags("command", command) + }; + + private static CounterOptions Query(string query) + => new CounterOptions + { + Name = "queries", + Tags = new MetricTags("query", query) + }; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Metrics/MetricsJob.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Metrics/MetricsJob.cs new file mode 100644 index 0000000..4a4519d --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Metrics/MetricsJob.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using App.Metrics; +using App.Metrics.Gauge; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace SwiftParcel.Services.Availability.Infrastructure.Metrics +{ + public class MetricsJob: BackgroundService + { + private readonly GaugeOptions _threads = new GaugeOptions + { + Name = "threads" + }; + + private readonly GaugeOptions _workingSet = new GaugeOptions + { + Name = "working_set" + }; + + private readonly ILogger _logger; + private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly MetricsOptions _options; + + public MetricsJob(IServiceScopeFactory serviceScopeFactory, MetricsOptions options, ILogger logger) + { + _logger = logger; + _serviceScopeFactory = serviceScopeFactory; + _options = options; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + if (!_options.Enabled) + { + _logger.LogInformation("Metrics are disabled."); + return; + } + + while (!stoppingToken.IsCancellationRequested) + { + using (var scope = _serviceScopeFactory.CreateScope()) + { + _logger.LogTrace("Processing metrics..."); + var metricsRoot = scope.ServiceProvider.GetRequiredService(); + var process = Process.GetCurrentProcess(); + metricsRoot.Measure.Gauge.SetValue(_threads, process.Threads.Count); + metricsRoot.Measure.Gauge.SetValue(_workingSet, process.WorkingSet64); + } + + await Task.Delay(5000, stoppingToken); + } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Documents/Extensions.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Documents/Extensions.cs new file mode 100644 index 0000000..c918f48 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Documents/Extensions.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Availability.Application.DTO; +using SwiftParcel.Services.Availability.Core.Entities; +using SwiftParcel.Services.Availability.Core.ValueObjects; + +namespace SwiftParcel.Services.Availability.Infrastructure.Mongo.Documents +{ + internal static class Extensions + { + public static Resource AsEntity(this ResourceDocument document) + => new Resource(document.Id, document.Tags, document.Reservations + .Select(r => new Reservation(r.TimeStamp.AsDateTime(), r.Priority)), document.Version); + + public static ResourceDocument AsDocument(this Resource entity) + => new ResourceDocument + { + Id = entity.Id, + Version = entity.Version, + Tags = entity.Tags, + Reservations = entity.Reservations.Select(r => new ReservationDocument + { + TimeStamp = r.DateTime.AsDaysSinceEpoch(), + Priority = r.Priority + }) + }; + + public static ResourceDto AsDto(this ResourceDocument document) + => new ResourceDto + { + Id = document.Id, + Tags = document.Tags ?? Enumerable.Empty(), + Reservations = document.Reservations?.Select(r => new ReservationDto + { + DateTime = r.TimeStamp.AsDateTime(), + Priority = r.Priority + }) ?? Enumerable.Empty() + }; + + internal static int AsDaysSinceEpoch(this DateTime dateTime) + => (dateTime - new DateTime()).Days; + + internal static DateTime AsDateTime(this int daysSinceEpoch) + => new DateTime().AddDays(daysSinceEpoch); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Documents/ReservationDocument.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Documents/ReservationDocument.cs new file mode 100644 index 0000000..49f5bbd --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Documents/ReservationDocument.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Availability.Infrastructure.Mongo.Documents +{ + internal sealed class ReservationDocument + { + public int TimeStamp { get; set; } + public int Priority { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Documents/ResourceDocument.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Documents/ResourceDocument.cs new file mode 100644 index 0000000..a2fc799 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Documents/ResourceDocument.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.Types; + +namespace SwiftParcel.Services.Availability.Infrastructure.Mongo.Documents +{ + internal sealed class ResourceDocument : IIdentifiable + { + public Guid Id { get; set; } + public int Version { get; set; } + public IEnumerable Tags { get; set; } + public IEnumerable Reservations { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Queries/GetResourceHandler.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Queries/GetResourceHandler.cs new file mode 100644 index 0000000..9b68180 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Queries/GetResourceHandler.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using MongoDB.Driver; +using SwiftParcel.Services.Availability.Application.DTO; +using SwiftParcel.Services.Availability.Application.Qeries; +using SwiftParcel.Services.Availability.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Availability.Infrastructure.Mongo.Queries +{ + public class GetResourceHandler: IQueryHandler + { + private readonly IMongoDatabase _database; + + public GetResourceHandler(IMongoDatabase database) + { + _database = database; + } + + public async Task HandleAsync(GetResource query, CancellationToken cancellationToken) + { + var document = await _database.GetCollection("resources") + .Find(r => r.Id == query.ResourceId) + .SingleOrDefaultAsync(); + + return document?.AsDto(); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Queries/GetResourcesHandler.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Queries/GetResourcesHandler.cs new file mode 100644 index 0000000..e59f0ad --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Queries/GetResourcesHandler.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using MongoDB.Driver; +using SwiftParcel.Services.Availability.Application.DTO; +using SwiftParcel.Services.Availability.Application.Qeries; +using SwiftParcel.Services.Availability.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Availability.Infrastructure.Mongo.Queries +{ + public class GetResourcesHandler: IQueryHandler> + { + private readonly IMongoDatabase _database; + + public GetResourcesHandler(IMongoDatabase database) + { + _database = database; + } + + public async Task> HandleAsync(GetResources query, CancellationToken cancellationToken) + { + var collection = _database.GetCollection("resources"); + + if (query.Tags is null || !query.Tags.Any()) + { + var allDocuments = await collection.Find(_ => true).ToListAsync(); + + return allDocuments.Select(d => d.AsDto()); + } + + var documents = collection.AsQueryable(); + documents = (MongoDB.Driver.Linq.IMongoQueryable)(query.MatchAllTags + ? documents.Where(d => query.Tags.All(t => d.Tags.Contains(t))) + : documents.Where(d => query.Tags.Any(t => d.Tags.Contains(t)))); + + var resources = await documents.ToListAsync(); + + return resources.Select(d => d.AsDto()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Repositories/ResourcesMongoRepository.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Repositories/ResourcesMongoRepository.cs new file mode 100644 index 0000000..0c43a75 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Mongo/Repositories/ResourcesMongoRepository.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.Persistence.MongoDB; +using MongoDB.Driver; +using SwiftParcel.Services.Availability.Core.Entities; +using SwiftParcel.Services.Availability.Core.Repositories; +using SwiftParcel.Services.Availability.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Availability.Infrastructure.Mongo.Repositories +{ + internal sealed class ResourcesMongoRepository: IResourcesRepository + { + private readonly IMongoRepository _repository; + + public ResourcesMongoRepository(IMongoRepository repository) + => _repository = repository; + + public async Task GetAsync(AggregateId id) + { + var document = await _repository.GetAsync(r => r.Id == id); + return document?.AsEntity(); + } + + public Task ExistsAsync(AggregateId id) + => _repository.ExistsAsync(r => r.Id == id); + + public Task AddAsync(Resource resource) + => _repository.AddAsync(resource.AsDocument()); + + public Task UpdateAsync(Resource resource) + => _repository.Collection.ReplaceOneAsync(r => r.Id == resource.Id && r.Version < resource.Version, + resource.AsDocument()); + + public Task DeleteAsync(AggregateId id) + => _repository.DeleteAsync(id); + } +} diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Services/Clients/CustomersServiceClient.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Services/Clients/CustomersServiceClient.cs new file mode 100644 index 0000000..2f4b95d --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Services/Clients/CustomersServiceClient.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.HTTP; +using Convey.Secrets.Vault; +using Convey.WebApi.Security; +using SwiftParcel.Services.Availability.Application.DTO; +using SwiftParcel.Services.Availability.Application.Services.Clients; + +namespace SwiftParcel.Services.Availability.Infrastructure.Services.Clients +{ + internal sealed class CustomersServiceClient : ICustomersServiceClient + { + private readonly IHttpClient _httpClient; + private readonly string _url; + + public CustomersServiceClient(IHttpClient httpClient, HttpClientOptions options, + ICertificatesService certificatesService, VaultOptions vaultOptions, SecurityOptions securityOptions) + { + _httpClient = httpClient; + _url = options.Services["customers"]; + if (!vaultOptions.Enabled || vaultOptions.Pki?.Enabled != true) + { + return; + } + + var certificate = certificatesService.Get(vaultOptions.Pki.RoleName); + if (certificate is null) + { + return; + } + + var header = securityOptions.Certificate.GetHeaderName(); + var certificateData = certificate.GetRawCertDataString(); + _httpClient.SetHeaders(h => h.Add(header, certificateData)); + } + + public Task GetStateAsync(Guid id) + => _httpClient.GetAsync($"{_url}/customers/{id}/state"); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Services/EventMapper.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Services/EventMapper.cs new file mode 100644 index 0000000..eaa3edd --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Services/EventMapper.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using SwiftParcel.Services.Availability.Application.Events; +using SwiftParcel.Services.Availability.Application.Services; +using SwiftParcel.Services.Availability.Core.Events; +using ReservationCanceled = SwiftParcel.Services.Availability.Core.Events.ReservationCanceled; +using ResourceDeleted = SwiftParcel.Services.Availability.Core.Events.ResourceDeleted; + + +namespace SwiftParcel.Services.Availability.Infrastructure.Services +{ + internal sealed class EventMapper: IEventMapper + { + public IEnumerable MapAll(IEnumerable events) + => events.Select(Map); + + public IEvent Map(IDomainEvent @event) + => @event switch + { + ResourceCreated e => (IEvent) new ResourceAdded(e.Resource.Id), + ResourceDeleted e => new Application.Events.ResourceDeleted(e.Resource.Id), + ReservationAdded e => new ResourceReserved(e.Resource.Id, e.Reservation.DateTime), + ReservationReleased e => new ResourceReservationReleased(e.Resource.Id, e.Reservation.DateTime), + ReservationCanceled e => new ResourceReservationCanceled(e.Resource.Id, e.Reservation.DateTime), + _ => null + }; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Services/EventProcessor.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Services/EventProcessor.cs new file mode 100644 index 0000000..e8867dc --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Services/EventProcessor.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using SwiftParcel.Services.Availability.Application.Events; +using SwiftParcel.Services.Availability.Application.Services; +using SwiftParcel.Services.Availability.Core.Events; + +namespace SwiftParcel.Services.Availability.Infrastructure.Services +{ + internal sealed class EventProcessor : IEventProcessor + { + private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly IEventMapper _eventMapper; + private readonly IMessageBroker _messageBroker; + private readonly ILogger _logger; + + public EventProcessor(IServiceScopeFactory serviceScopeFactory, IEventMapper eventMapper, + IMessageBroker messageBroker, ILogger logger) + { + _serviceScopeFactory = serviceScopeFactory; + _eventMapper = eventMapper; + _messageBroker = messageBroker; + _logger = logger; + } + + public async Task ProcessAsync(IEnumerable events) + { + if (events is null) + { + return; + } + + _logger.LogTrace("Processing domain events..."); + var integrationEvents = await HandleDomainEvents(events); + if (!integrationEvents.Any()) + { + return; + } + + _logger.LogTrace("Processing integration events..."); + await _messageBroker.PublishAsync(integrationEvents); + } + + private async Task> HandleDomainEvents(IEnumerable events) + { + var integrationEvents = new List(); + using var scope = _serviceScopeFactory.CreateScope(); + foreach (var @event in events) + { + var eventType = @event.GetType(); + _logger.LogTrace($"Handling domain event: {eventType.Name}"); + var handlerType = typeof(IDomainEventHandler<>).MakeGenericType(eventType); + dynamic handlers = scope.ServiceProvider.GetServices(handlerType); + foreach (var handler in handlers) + { + await handler.HandleAsync((dynamic) @event); + } + + var integrationEvent = _eventMapper.Map(@event); + if (integrationEvent is null) + { + continue; + } + + integrationEvents.Add(integrationEvent); + } + + return integrationEvents; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Services/IMessageBroker.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Services/IMessageBroker.cs new file mode 100644 index 0000000..91b07b9 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Services/IMessageBroker.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Availability.Infrastructure.Services +{ + public interface IMessageBroker + { + Task PublishAsync(params IEvent[] events); + Task PublishAsync(IEnumerable events); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Services/MessageBroker.cs b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Services/MessageBroker.cs new file mode 100644 index 0000000..ba8a849 --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/Services/MessageBroker.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; +using Convey.MessageBrokers.RabbitMQ; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using OpenTracing; + +namespace SwiftParcel.Services.Availability.Infrastructure.Services +{ + internal sealed class MessageBroker: IMessageBroker + { + private const string DefaultSpanContextHeader = "span_context"; + private readonly IBusPublisher _busPublisher; + private readonly IMessageOutbox _outbox; + private readonly ICorrelationContextAccessor _contextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IMessagePropertiesAccessor _messagePropertiesAccessor; + private readonly ITracer _tracer; + private readonly ILogger _logger; + private readonly string _spanContextHeader; + + public MessageBroker(IBusPublisher busPublisher, IMessageOutbox outbox, + ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor, + IMessagePropertiesAccessor messagePropertiesAccessor, RabbitMqOptions options, ITracer tracer, + ILogger logger) + { + _busPublisher = busPublisher; + _outbox = outbox; + _contextAccessor = contextAccessor; + _httpContextAccessor = httpContextAccessor; + _messagePropertiesAccessor = messagePropertiesAccessor; + _tracer = tracer; + _logger = logger; + _spanContextHeader = string.IsNullOrWhiteSpace(options.SpanContextHeader) + ? DefaultSpanContextHeader + : options.SpanContextHeader; + } + + public Task PublishAsync(params IEvent[] events) => PublishAsync(events?.AsEnumerable()); + + public async Task PublishAsync(IEnumerable events) + { + if (events is null) + { + return; + } + + var messageProperties = _messagePropertiesAccessor.MessageProperties; + var originatedMessageId = messageProperties?.MessageId; + var correlationId = messageProperties?.CorrelationId; + var spanContext = messageProperties?.GetSpanContext(_spanContextHeader); + if (string.IsNullOrWhiteSpace(spanContext)) + { + spanContext = _tracer.ActiveSpan is null ? string.Empty : _tracer.ActiveSpan.Context.ToString(); + } + + var headers = messageProperties.GetHeadersToForward(); + var correlationContext = _contextAccessor.CorrelationContext ?? + _httpContextAccessor.GetCorrelationContext(); + + foreach (var @event in events) + { + if (@event is null) + { + continue; + } + + var messageId = Guid.NewGuid().ToString("N"); + _logger.LogTrace($"Publishing integration event: {@event.GetType().Name} [id: '{messageId}']."); + if (_outbox.Enabled) + { + await _outbox.SendAsync(@event, originatedMessageId, messageId, correlationId, spanContext, + correlationContext, headers); + continue; + } + + await _busPublisher.PublishAsync(@event, messageId, correlationId, spanContext, correlationContext, + headers); + } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure.csproj b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure.csproj new file mode 100644 index 0000000..3413d9a --- /dev/null +++ b/SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure/SwiftParcel.Services.Availability.Infrastructure.csproj @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + net6.0 + enable + disable + + + diff --git a/SwiftParcel.Services.Couriers/SwiftParcel.Services.Couriers.sln b/SwiftParcel.Services.Couriers/SwiftParcel.Services.Couriers.sln new file mode 100644 index 0000000..9924752 --- /dev/null +++ b/SwiftParcel.Services.Couriers/SwiftParcel.Services.Couriers.sln @@ -0,0 +1,60 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{67465F3C-B4F2-4DAE-86A8-05047C2291E1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Couriers.Api", "SwiftParcel.Services.Couriers.Api", "{99B398DB-F033-4D6A-9D52-1A7BDF4414E1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Couriers.Api", "src\SwiftParcel.Services.Couriers.Api\SwiftParcel.Services.Couriers.Api\SwiftParcel.Services.Couriers.Api.csproj", "{A463D0B1-3D99-4842-AF72-190CA6807884}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Couriers.Application", "SwiftParcel.Services.Couriers.Application", "{CCB2F1E2-630A-4B5B-ACA6-504A39698B27}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Couriers.Application", "src\SwiftParcel.Services.Couriers.Application\SwiftParcel.Services.Couriers.Application\SwiftParcel.Services.Couriers.Application.csproj", "{42EAD761-30F3-41D5-AE95-F207CDFD3F1F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Couriers.Core", "SwiftParcel.Services.Couriers.Core", "{03CE8E75-731B-4228-BCE3-D4D3F40C8F1D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Couriers.Core", "src\SwiftParcel.Services.Couriers.Core\SwiftParcel.Services.Couriers.Core\SwiftParcel.Services.Couriers.Core.csproj", "{EB4751CB-FC11-4289-9F3F-B2C3FB4B92DF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Couriers.Infrastructure", "SwiftParcel.Services.Couriers.Infrastructure", "{CA6BE893-52D3-4F92-A7F6-B4D82EF9C999}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Couriers.Infrastructure", "src\SwiftParcel.Services.Couriers.Infrastructure\SwiftParcel.Services.Couriers.Infrastructure\SwiftParcel.Services.Couriers.Infrastructure.csproj", "{4107EAA5-65B2-4C31-9DBF-4BA7CA6A4E42}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A463D0B1-3D99-4842-AF72-190CA6807884}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A463D0B1-3D99-4842-AF72-190CA6807884}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A463D0B1-3D99-4842-AF72-190CA6807884}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A463D0B1-3D99-4842-AF72-190CA6807884}.Release|Any CPU.Build.0 = Release|Any CPU + {42EAD761-30F3-41D5-AE95-F207CDFD3F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42EAD761-30F3-41D5-AE95-F207CDFD3F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42EAD761-30F3-41D5-AE95-F207CDFD3F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42EAD761-30F3-41D5-AE95-F207CDFD3F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {EB4751CB-FC11-4289-9F3F-B2C3FB4B92DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB4751CB-FC11-4289-9F3F-B2C3FB4B92DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB4751CB-FC11-4289-9F3F-B2C3FB4B92DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB4751CB-FC11-4289-9F3F-B2C3FB4B92DF}.Release|Any CPU.Build.0 = Release|Any CPU + {4107EAA5-65B2-4C31-9DBF-4BA7CA6A4E42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4107EAA5-65B2-4C31-9DBF-4BA7CA6A4E42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4107EAA5-65B2-4C31-9DBF-4BA7CA6A4E42}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4107EAA5-65B2-4C31-9DBF-4BA7CA6A4E42}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {99B398DB-F033-4D6A-9D52-1A7BDF4414E1} = {67465F3C-B4F2-4DAE-86A8-05047C2291E1} + {A463D0B1-3D99-4842-AF72-190CA6807884} = {99B398DB-F033-4D6A-9D52-1A7BDF4414E1} + {CCB2F1E2-630A-4B5B-ACA6-504A39698B27} = {67465F3C-B4F2-4DAE-86A8-05047C2291E1} + {42EAD761-30F3-41D5-AE95-F207CDFD3F1F} = {CCB2F1E2-630A-4B5B-ACA6-504A39698B27} + {03CE8E75-731B-4228-BCE3-D4D3F40C8F1D} = {67465F3C-B4F2-4DAE-86A8-05047C2291E1} + {EB4751CB-FC11-4289-9F3F-B2C3FB4B92DF} = {03CE8E75-731B-4228-BCE3-D4D3F40C8F1D} + {CA6BE893-52D3-4F92-A7F6-B4D82EF9C999} = {67465F3C-B4F2-4DAE-86A8-05047C2291E1} + {4107EAA5-65B2-4C31-9DBF-4BA7CA6A4E42} = {CA6BE893-52D3-4F92-A7F6-B4D82EF9C999} + EndGlobalSection +EndGlobal diff --git a/SwiftParcel.Services.Couriers/scripts/build.sh b/SwiftParcel.Services.Couriers/scripts/build.sh new file mode 100755 index 0000000..3affad0 --- /dev/null +++ b/SwiftParcel.Services.Couriers/scripts/build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +dotnet build -c release \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/scripts/start.sh b/SwiftParcel.Services.Couriers/scripts/start.sh new file mode 100755 index 0000000..c95535b --- /dev/null +++ b/SwiftParcel.Services.Couriers/scripts/start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=local +cd ../src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api +dotnet run \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/scripts/test.sh b/SwiftParcel.Services.Couriers/scripts/test.sh new file mode 100755 index 0000000..6046c35 --- /dev/null +++ b/SwiftParcel.Services.Couriers/scripts/test.sh @@ -0,0 +1,2 @@ +#!/bin/bash +dotnet test \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api/Program.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api/Program.cs new file mode 100644 index 0000000..d2d700b --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api/Program.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Convey; +using Convey.WebApi; +using SwiftParcel.Services.Couriers.Application; +using SwiftParcel.Services.Couriers.Infrastructure; +using Convey.WebApi.CQRS; +using Convey.Types; +using SwiftParcel.Services.Couriers.Application.Queries; +using SwiftParcel.Services.Couriers.Application.DTO; +using Convey.CQRS.Queries; +using SwiftParcel.Services.Couriers.Application.Commands; +using Convey.Logging; +using Convey.Secrets.Vault; + +namespace SwiftParcel.Services.Couriers.Api +{ + public class Program + { + public static async Task Main(string[] args) + => await WebHost.CreateDefaultBuilder(args) + .ConfigureServices(services => services + .AddConvey() + .AddWebApi() + .AddApplication() + .AddInfrastructure() + .Build()) + .Configure(app => app + .UseInfrastructure() + .UseDispatcherEndpoints(endpoints => endpoints + .Get("", ctx => ctx.Response.WriteAsync(ctx.RequestServices.GetService().Name)) + .Get("couriers/{courierId}") + .Get>("couriers") + .Post("couriers", + afterDispatch: (cmd, ctx) => ctx.Response.Created($"couriers/{cmd.CourierId}")) + .Put("couriers/{courierId}") + .Delete("couriers/{courierId}") + )) + .UseLogging() + .UseVault() + .Build() + .RunAsync(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api/Properties/launchSettings.json b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api/Properties/launchSettings.json new file mode 100644 index 0000000..0d7d0d6 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:14126", + "sslPort": 44381 + } + }, + "profiles": { + "SwiftParcel.Services.Couriers.Api": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5009", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api.csproj b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api.csproj new file mode 100644 index 0000000..f1de15c --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api.csproj @@ -0,0 +1,20 @@ + + + + net6.0 + disable + enable + + + + + + + + + + + + + + diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api/appsettings.Development.json b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api/appsettings.Development.json new file mode 100644 index 0000000..ff66ba6 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api/appsettings.json b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api/appsettings.json new file mode 100644 index 0000000..3744b7c --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Api/SwiftParcel.Services.Couriers.Api/appsettings.json @@ -0,0 +1,194 @@ +{ + "app": { + "name": "SwiftParcel Couriers Service", + "service": "couriers-service", + "version": "1" + }, + "consul": { + "enabled": true, + "url": "http://localhost:8500", + "service": "couriers-service", + "address": "docker.for.win.localhost", + "port": "5009", + "pingEnabled": false, + "pingEndpoint": "ping", + "pingInterval": 3, + "removeAfterInterval": 3 + }, + "fabio": { + "enabled": true, + "url": "http://localhost:9999", + "service": "couriers-service" + }, + "httpClient": { + "type": "fabio", + "retries": 3, + "services": {}, + "requestMasking": { + "enabled": true, + "maskTemplate": "*****" + } + }, + "logger": { + "level": "information", + "excludePaths": ["/", "/ping", "/metrics"], + "excludeProperties": [ + "api_key", + "access_key", + "ApiKey", + "ApiSecret", + "ClientId", + "ClientSecret", + "ConnectionString", + "Password", + "Email", + "Login", + "Secret", + "Token" + ], + "console": { + "enabled": true + }, + "elk": { + "enabled": false, + "url": "http://localhost:9200" + }, + "file": { + "enabled": true, + "path": "logs/logs.txt", + "interval": "day" + }, + "seq": { + "enabled": true, + "url": "http://localhost:5341", + "apiKey": "secret" + }, + "tags": {} + }, + "jaeger": { + "enabled": true, + "serviceName": "couriers", + "udpHost": "localhost", + "udpPort": 6831, + "maxPacketSize": 0, + "sampler": "const", + "excludePaths": ["/", "/ping", "/metrics"] + }, + "jwt": { + "certificate": { + "location": "certs/localhost.cer" + }, + "validIssuer": "swiftparcel", + "validateAudience": false, + "validateIssuer": true, + "validateLifetime": true + }, + "metrics": { + "enabled": true, + "influxEnabled": false, + "prometheusEnabled": true, + "influxUrl": "http://localhost:8086", + "database": "swiftparcel", + "env": "local", + "interval": 5 + }, + "mongo": { + "connectionString": "mongodb+srv://andrii-courier-db-user:piotr-amadeusz-andrii-2023@cluster0.br51nsv.mongodb.net/?retryWrites=true&w=majority", + "database": "couriers-service", + "seed": false + }, + "outbox": { + "enabled": true, + "type": "sequential", + "expiry": 3600, + "intervalMilliseconds": 2000, + "inboxCollection": "inbox", + "outboxCollection": "outbox", + "disableTransactions": true + }, + "rabbitMq": { + "connectionName": "couriers-service", + "retries": 3, + "retryInterval": 2, + "conventionsCasing": "snakeCase", + "logger": { + "enabled": true + }, + "username": "guest", + "password": "guest", + "virtualHost": "/", + "port": 5672, + "hostnames": [ + "localhost" + ], + "requestedConnectionTimeout": "00:00:30", + "requestedHeartbeat": "00:01:00", + "socketReadTimeout": "00:00:30", + "socketWriteTimeout": "00:00:30", + "continuationTimeout": "00:00:20", + "handshakeContinuationTimeout": "00:00:10", + "networkRecoveryInterval": "00:00:05", + "exchange": { + "declare": true, + "durable": true, + "autoDelete": false, + "type": "topic", + "name": "couriers" + }, + "queue": { + "declare": true, + "durable": true, + "exclusive": false, + "autoDelete": false, + "template": "vehicles-service/{{exchange}}.{{message}}" + }, + "context": { + "enabled": true, + "header": "message_context" + }, + "spanContextHeader": "span_context" + }, + "redis": { + "connectionString": "localhost", + "instance": "couriers:" + }, + "swagger": { + "enabled": true, + "reDocEnabled": false, + "name": "v1", + "title": "API", + "version": "v1", + "routePrefix": "docs", + "includeSecurity": true + }, + "vault": { + "enabled": false, + "url": "http://localhost:8200", + "authType": "token", + "token": "secret", + "username": "user", + "password": "secret", + "kv": { + "enabled": true, + "engineVersion": 2, + "mountPoint": "kv", + "path": "couriers-service/settings" + }, + "pki": { + "enabled": true, + "roleName": "couriers-service", + "commonName": "couriers-service.pacco.io" + }, + "lease": { + "mongo": { + "type": "database", + "roleName": "couriers-service", + "enabled": true, + "autoRenewal": true, + "templates": { + "connectionString": "mongodb://{{username}}:{{password}}@localhost:27017" + } + } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Commands/AddCourier.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Commands/AddCourier.cs new file mode 100644 index 0000000..28a7a09 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Commands/AddCourier.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using SwiftParcel.Services.Couriers.Core.Entities; + +namespace SwiftParcel.Services.Couriers.Application.Commands +{ + public class AddCourier : ICommand + { + public Guid CourierId { get; } + + public string Name { get; } + // public string Brand { get; } + // public string Model { get; } + public string Description { get; } + public double PayloadCapacity { get; } + public double LoadingCapacity { get; } + public decimal PricePerService { get; } + public Variants Variants { get; } + + // public AddVehicle(Guid vehicleId, string brand, string model, string description, double payloadCapacity, + // double loadingCapacity, decimal pricePerService, Variants variants) + public AddCourier(Guid courierId, string name, string description, double payloadCapacity, + double loadingCapacity, decimal pricePerService, Variants variants) + { + CourierId = courierId == Guid.Empty ? Guid.NewGuid() : courierId; + Name = name; + Description = description; + PayloadCapacity = payloadCapacity; + LoadingCapacity = loadingCapacity; + PricePerService = pricePerService; + Variants = variants; + } + + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Commands/DeleteCourier.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Commands/DeleteCourier.cs new file mode 100644 index 0000000..6eb5a73 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Commands/DeleteCourier.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.Couriers.Application.Commands +{ + public class DeleteCourier : ICommand + { + public Guid CourierId { get; } + + public DeleteCourier(Guid id) + => CourierId = id; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Commands/UpdateCourier.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Commands/UpdateCourier.cs new file mode 100644 index 0000000..f3949fe --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Commands/UpdateCourier.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using SwiftParcel.Services.Couriers.Core.Entities; + +namespace SwiftParcel.Services.Couriers.Application.Commands +{ + public class UpdateCourier : ICommand + { + public Guid CourierId { get; } + public string Description { get; } + public decimal PricePerService { get; } + public Variants Variants { get; } + + public UpdateCourier(Guid courierId,string description, decimal pricePerService, Variants variants) + { + CourierId = courierId; + Description = description; + PricePerService = pricePerService; + Variants = variants; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/ContractAttribute.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/ContractAttribute.cs new file mode 100644 index 0000000..167d090 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/ContractAttribute.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Couriers.Application +{ + public class ContractAttribute: Attribute + { + + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/DTO/CourierDto.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/DTO/CourierDto.cs new file mode 100644 index 0000000..9125f54 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/DTO/CourierDto.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Couriers.Application.DTO +{ + public class CourierDto + { + public Guid Id { get; set; } + public string Name { get; set; } + // public string Brand { get; set; } + // public string Model { get; set; } + public string Description { get; set; } + public double PayloadCapacity { get; set; } + public double LoadingCapacity { get; set; } + public decimal PricePerService { get; set; } + public IEnumerable Variants { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/CourierAdded.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/CourierAdded.cs new file mode 100644 index 0000000..4280df6 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/CourierAdded.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Couriers.Application.Events +{ + public class CourierAdded : IEvent + { + public Guid CourierId { get; } + + public CourierAdded(Guid id) + => CourierId = id; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/CourierDeleted.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/CourierDeleted.cs new file mode 100644 index 0000000..0348a79 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/CourierDeleted.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Couriers.Application.Events +{ + public class CourierDeleted : IEvent + { + public Guid CourierId { get; } + + public CourierDeleted(Guid id) + => CourierId = id; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/CourierUpdated.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/CourierUpdated.cs new file mode 100644 index 0000000..b8ccaac --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/CourierUpdated.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Couriers.Application.Events +{ + public class CourierUpdated : IEvent + { + public Guid CourierId { get; } + + public CourierUpdated(Guid id) + => CourierId = id; + } +} diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/Rejected/AddCourierRejected.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/Rejected/AddCourierRejected.cs new file mode 100644 index 0000000..ab71de8 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/Rejected/AddCourierRejected.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Couriers.Application.Events.Rejected +{ + public class AddCourierRejected : IRejectedEvent + { + public Guid CourierId { get; } + public string Reason { get; } + public string Code { get; } + + public AddCourierRejected(Guid courierId, string reason, string code) + { + CourierId = courierId; + Reason = reason; + Code = code; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/Rejected/DeleteCourierRejected.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/Rejected/DeleteCourierRejected.cs new file mode 100644 index 0000000..70f322a --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/Rejected/DeleteCourierRejected.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Couriers.Application.Events.Rejected +{ + public class DeleteCourierRejected : IRejectedEvent + { + public Guid CourierId { get; } + public string Reason { get; } + public string Code { get; } + + public DeleteCourierRejected(Guid courierId, string reason, string code) + { + CourierId = courierId; + Reason = reason; + Code = code; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/Rejected/UpdateCourierRejected.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/Rejected/UpdateCourierRejected.cs new file mode 100644 index 0000000..c6ae508 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Events/Rejected/UpdateCourierRejected.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Couriers.Application.Events.Rejected +{ + public class UpdateCourierRejected : IRejectedEvent + { + public Guid CourierId { get; } + public string Reason { get; } + public string Code { get; } + + public UpdateCourierRejected(Guid courierId, string reason, string code) + { + CourierId = courierId; + Reason = reason; + Code = code; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Exceptions/AppException.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Exceptions/AppException.cs new file mode 100644 index 0000000..703b580 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Exceptions/AppException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Couriers.Application.Exceptions +{ + public class AppException: Exception + { + public virtual string Code { get; } + + protected AppException(string message) : base(message) + { + + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Exceptions/CourierNotFoundException.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Exceptions/CourierNotFoundException.cs new file mode 100644 index 0000000..4329dc0 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Exceptions/CourierNotFoundException.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Couriers.Application.Exceptions +{ + public class CourierNotFoundException : AppException + { + public override string Code { get; } = "courier_not_found"; + public Guid Id { get; } + + public CourierNotFoundException(Guid id) : base($"Courier not found: {id}.") + { + Id = id; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Extensions.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Extensions.cs new file mode 100644 index 0000000..6e83c90 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Extensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey; +using Convey.CQRS.Commands; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Couriers.Application +{ + public static class Extensions + { + public static IConveyBuilder AddApplication(this IConveyBuilder builder) + => builder + .AddCommandHandlers() + .AddEventHandlers() + .AddInMemoryCommandDispatcher() + .AddInMemoryEventDispatcher(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/IAppContext.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/IAppContext.cs new file mode 100644 index 0000000..8b2c2bb --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/IAppContext.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Couriers.Application +{ + public class IAppContext + { + string RequestId { get; } + IIdentityContext Identity { get; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/IIdentityContext.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/IIdentityContext.cs new file mode 100644 index 0000000..eae0dc4 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/IIdentityContext.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Couriers.Application +{ + public interface IIdentityContext + { + Guid Id { get; } + string Role { get; } + bool IsAuthenticated { get; } + bool IsOfficeWorker { get; } + bool IsCourier { get; } + IDictionary Claims { get; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Queries/GetCourier.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Queries/GetCourier.cs new file mode 100644 index 0000000..b02b31a --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Queries/GetCourier.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using SwiftParcel.Services.Couriers.Application.DTO; + +namespace SwiftParcel.Services.Couriers.Application.Queries +{ + public class GetCourier : IQuery + { + public Guid CourierId { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Queries/SearchCouriers.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Queries/SearchCouriers.cs new file mode 100644 index 0000000..18b4514 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Queries/SearchCouriers.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using SwiftParcel.Services.Couriers.Application.DTO; +using SwiftParcel.Services.Couriers.Core.Entities; + +namespace SwiftParcel.Services.Couriers.Application.Queries +{ + public class SearchCouriers : PagedQueryBase, IQuery> + { + public double PayloadCapacity { get; set; } + public double LoadingCapacity { get; set; } + public Variants Variants { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Services/IMessageBroker.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Services/IMessageBroker.cs new file mode 100644 index 0000000..34fffc7 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/Services/IMessageBroker.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Couriers.Application.Services +{ + public interface IMessageBroker + { + Task PublishAsync(params IEvent[] events); + Task PublishAsync(IEnumerable events); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application.csproj b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application.csproj new file mode 100644 index 0000000..f7c976c --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application/SwiftParcel.Services.Couriers.Application.csproj @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + net6.0 + enable + disable + + + diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Entities/Courier.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Entities/Courier.cs new file mode 100644 index 0000000..abbc74d --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Entities/Courier.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Couriers.Core.Exceptions; + +namespace SwiftParcel.Services.Couriers.Core.Entities +{ + public class Courier + { + public Guid Id { get; protected set; } + + public string Name { get; protected set; } + + // public string Brand { get; protected set; } + // public string Model { get; protected set; } + public string Description { get; protected set; } + public double PayloadCapacity { get; protected set; } + public double LoadingCapacity { get; protected set; } + public decimal PricePerService { get; protected set; } + public Variants Variants { get; protected set; } + + // public Courier(Guid id, string brand, string model, string description, double payloadCapacity, + // double loadingCapacity, decimal pricePerService) + public Courier(Guid id, string name, string description, double payloadCapacity, + double loadingCapacity, decimal pricePerService) + { + Id = id; + Name = name; + ChangeDescription(description); + PayloadCapacity = payloadCapacity > 0 ? payloadCapacity : throw new InvalidCourierCapacity(payloadCapacity); + LoadingCapacity = loadingCapacity > 0 ? loadingCapacity : throw new InvalidCourierCapacity(loadingCapacity); + ChangePricePerService(pricePerService); + AddVariants(Variants.Standard); + } + + // public Courier(Guid id, string brand, string model, string description, double payloadCapacity, + // double loadingCapacity, decimal pricePerService, params Variants[] variants) + // : this(id, brand, model, description, payloadCapacity, loadingCapacity, pricePerService) + public Courier(Guid id, string name, string description, double payloadCapacity, + double loadingCapacity, decimal pricePerService, params Variants[] variants) + : this(id, name, description, payloadCapacity, loadingCapacity, pricePerService) + { + AddVariants(variants); + } + + public void ChangeDescription(string description) + { + if (string.IsNullOrEmpty(description)) + { + throw new InvalidCourierDescriptionException(description); + } + + Description = description; + } + + public void ChangePricePerService(decimal pricePerService) + { + if (pricePerService <= 0) + { + throw new InvalidCourierPricePerServiceException(pricePerService); + } + + PricePerService = pricePerService; + } + + public void ChangeVariants(Variants variants) + => Variants = variants; + + public void AddVariants(params Variants[] variants) + { + foreach (var variant in variants) + { + Variants |= variant; + } + } + + public void RemoveVariants(params Variants[] variants) + { + foreach (var variant in variants) + { + Variants &= ~variant; + } + } + } +} diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Entities/Variants.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Entities/Variants.cs new file mode 100644 index 0000000..95929a5 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Entities/Variants.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Couriers.Core.Entities +{ + public enum Variants + { + Standard = 1 << 0, + Chemistry = 1 << 1, + Weapon = 1 << 2, + Animal = 1 << 3, + Organ = 1 << 4 + } + +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Exceptions/DomainException.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Exceptions/DomainException.cs new file mode 100644 index 0000000..883b5da --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Exceptions/DomainException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Couriers.Core.Exceptions +{ + public abstract class DomainException : Exception + { + public virtual string Code { get; } + + protected DomainException(string message) : base(message) + { + + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Exceptions/InvalidCourierCapacity.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Exceptions/InvalidCourierCapacity.cs new file mode 100644 index 0000000..c849c7c --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Exceptions/InvalidCourierCapacity.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Couriers.Core.Exceptions +{ + public class InvalidCourierCapacity : DomainException + { + public override string Code { get; } = "invalid_courier_capacity"; + + public InvalidCourierCapacity(double capacity) + : base($"Courier capacity is invalid: {capacity}.") + { + + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Exceptions/InvalidCourierDescriptionException.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Exceptions/InvalidCourierDescriptionException.cs new file mode 100644 index 0000000..a0e322c --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Exceptions/InvalidCourierDescriptionException.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Couriers.Core.Exceptions +{ + public class InvalidCourierDescriptionException: DomainException + { + public override string Code { get; } = "invalid_courier_description"; + + public InvalidCourierDescriptionException(string description) + : base($"Courier description is invalid: {description}.") + { + + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Exceptions/InvalidCourierPricePerServiceException.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Exceptions/InvalidCourierPricePerServiceException.cs new file mode 100644 index 0000000..1bdfd0c --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Exceptions/InvalidCourierPricePerServiceException.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Couriers.Core.Exceptions +{ + public class InvalidCourierPricePerServiceException : DomainException + { + public override string Code { get; } = "invalid_courier_price_per_service"; + + public InvalidCourierPricePerServiceException(decimal pricePerService) + : base($"Courier price per service is invalid: {pricePerService}.") + { + + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Repositories/ICourierRepository.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Repositories/ICourierRepository.cs new file mode 100644 index 0000000..b3a31e7 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/Repositories/ICourierRepository.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Couriers.Core.Entities; + +namespace SwiftParcel.Services.Couriers.Core.Repositories +{ + public interface ICourierRepository + { + Task GetAsync(Guid id); + Task AddAsync(Courier courier); + Task UpdateAsync(Courier courier); + Task DeleteAsync(Courier courier); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core.csproj b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core.csproj new file mode 100644 index 0000000..bafd05b --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core/SwiftParcel.Services.Couriers.Core.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Contexts/AppContext.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Contexts/AppContext.cs new file mode 100644 index 0000000..6e36958 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Contexts/AppContext.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Couriers.Application; + +namespace SwiftParcel.Services.Couriers.Infrastructure.Contexts +{ + internal class AppContext: IAppContext + { + public string RequestId { get; } + public IIdentityContext Identity { get; } + + internal AppContext() : this(Guid.NewGuid().ToString("N"), IdentityContext.Empty) + { + } + + internal AppContext(CorrelationContext context) : this(context.CorrelationId, + context.User is null ? IdentityContext.Empty : new IdentityContext(context.User)) + { + } + + internal AppContext(string requestId, IIdentityContext identity) + { + RequestId = requestId; + Identity = identity; + } + + internal static IAppContext Empty => new AppContext(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Contexts/AppContextFactory.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Contexts/AppContextFactory.cs new file mode 100644 index 0000000..f84e79c --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Contexts/AppContextFactory.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.MessageBrokers; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using SwiftParcel.Services.Couriers.Application; + +namespace SwiftParcel.Services.Couriers.Infrastructure.Contexts +{ + internal sealed class AppContextFactory: IAppContextFactory + { + private readonly ICorrelationContextAccessor _contextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + + public AppContextFactory(ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor) + { + _contextAccessor = contextAccessor; + _httpContextAccessor = httpContextAccessor; + } + + public IAppContext Create() + { + if (_contextAccessor.CorrelationContext is {}) + { + var payload = JsonConvert.SerializeObject(_contextAccessor.CorrelationContext); + + return string.IsNullOrWhiteSpace(payload) + ? AppContext.Empty + : new AppContext(JsonConvert.DeserializeObject(payload)); + } + + var context = _httpContextAccessor.GetCorrelationContext(); + + return context is null ? AppContext.Empty : new AppContext(context); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Contexts/CorrelationContext.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Contexts/CorrelationContext.cs new file mode 100644 index 0000000..a4587c1 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Contexts/CorrelationContext.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Couriers.Infrastructure.Contexts +{ + internal class CorrelationContext + { + public string CorrelationId { get; set; } + public string SpanContext { get; set; } + public UserContext User { get; set; } + public string ResourceId { get; set; } + public string TraceId { get; set; } + public string ConnectionId { get; set; } + public string Name { get; set; } + public DateTime CreatedAt { get; set; } + + public class UserContext + { + public string Id { get; set; } + public bool IsAuthenticated { get; set; } + public string Role { get; set; } + public IDictionary Claims { get; set; } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Contexts/IdentityContext.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Contexts/IdentityContext.cs new file mode 100644 index 0000000..28a0d7b --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Contexts/IdentityContext.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using System.Threading.Tasks; +using SwiftParcel.Services.Couriers.Application; + +namespace SwiftParcel.Services.Couriers.Infrastructure.Contexts +{ + public class IdentityContext : IIdentityContext + { + public Guid Id { get; } + public string Role { get; } = string.Empty; + public bool IsAuthenticated { get; } + public bool IsOfficeWorker{ get; } + public bool IsCourier{ get; } + public IDictionary Claims { get; } = new Dictionary(); + + internal IdentityContext() + { + } + + internal IdentityContext(CorrelationContext.UserContext context) + : this(context.Id, context.Role, context.IsAuthenticated, context.Claims) + { + } + + internal IdentityContext(string id, string role, bool isAuthenticated, IDictionary claims) + { + Id = Guid.TryParse(id, out var userId) ? userId : Guid.Empty; + Role = role ?? string.Empty; + IsAuthenticated = isAuthenticated; + IsOfficeWorker = Role.Equals("officeworker", StringComparison.InvariantCultureIgnoreCase); + IsOfficeWorker = Role.Equals("courier", StringComparison.InvariantCultureIgnoreCase); + Claims = claims ?? new Dictionary(); + } + + internal static IIdentityContext Empty => new IdentityContext(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs new file mode 100644 index 0000000..31d6459 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; + +namespace SwiftParcel.Services.Couriers.Infrastructure.Decorators +{ + internal sealed class OutboxCommandHandlerDecorator : ICommandHandler + where TCommand : class, ICommand + { + private readonly ICommandHandler _handler; + private readonly IMessageOutbox _outbox; + private readonly string _messageId; + private readonly bool _enabled; + + public OutboxCommandHandlerDecorator(ICommandHandler handler, IMessageOutbox outbox, + OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) + { + _handler = handler; + _outbox = outbox; + _enabled = outboxOptions.Enabled; + + var messageProperties = messagePropertiesAccessor.MessageProperties; + _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId) + ? Guid.NewGuid().ToString("N") + : messageProperties.MessageId; + } + + public Task HandleAsync(TCommand command, CancellationToken cancellationToken) + => _enabled + ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(command)) + : _handler.HandleAsync(command); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs new file mode 100644 index 0000000..6840cf3 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; + +namespace SwiftParcel.Services.Couriers.Infrastructure.Decorators +{ + internal sealed class OutboxEventHandlerDecorator : IEventHandler + where TEvent : class, IEvent + { + private readonly IEventHandler _handler; + private readonly IMessageOutbox _outbox; + private readonly string _messageId; + private readonly bool _enabled; + + public OutboxEventHandlerDecorator(IEventHandler handler, IMessageOutbox outbox, + OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) + { + _handler = handler; + _outbox = outbox; + _enabled = outboxOptions.Enabled; + + var messageProperties = messagePropertiesAccessor.MessageProperties; + _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId) + ? Guid.NewGuid().ToString("N") + : messageProperties.MessageId; + } + + public Task HandleAsync(TEvent @event, CancellationToken cancellationToken) + => _enabled + ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(@event)) + : _handler.HandleAsync(@event); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Exceptions/ExceptionToMessageMapper.cs new file mode 100644 index 0000000..5e8ad77 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.MessageBrokers.RabbitMQ; +using SwiftParcel.Services.Couriers.Application.Commands; +using SwiftParcel.Services.Couriers.Application.Events.Rejected; +using SwiftParcel.Services.Couriers.Application.Exceptions; +using SwiftParcel.Services.Couriers.Core.Exceptions; + +namespace SwiftParcel.Services.Couriers.Infrastructure.Exceptions +{ + public class ExceptionToMessageMapper: IExceptionToMessageMapper + { + public object Map(Exception exception, object message) + => exception switch + { + InvalidCourierCapacity ex => message switch + { + AddCourier command => new AddCourierRejected(command.CourierId, ex.Message,ex.Code), + UpdateCourier command => new UpdateCourierRejected(command.CourierId, ex.Message, ex.Code), + _ => null, + }, + InvalidCourierDescriptionException ex => message switch + { + AddCourier command => new AddCourierRejected(command.CourierId, ex.Message,ex.Code), + UpdateCourier command => new UpdateCourierRejected(command.CourierId, ex.Message, ex.Code), + _ => null, + }, + InvalidCourierPricePerServiceException ex => message switch + { + AddCourier command => new AddCourierRejected(command.CourierId, ex.Message,ex.Code), + UpdateCourier command => new UpdateCourierRejected(command.CourierId, ex.Message, ex.Code), + _ => null, + }, + CourierNotFoundException ex => message switch + { + UpdateCourier command => new UpdateCourierRejected(command.CourierId, ex.Message,ex.Code), + DeleteCourier command => new DeleteCourierRejected(command.CourierId, ex.Message, ex.Code), + _ => null, + }, + _ => null, + }; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Exceptions/ExceptionToResponseMapper.cs new file mode 100644 index 0000000..f4135d9 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Convey; +using Convey.WebApi.Exceptions; +using SwiftParcel.Services.Couriers.Application.Exceptions; +using SwiftParcel.Services.Couriers.Core.Exceptions; + +namespace SwiftParcel.Services.Couriers.Infrastructure.Exceptions +{ + public class ExceptionToResponseMapper: IExceptionToResponseMapper + { + private static readonly ConcurrentDictionary Codes = new ConcurrentDictionary(); + + public ExceptionResponse Map(Exception exception) + => exception switch + { + DomainException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message}, + HttpStatusCode.BadRequest), + AppException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message}, + HttpStatusCode.BadRequest), + _ => new ExceptionResponse(new {code = "error", reason = "There was an error."}, + HttpStatusCode.BadRequest) + }; + + private static string GetCode(Exception exception) + { + var type = exception.GetType(); + if (Codes.TryGetValue(type, out var code)) + { + return code; + } + + var exceptionCode = exception switch + { + DomainException domainException when !string.IsNullOrWhiteSpace(domainException.Code) => domainException + .Code, + AppException appException when !string.IsNullOrWhiteSpace(appException.Code) => appException.Code, + _ => exception.GetType().Name.Underscore().Replace("_exception", string.Empty) + }; + + Codes.TryAdd(type, exceptionCode); + + return exceptionCode; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Extensions.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Extensions.cs new file mode 100644 index 0000000..7b2470d --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Extensions.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Convey; +using Convey.CQRS.Commands; +using Convey.CQRS.Events; +using Convey.CQRS.Queries; +using Convey.Discovery.Consul; +using Convey.Docs.Swagger; +using Convey.HTTP; +using Convey.LoadBalancing.Fabio; +using Convey.MessageBrokers; +using Convey.MessageBrokers.CQRS; +using Convey.MessageBrokers.Outbox; +using Convey.MessageBrokers.Outbox.Mongo; +using Convey.MessageBrokers.RabbitMQ; +using Convey.Metrics.AppMetrics; +using Convey.Persistence.MongoDB; +using Convey.Persistence.Redis; +using Convey.Security; +using Convey.Tracing.Jaeger; +using Convey.Tracing.Jaeger.RabbitMQ; +using Convey.WebApi; +using Convey.WebApi.CQRS; +using Convey.WebApi.Swagger; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using SwiftParcel.Services.Couriers.Application; +using SwiftParcel.Services.Couriers.Application.Commands; +using SwiftParcel.Services.Couriers.Application.Services; +using SwiftParcel.Services.Couriers.Core.Repositories; +using SwiftParcel.Services.Couriers.Infrastructure.Contexts; +using SwiftParcel.Services.Couriers.Infrastructure.Decorators; +using SwiftParcel.Services.Couriers.Infrastructure.Exceptions; +using SwiftParcel.Services.Couriers.Infrastructure.Logging; +using SwiftParcel.Services.Couriers.Infrastructure.Mongo.Documents; +using SwiftParcel.Services.Couriers.Infrastructure.Mongo.Repositories; +using SwiftParcel.Services.Couriers.Infrastructure.Services; + +namespace SwiftParcel.Services.Couriers.Infrastructure +{ + public static class Extensions + { + public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + { + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(ctx => ctx.GetRequiredService().Create()); + builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>)); + builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>)); + + return builder + .AddErrorHandler() + .AddQueryHandlers() + .AddInMemoryQueryDispatcher() + .AddHttpClient() + .AddConsul() + .AddFabio() + .AddRabbitMq(plugins: p => p.AddJaegerRabbitMqPlugin()) + .AddMessageOutbox(o => o.AddMongo()) + .AddExceptionToMessageMapper() + .AddMongo() + .AddRedis() + .AddMetrics() + .AddJaeger() + .AddMongo() + .AddHandlersLogging() + .AddMongoRepository("couriers") + .AddWebApiSwaggerDocs() + .AddSecurity(); + } + + public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app) + { + app.UseErrorHandler() + .UseSwaggerDocs() + .UseJaeger() + .UseConvey() + .UsePublicContracts() + .UseMetrics() + .UseRabbitMq() + .SubscribeCommand() + .SubscribeCommand() + .SubscribeCommand(); + + return app; + } + + internal static CorrelationContext GetCorrelationContext(this IHttpContextAccessor accessor) + => accessor.HttpContext?.Request.Headers.TryGetValue("Correlation-Context", out var json) is true + ? JsonConvert.DeserializeObject(json.FirstOrDefault()) + : null; + + internal static IDictionary GetHeadersToForward(this IMessageProperties messageProperties) + { + const string sagaHeader = "Saga"; + if (messageProperties?.Headers is null || !messageProperties.Headers.TryGetValue(sagaHeader, out var saga)) + { + return null; + } + + return saga is null + ? null + : new Dictionary + { + [sagaHeader] = saga + }; + } + + internal static string GetSpanContext(this IMessageProperties messageProperties, string header) + { + if (messageProperties is null) + { + return string.Empty; + } + + if (messageProperties.Headers.TryGetValue(header, out var span) && span is byte[] spanBytes) + { + return Encoding.UTF8.GetString(spanBytes); + } + + return string.Empty; + } + } + +} diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/IAppContextFactory.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/IAppContextFactory.cs new file mode 100644 index 0000000..3ade6c2 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/IAppContextFactory.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Couriers.Application; + +namespace SwiftParcel.Services.Couriers.Infrastructure +{ + public interface IAppContextFactory + { + IAppContext Create(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Logging/Extensions.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Logging/Extensions.cs new file mode 100644 index 0000000..a0e361a --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Logging/Extensions.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey; +using Convey.Logging.CQRS; +using Microsoft.Extensions.DependencyInjection; +using SwiftParcel.Services.Couriers.Application.Commands; + +namespace SwiftParcel.Services.Couriers.Infrastructure.Logging +{ + internal static class Extensions + { + public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + { + var assembly = typeof(AddCourier).Assembly; + + builder.Services.AddSingleton(new MessageToLogTemplateMapper()); + + return builder + .AddCommandHandlersLogging(assembly) + .AddEventHandlersLogging(assembly); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Logging/MessageToLogTemplateMapper.cs new file mode 100644 index 0000000..110e503 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.Logging.CQRS; +using SwiftParcel.Services.Couriers.Application.Commands; + +namespace SwiftParcel.Services.Couriers.Infrastructure.Logging +{ + internal sealed class MessageToLogTemplateMapper : IMessageToLogTemplateMapper + { + private static IReadOnlyDictionary MessageTemplates + => new Dictionary + { + { + typeof(AddCourier), + new HandlerLogTemplate + { + After = "Added a courier with id: {CourierId}." + } + }, + { + typeof(DeleteCourier), + new HandlerLogTemplate + { + After = "Deleted a courier with id: {CourierId}." + } + }, + { + typeof(UpdateCourier), + new HandlerLogTemplate + { + After = "Updated a courier with id: {CourierId}." + } + }, + }; + + public HandlerLogTemplate Map(TMessage message) where TMessage : class + { + var key = message.GetType(); + return MessageTemplates.TryGetValue(key, out var template) ? template : null; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Mongo/Documents/CourierDocument.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Mongo/Documents/CourierDocument.cs new file mode 100644 index 0000000..cf1ed3b --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Mongo/Documents/CourierDocument.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Convey.Types; +using SwiftParcel.Services.Couriers.Core.Entities; + +namespace SwiftParcel.Services.Couriers.Infrastructure.Mongo.Documents +{ + internal class CourierDocument : IIdentifiable + { + public Guid Id { get; set; } + // public string Brand { get; set; } + // public string Model { get; set; } + + public string Name { get; set; } + public string Description { get; set; } + public double PayloadCapacity { get; set; } + public double LoadingCapacity { get; set; } + public decimal PricePerService { get; set; } + public Variants Variants { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Mongo/Documents/Extensions.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Mongo/Documents/Extensions.cs new file mode 100644 index 0000000..e35cb90 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Mongo/Documents/Extensions.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Couriers.Application.DTO; +using SwiftParcel.Services.Couriers.Core.Entities; + +namespace SwiftParcel.Services.Couriers.Infrastructure.Mongo.Documents +{ + internal static class Extensions + { + public static Courier AsEntity(this CourierDocument document) + => document is null? null : new Courier( + document.Id, + // document.Brand, + // document.Model, + document.Name, + //document.LastName, + document.Description, + document.PayloadCapacity, + document.LoadingCapacity, + document.PricePerService, + document.Variants); + + public static async Task AsEntityAsync(this Task task) + => (await task).AsEntity(); + + public static CourierDocument AsDocument(this Courier entity) + => new CourierDocument + { + Id = entity.Id, + // Brand = entity.Brand, + // Model = entity.Model, + Name = entity.Name, + // LastName = entity.LastName, + Description = entity.Description, + PayloadCapacity = entity.PayloadCapacity, + LoadingCapacity = entity.LoadingCapacity, + PricePerService = entity.PricePerService, + Variants = entity.Variants + }; + + public static async Task AsDocumentAsync(this Task task) + => (await task).AsDocument(); + + public static CourierDto AsDto(this CourierDocument document) + => new CourierDto + { + Id = document.Id, + // Brand = document.Brand, + // Model = document.Model, + Name = document.Name, + Description = document.Description, + PayloadCapacity = document.PayloadCapacity, + LoadingCapacity = document.LoadingCapacity, + PricePerService = document.PricePerService, + Variants = document.Variants.ToString().Split(',') + }; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Mongo/Queries/GetCourierHandler.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Mongo/Queries/GetCourierHandler.cs new file mode 100644 index 0000000..b09b19c --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Mongo/Queries/GetCourierHandler.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Couriers.Application.DTO; +using SwiftParcel.Services.Couriers.Application.Queries; +using SwiftParcel.Services.Couriers.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Couriers.Infrastructure.Mongo.Queries +{ + internal sealed class GetCourierHandler : IQueryHandler + { + private readonly IMongoRepository _repository; + + public GetCourierHandler(IMongoRepository repository) + => _repository = repository; + + public async Task HandleAsync(GetCourier query, CancellationToken cancellationToken) + { + var document = await _repository.GetAsync(v => v.Id == query.CourierId); + return document?.AsDto(); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Mongo/Queries/SearchCouriersHandler.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Mongo/Queries/SearchCouriersHandler.cs new file mode 100644 index 0000000..edb5f93 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Mongo/Queries/SearchCouriersHandler.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Couriers.Application.DTO; +using SwiftParcel.Services.Couriers.Application.Queries; +using SwiftParcel.Services.Couriers.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Couriers.Infrastructure.Mongo.Queries +{ + internal sealed class SearchCouriersHandler : IQueryHandler> + { + private readonly IMongoRepository _repository; + + public SearchCouriersHandler(IMongoRepository repository) + => _repository = repository; + + public async Task> HandleAsync(SearchCouriers query, CancellationToken cancellationToken) + { + PagedResult pagedResult; + if (query.PayloadCapacity <= 0 && query.LoadingCapacity <= 0 && query.Variants <= 0) + { + pagedResult = await _repository.BrowseAsync(_ => true, query); + } + else + { + pagedResult = await _repository.BrowseAsync(v => v.PayloadCapacity >= query.PayloadCapacity + && v.LoadingCapacity >= query.LoadingCapacity && + v.Variants == query.Variants, query); + } + + + return pagedResult?.Map(d => d.AsDto()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Mongo/Repositories/CouriersMongoRepository.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Mongo/Repositories/CouriersMongoRepository.cs new file mode 100644 index 0000000..bc7a44c --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Mongo/Repositories/CouriersMongoRepository.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MongoDB.Driver; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Couriers.Core.Entities; +using SwiftParcel.Services.Couriers.Core.Repositories; +using SwiftParcel.Services.Couriers.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Couriers.Infrastructure.Mongo.Repositories +{ + internal class CouriersMongoRepository : ICourierRepository + { + private readonly IMongoRepository _repository; + + public CouriersMongoRepository(IMongoRepository repository) + => _repository = repository; + + public Task GetAsync(Guid id) + => _repository + .GetAsync(id) + .AsEntityAsync(); + + public Task AddAsync(Courier courier) + => _repository.AddAsync(courier.AsDocument()); + + public Task UpdateAsync(Courier courier) + => _repository.UpdateAsync(courier.AsDocument()); + + public Task DeleteAsync(Courier courier) + => _repository.DeleteAsync(courier.Id); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Services/MessageBroker.cs b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Services/MessageBroker.cs new file mode 100644 index 0000000..0cdb223 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/Services/MessageBroker.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; +using Convey.MessageBrokers.RabbitMQ; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using OpenTracing; +using SwiftParcel.Services.Couriers.Application.Services; + +namespace SwiftParcel.Services.Couriers.Infrastructure.Services +{ + internal sealed class MessageBroker + : IMessageBroker + { + private const string DefaultSpanContextHeader = "span_context"; + private readonly IBusPublisher _busPublisher; + private readonly IMessageOutbox _outbox; + private readonly ICorrelationContextAccessor _contextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IMessagePropertiesAccessor _messagePropertiesAccessor; + private readonly ITracer _tracer; + private readonly ILogger _logger; + private readonly string _spanContextHeader; + + public MessageBroker(IBusPublisher busPublisher, IMessageOutbox outbox, + ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor, + IMessagePropertiesAccessor messagePropertiesAccessor, RabbitMqOptions options, ITracer tracer, + ILogger logger) + { + _busPublisher = busPublisher; + _outbox = outbox; + _contextAccessor = contextAccessor; + _httpContextAccessor = httpContextAccessor; + _messagePropertiesAccessor = messagePropertiesAccessor; + _tracer = tracer; + _logger = logger; + _spanContextHeader = string.IsNullOrWhiteSpace(options.SpanContextHeader) + ? DefaultSpanContextHeader + : options.SpanContextHeader; + } + + public Task PublishAsync(params IEvent[] events) => PublishAsync(events?.AsEnumerable()); + + public async Task PublishAsync(IEnumerable events) + { + if (events is null) + { + return; + } + + var messageProperties = _messagePropertiesAccessor.MessageProperties; + var originatedMessageId = messageProperties?.MessageId; + var correlationId = messageProperties?.CorrelationId; + var spanContext = messageProperties?.GetSpanContext(_spanContextHeader); + if (string.IsNullOrWhiteSpace(spanContext)) + { + spanContext = _tracer.ActiveSpan is null ? string.Empty : _tracer.ActiveSpan.Context.ToString(); + } + + var headers = messageProperties.GetHeadersToForward(); + var correlationContext = _contextAccessor.CorrelationContext ?? + _httpContextAccessor.GetCorrelationContext(); + + foreach (var @event in events) + { + if (@event is null) + { + continue; + } + + var messageId = Guid.NewGuid().ToString("N"); + _logger.LogTrace($"Publishing integration event: {@event.GetType().Name} [id: '{messageId}']."); + if (_outbox.Enabled) + { + await _outbox.SendAsync(@event, originatedMessageId, messageId, correlationId, spanContext, + correlationContext, headers); + continue; + } + + await _busPublisher.PublishAsync(@event, messageId, correlationId, spanContext, correlationContext, + headers); + } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure.csproj b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure.csproj new file mode 100644 index 0000000..58838f7 --- /dev/null +++ b/SwiftParcel.Services.Couriers/src/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure/SwiftParcel.Services.Couriers.Infrastructure.csproj @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + net6.0 + enable + disable + + + diff --git a/SwiftParcel.Services.Customers/SwiftParcel.Services.Customers.sln b/SwiftParcel.Services.Customers/SwiftParcel.Services.Customers.sln new file mode 100644 index 0000000..6ce9e3e --- /dev/null +++ b/SwiftParcel.Services.Customers/SwiftParcel.Services.Customers.sln @@ -0,0 +1,60 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{027FF04D-1882-4EDC-81EF-86829FD874C3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Customers.Api", "SwiftParcel.Services.Customers.Api", "{E0B5A2BA-83DA-4505-8808-44C020E92B33}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Customers.Api", "src\SwiftParcel.Services.Customers.Api\SwiftParcel.Services.Customers.Api\SwiftParcel.Services.Customers.Api.csproj", "{AACD57C5-60E1-42D9-9BAC-41CB0CBB8DCF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Customers.Application", "SwiftParcel.Services.Customers.Application", "{4D42EB06-3736-463E-9D74-83B57149B0D0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Customers.Application", "src\SwiftParcel.Services.Customers.Application\SwiftParcel.Services.Customers.Application\SwiftParcel.Services.Customers.Application.csproj", "{61FE9F60-61E6-43C2-824D-FCCD7A2B917C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Customers.Core", "SwiftParcel.Services.Customers.Core", "{831EEA57-FD6D-43DF-AC3A-35A2648C02BE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Customers.Core", "src\SwiftParcel.Services.Customers.Core\SwiftParcel.Services.Customers.Core\SwiftParcel.Services.Customers.Core.csproj", "{15213ED3-AC0E-4028-84A5-8A836C6FE382}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Customers.Infrastructure", "SwiftParcel.Services.Customers.Infrastructure", "{84AAFC87-A92F-46CC-9CB7-901E92654CA7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Customers.Infrastructure", "src\SwiftParcel.Services.Customers.Infrastructure\SwiftParcel.Services.Customers.Infrastructure\SwiftParcel.Services.Customers.Infrastructure.csproj", "{652863F5-9DE5-418A-BB74-D620A97D6FC4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AACD57C5-60E1-42D9-9BAC-41CB0CBB8DCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AACD57C5-60E1-42D9-9BAC-41CB0CBB8DCF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AACD57C5-60E1-42D9-9BAC-41CB0CBB8DCF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AACD57C5-60E1-42D9-9BAC-41CB0CBB8DCF}.Release|Any CPU.Build.0 = Release|Any CPU + {61FE9F60-61E6-43C2-824D-FCCD7A2B917C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {61FE9F60-61E6-43C2-824D-FCCD7A2B917C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {61FE9F60-61E6-43C2-824D-FCCD7A2B917C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {61FE9F60-61E6-43C2-824D-FCCD7A2B917C}.Release|Any CPU.Build.0 = Release|Any CPU + {15213ED3-AC0E-4028-84A5-8A836C6FE382}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {15213ED3-AC0E-4028-84A5-8A836C6FE382}.Debug|Any CPU.Build.0 = Debug|Any CPU + {15213ED3-AC0E-4028-84A5-8A836C6FE382}.Release|Any CPU.ActiveCfg = Release|Any CPU + {15213ED3-AC0E-4028-84A5-8A836C6FE382}.Release|Any CPU.Build.0 = Release|Any CPU + {652863F5-9DE5-418A-BB74-D620A97D6FC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {652863F5-9DE5-418A-BB74-D620A97D6FC4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {652863F5-9DE5-418A-BB74-D620A97D6FC4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {652863F5-9DE5-418A-BB74-D620A97D6FC4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {E0B5A2BA-83DA-4505-8808-44C020E92B33} = {027FF04D-1882-4EDC-81EF-86829FD874C3} + {AACD57C5-60E1-42D9-9BAC-41CB0CBB8DCF} = {E0B5A2BA-83DA-4505-8808-44C020E92B33} + {4D42EB06-3736-463E-9D74-83B57149B0D0} = {027FF04D-1882-4EDC-81EF-86829FD874C3} + {61FE9F60-61E6-43C2-824D-FCCD7A2B917C} = {4D42EB06-3736-463E-9D74-83B57149B0D0} + {831EEA57-FD6D-43DF-AC3A-35A2648C02BE} = {027FF04D-1882-4EDC-81EF-86829FD874C3} + {15213ED3-AC0E-4028-84A5-8A836C6FE382} = {831EEA57-FD6D-43DF-AC3A-35A2648C02BE} + {84AAFC87-A92F-46CC-9CB7-901E92654CA7} = {027FF04D-1882-4EDC-81EF-86829FD874C3} + {652863F5-9DE5-418A-BB74-D620A97D6FC4} = {84AAFC87-A92F-46CC-9CB7-901E92654CA7} + EndGlobalSection +EndGlobal diff --git a/SwiftParcel.Services.Customers/scripts/build.sh b/SwiftParcel.Services.Customers/scripts/build.sh new file mode 100755 index 0000000..533c43e --- /dev/null +++ b/SwiftParcel.Services.Customers/scripts/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cd ../src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api +dotnet build -c release diff --git a/SwiftParcel.Services.Customers/scripts/start.sh b/SwiftParcel.Services.Customers/scripts/start.sh new file mode 100755 index 0000000..316a320 --- /dev/null +++ b/SwiftParcel.Services.Customers/scripts/start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=Development +cd ../src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api +dotnet run diff --git a/SwiftParcel.Services.Customers/scripts/test.sh b/SwiftParcel.Services.Customers/scripts/test.sh new file mode 100755 index 0000000..9fc7433 --- /dev/null +++ b/SwiftParcel.Services.Customers/scripts/test.sh @@ -0,0 +1,2 @@ +#!/bin/bash +dotnet test diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api/Program.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api/Program.cs new file mode 100644 index 0000000..f1d4aed --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api/Program.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Convey; +using Convey.WebApi; +using SwiftParcel.Services.Customers.Application; +using SwiftParcel.Services.Customers.Infrastructure; +using Convey.WebApi.CQRS; +using SwiftParcel.Services.Customers.Application.Queries; +using SwiftParcel.Services.Customers.Application.Dto; +using Convey.Types; +using SwiftParcel.Services.Customers.Application.Commands; +using Convey.Logging; +using Convey.Secrets.Vault; + +namespace SwiftParcel.Services.Customers.Api +{ + public class Program + { + public static async Task Main(string[] args) + => await WebHost.CreateDefaultBuilder(args) + .ConfigureServices(services => services + .AddConvey() + .AddWebApi() + .AddApplication() + .AddInfrastructure() + .Build()) + .Configure(app => app + .UseInfrastructure() + .UseDispatcherEndpoints(endpoints => endpoints + .Get("", ctx => ctx.Response.WriteAsync(ctx.RequestServices.GetService().Name)) + .Get>("customers") + .Get("customers/{customerId}") + .Get("customers/{customerId}/state") + .Post("customers", + afterDispatch: (cmd, ctx) => ctx.Response.Created($"customers/{cmd.CustomerId}")) + .Put("customers/{customerId}/state/{state}", + afterDispatch: (cmd, ctx) => ctx.Response.NoContent()))) + .UseLogging() + .UseVault() + .Build() + .RunAsync(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api/Properties/launchSettings.json b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api/Properties/launchSettings.json new file mode 100644 index 0000000..447dd97 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:21852", + "sslPort": 44384 + } + }, + "profiles": { + "SwiftParcel.Services.Customers.Api": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5002", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api.csproj b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api.csproj new file mode 100644 index 0000000..b47d7f5 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + disable + enable + + + + + + + + + + + + + + + diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api/appsettings.Development.json b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api/appsettings.Development.json new file mode 100644 index 0000000..ff66ba6 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api/appsettings.json b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api/appsettings.json new file mode 100644 index 0000000..7cadfec --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Api/SwiftParcel.Services.Customers.Api/appsettings.json @@ -0,0 +1,213 @@ +{ + "app": { + "name": "SwiftParcel Customers Service", + "service": "customers-service", + "version": "1" + }, + "consul": { + "enabled": true, + "url": "http://localhost:8500", + "service": "customers-service", + "address": "docker.for.win.localhost", + "port": "5002", + "pingEnabled": false, + "pingEndpoint": "ping", + "pingInterval": 3, + "removeAfterInterval": 3 + }, + "fabio": { + "enabled": true, + "url": "http://localhost:9999", + "service": "customers-service" + }, + "httpClient": { + "type": "fabio", + "retries": 3, + "services": {}, + "requestMasking": { + "enabled": true, + "maskTemplate": "*****" + } + }, + "logger": { + "level": "information", + "excludePaths": ["/", "/ping", "/metrics"], + "excludeProperties": [ + "api_key", + "access_key", + "ApiKey", + "ApiSecret", + "ClientId", + "ClientSecret", + "ConnectionString", + "Password", + "Email", + "Login", + "Secret", + "Token" + ], + "console": { + "enabled": true + }, + "elk": { + "enabled": false, + "url": "http://localhost:9200" + }, + "file": { + "enabled": true, + "path": "logs/logs.txt", + "interval": "day" + }, + "seq": { + "enabled": true, + "url": "http://localhost:5341", + "apiKey": "secret" + }, + "tags": {} + }, + "jaeger": { + "enabled": true, + "serviceName": "customers", + "udpHost": "localhost", + "udpPort": 6831, + "sampler": "const", + "excludePaths": ["/", "/ping", "/metrics"] + }, + "jwt": { + "certificate": { + "location": "certs/localhost.cer" + }, + "validIssuer": "swiftparcel", + "validateAudience": false, + "validateIssuer": true, + "validateLifetime": true + }, + "metrics": { + "enabled": true, + "influxEnabled": false, + "prometheusEnabled": true, + "influxUrl": "http://localhost:8086", + "database": "pacco", + "env": "local", + "interval": 5 + }, + "mongo": { + "connectionString": "mongodb+srv://andrii-courier-db-user:piotr-amadeusz-andrii-2023@cluster0.br51nsv.mongodb.net/?retryWrites=true&w=majority", + "database": "customers-service", + "seed": false + }, + "outbox": { + "enabled": true, + "type": "sequential", + "expiry": 3600, + "intervalMilliseconds": 2000, + "inboxCollection": "inbox", + "outboxCollection": "outbox", + "disableTransactions": true + }, + "rabbitMq": { + "connectionName": "customers-service", + "retries": 3, + "retryInterval": 2, + "conventionsCasing": "snakeCase", + "logger": { + "enabled": true + }, + "username": "guest", + "password": "guest", + "virtualHost": "/", + "port": 5672, + "hostnames": [ + "localhost" + ], + "requestedConnectionTimeout": "00:00:30", + "requestedHeartbeat": "00:01:00", + "socketReadTimeout": "00:00:30", + "socketWriteTimeout": "00:00:30", + "continuationTimeout": "00:00:20", + "handshakeContinuationTimeout": "00:00:10", + "networkRecoveryInterval": "00:00:05", + "exchange": { + "declare": true, + "durable": true, + "autoDelete": false, + "type": "topic", + "name": "customers" + }, + "queue": { + "declare": true, + "durable": true, + "exclusive": false, + "autoDelete": false, + "template": "customers-service/{{exchange}}.{{message}}" + }, + "context": { + "enabled": true, + "header": "message_context" + }, + "spanContextHeader": "span_context" + }, + "redis": { + "connectionString": "localhost", + "instance": "customers:" + }, + "swagger": { + "enabled": true, + "reDocEnabled": false, + "name": "v1", + "title": "API", + "version": "v1", + "routePrefix": "docs", + "includeSecurity": true + }, + "security": { + "certificate": { + "enabled": true, + "header": "Certificate", + "skipRevocationCheck": false, + "allowedDomains": ["pacco.io"], + "allowSubdomains": true, + "allowedHosts": [ + "localhost" + ], + "acl": { + "availability-service": { + "validIssuer": "localhost", + "permissions": [ + "customers:read" + ] + } + } + } + }, + "vault": { + "enabled": false, + "url": "http://localhost:8200", + "authType": "token", + "token": "secret", + "username": "user", + "password": "secret", + "kv": { + "enabled": true, + "engineVersion": 2, + "mountPoint": "kv", + "path": "customers-service/settings" + }, + "pki": { + "enabled": true, + "roleName": "customers-service", + "commonName": "customers-service.pacco.io" + }, + "lease": { + "mongo": { + "type": "database", + "roleName": "customers-service", + "enabled": true, + "autoRenewal": true, + "templates": { + "connectionString": "mongodb://{{username}}:{{password}}@localhost:27017" + } + } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Commands/ChangeCustomerState.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Commands/ChangeCustomerState.cs new file mode 100644 index 0000000..39931ad --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Commands/ChangeCustomerState.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.Customers.Application.Commands +{ + public class ChangeCustomerState : ICommand + { + public Guid CustomerId { get; } + public string State { get; } + + public ChangeCustomerState(Guid customerId, string state) + { + CustomerId = customerId; + State = state; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Commands/CompleteCustomerRegistration.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Commands/CompleteCustomerRegistration.cs new file mode 100644 index 0000000..f18db5a --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Commands/CompleteCustomerRegistration.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.Customers.Application.Commands +{ + public class CompleteCustomerRegistration : ICommand + { + public Guid CustomerId { get; } + public string FirstName { get; private set; } + public string LastName { get; private set; } + public string FullName => $"{FirstName} {LastName}"; + public string Address { get; } + public string SourceAddress { get; private set; } + + public CompleteCustomerRegistration(Guid customerId, string firstName, string lastName, string address, string sourceAddress) + { + CustomerId = customerId; + FirstName = firstName; + LastName = lastName; + Address = address; + SourceAddress = sourceAddress; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Commands/Handlers/ChangeCustomerStateHandler.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Commands/Handlers/ChangeCustomerStateHandler.cs new file mode 100644 index 0000000..94af1b8 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Commands/Handlers/ChangeCustomerStateHandler.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using SwiftParcel.Services.Customers.Application.Services; +using SwiftParcel.Services.Customers.Core; +using SwiftParcel.Services.Customers.Core.Entities; +using SwiftParcel.Services.Customers.Core.Exceptions; +using SwiftParcel.Services.Customers.Core.Repositories; + +namespace SwiftParcel.Services.Customers.Application.Commands.Handlers +{ + public class ChangeCustomerStateHandler : ICommandHandler + { + private readonly ICustomerRepository _customerRepository; + private readonly IEventMapper _eventMapper; + private readonly IMessageBroker _messageBroker; + + public ChangeCustomerStateHandler(ICustomerRepository customerRepository, IEventMapper eventMapper, + IMessageBroker messageBroker) + { + _customerRepository = customerRepository; + _eventMapper = eventMapper; + _messageBroker = messageBroker; + } + + public async Task HandleAsync(ChangeCustomerState command, CancellationToken cancellationToken) + { + var customer = await _customerRepository.GetAsync(command.CustomerId); + if (customer is null) + { + throw new CustomerNotFoundException(command.CustomerId); + } + + if (!Enum.TryParse(command.State, true, out var state)) + { + throw new CannotChangeCustomerStateException(customer.Id, State.Unknown); + } + + if (customer.State == state) + { + return; + } + + switch (state) + { + case State.Valid: + customer.SetValid(); + break; + case State.Incomplete: + customer.SetIncomplete(); + break; + case State.Suspicious: + customer.MarkAsSuspicious(); + break; + case State.Locked: + customer.Lock(); + break; + default: + throw new CannotChangeCustomerStateException(customer.Id, state); + } + + await _customerRepository.UpdateAsync(customer); + var events = _eventMapper.MapAll(customer.Events); + await _messageBroker.PublishAsync(events.ToArray()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Commands/Handlers/CompleteCustomerRegistrationHandler.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Commands/Handlers/CompleteCustomerRegistrationHandler.cs new file mode 100644 index 0000000..895c84d --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Commands/Handlers/CompleteCustomerRegistrationHandler.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using SwiftParcel.Services.Customers.Application.Exceptions; +using SwiftParcel.Services.Customers.Application.Services; +using SwiftParcel.Services.Customers.Core.Exceptions; +using SwiftParcel.Services.Customers.Core.Repositories; + +namespace SwiftParcel.Services.Customers.Application.Commands.Handlers +{ + public class CompleteCustomerRegistrationHandler : ICommandHandler + { + private readonly ICustomerRepository _customerRepository; + private readonly IEventMapper _eventMapper; + private readonly IMessageBroker _messageBroker; + + public CompleteCustomerRegistrationHandler(ICustomerRepository customerRepository, IEventMapper eventMapper, + IMessageBroker messageBroker) + { + _customerRepository = customerRepository; + _eventMapper = eventMapper; + _messageBroker = messageBroker; + } + + public async Task HandleAsync(CompleteCustomerRegistration command, CancellationToken cancellationToken) + { + var customer = await _customerRepository.GetAsync(command.CustomerId); + if (customer is null) + { + throw new CustomerNotFoundException(command.CustomerId); + } + + if (customer.State is Core.Entities.State.Valid) + { + throw new CustomerAlreadyRegisteredException(command.CustomerId); + } + + customer.CompleteRegistration(command.FirstName, command.LastName, command.Address, command.SourceAddress); + await _customerRepository.UpdateAsync(customer); + + var events = _eventMapper.MapAll(customer.Events); + await _messageBroker.PublishAsync(events.ToArray()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/ContractAttribute.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/ContractAttribute.cs new file mode 100644 index 0000000..5c1aa38 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/ContractAttribute.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Application +{ + public class ContractAttribute: Attribute + { + + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Dto/CustomerDetailsDto.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Dto/CustomerDetailsDto.cs new file mode 100644 index 0000000..f3a0f93 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Dto/CustomerDetailsDto.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Application.Dto +{ + public class CustomerDetailsDto : CustomerDto + { + public string Email { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string FullName => $"{FirstName} {LastName}"; + public string Address { get; set; } + public string SourceAddress { get; set; } + public bool IsVip { get; set; } + public IEnumerable CompletedOrders { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Dto/CustomerDto.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Dto/CustomerDto.cs new file mode 100644 index 0000000..5b9bb2d --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Dto/CustomerDto.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Application.Dto +{ + public class CustomerDto + { + public Guid Id { get; set; } + public string State { get; set; } + public DateTime CreatedAt { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Dto/CustomerStateDto.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Dto/CustomerStateDto.cs new file mode 100644 index 0000000..c2e9518 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Dto/CustomerStateDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Application.Dto +{ + public class CustomerStateDto + { + public Guid Id { get; set; } + public string State { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/CustomerBecameVip.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/CustomerBecameVip.cs new file mode 100644 index 0000000..faeecd4 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/CustomerBecameVip.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Customers.Application.Events +{ + public class CustomerBecameVip : IEvent + { + public Guid CustomerId { get; } + + public CustomerBecameVip(Guid customerId) + { + CustomerId = customerId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/CustomerCreated.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/CustomerCreated.cs new file mode 100644 index 0000000..9a7006b --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/CustomerCreated.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Customers.Application.Events +{ + public class CustomerCreated : IEvent + { + public Guid CustomerId { get; } + public string Email { get; } + public string FullName { get; } + + public CustomerCreated(Guid customerId, string email, string fullName) + { + CustomerId = customerId; + Email = email; + FullName = fullName; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/CustomerStateChanged.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/CustomerStateChanged.cs new file mode 100644 index 0000000..c84a1f4 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/CustomerStateChanged.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Customers.Application.Events +{ + public class CustomerStateChanged : IEvent + { + public Guid CustomerId { get; } + public string CurrentState { get; } + public string PreviousState { get; } + + public CustomerStateChanged(Guid customerId, string currentState, string previousState) + { + CustomerId = customerId; + CurrentState = currentState; + PreviousState = previousState; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/External/Handlers/OrderCompletedHandler.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/External/Handlers/OrderCompletedHandler.cs new file mode 100644 index 0000000..84543bc --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/External/Handlers/OrderCompletedHandler.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using SwiftParcel.Services.Customers.Application.Services; +using SwiftParcel.Services.Customers.Core.Exceptions; +using SwiftParcel.Services.Customers.Core.Repositories; +using SwiftParcel.Services.Customers.Core.Services; + +namespace SwiftParcel.Services.Customers.Application.Events.External.Handlers +{ + public class OrderCompletedHandler : IEventHandler + { + private readonly ICustomerRepository _customerRepository; + private readonly IVipPolicy _vipPolicy; + private readonly IEventMapper _eventMapper; + private readonly IMessageBroker _messageBroker; + + public OrderCompletedHandler(ICustomerRepository customerRepository, IVipPolicy vipPolicy, + IEventMapper eventMapper, IMessageBroker messageBroker) + { + _customerRepository = customerRepository; + _vipPolicy = vipPolicy; + _eventMapper = eventMapper; + _messageBroker = messageBroker; + } + + public async Task HandleAsync(OrderCompleted @event, CancellationToken cancellationToken) + { + var customer = await _customerRepository.GetAsync(@event.CustomerId); + if (customer is null) + { + throw new CustomerNotFoundException(@event.CustomerId); + } + + var isVip = customer.IsVip; + customer.AddCompletedOrder(@event.OrderId); + _vipPolicy.ApplyVipStatusIfEligible(customer); + var vipApplied = !isVip && customer.IsVip; + await _customerRepository.UpdateAsync(customer); + var events = _eventMapper.MapAll(customer.Events); + await _messageBroker.PublishAsync(events.ToArray()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/External/Handlers/SignedUpHandler.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/External/Handlers/SignedUpHandler.cs new file mode 100644 index 0000000..34d22f9 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/External/Handlers/SignedUpHandler.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Microsoft.Extensions.Logging; +using SwiftParcel.Services.Customers.Application.Exceptions; +using SwiftParcel.Services.Customers.Application.Services; +using SwiftParcel.Services.Customers.Core.Repositories; + +namespace SwiftParcel.Services.Customers.Application.Events.External.Handlers +{ + public class SignedUpHandler : IEventHandler + { + private const string RequiredRole = "user"; + private readonly ICustomerRepository _customerRepository; + private readonly IDateTimeProvider _dateTimeProvider; + private readonly ILogger _logger; + + public SignedUpHandler(ICustomerRepository customerRepository, IDateTimeProvider dateTimeProvider, + ILogger logger) + { + _customerRepository = customerRepository; + _dateTimeProvider = dateTimeProvider; + _logger = logger; + } + + public async Task HandleAsync(SignedUp @event, CancellationToken cancellationToken) + { + if (@event.Role != RequiredRole) + { + throw new InvalidRoleException(@event.UserId, @event.Role, RequiredRole); + } + + var customer = await _customerRepository.GetAsync(@event.UserId); + if (customer is {}) + { + throw new CustomerAlreadyCreatedException(customer.Id); + } + + customer = new Core.Entities.Customer(@event.UserId, @event.Email, _dateTimeProvider.Now); + await _customerRepository.AddAsync(customer); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/External/OrderCompleted.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/External/OrderCompleted.cs new file mode 100644 index 0000000..9d744da --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/External/OrderCompleted.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; + +namespace SwiftParcel.Services.Customers.Application.Events.External.Handlers +{ + [Message("orders")] + public class OrderCompleted : IEvent + { + public Guid OrderId { get; } + public Guid CustomerId { get; } + + public OrderCompleted(Guid orderId, Guid customerId) + { + OrderId = orderId; + CustomerId = customerId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/External/SignedUp.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/External/SignedUp.cs new file mode 100644 index 0000000..21aac29 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/External/SignedUp.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; + +namespace SwiftParcel.Services.Customers.Application.Events.External +{ + [Message("identity")] + public class SignedUp : IEvent + { + public Guid UserId { get; } + public string Email { get; } + public string Role { get; } + + public SignedUp(Guid userId, string email, string role) + { + UserId = userId; + Email = email; + Role = role; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/Rejected/ChangeCustomerStateRejected.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/Rejected/ChangeCustomerStateRejected.cs new file mode 100644 index 0000000..6bc1084 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/Rejected/ChangeCustomerStateRejected.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Customers.Application.Events.Rejected +{ + public class ChangeCustomerStateRejected : IRejectedEvent + { + public Guid CustomerId { get; } + public string State { get; } + public string Reason { get; } + public string Code { get; } + + public ChangeCustomerStateRejected(Guid customerId, string state, string reason, string code) + { + CustomerId = customerId; + State = state; + Reason = reason; + Code = code; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/Rejected/CompleteCustomerRegistrationRejected.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/Rejected/CompleteCustomerRegistrationRejected.cs new file mode 100644 index 0000000..9ccfbb3 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Events/Rejected/CompleteCustomerRegistrationRejected.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Customers.Application.Events.Rejected +{ + public class CompleteCustomerRegistrationRejected : IRejectedEvent + { + public Guid CustomerId { get; } + public string Reason { get; } + public string Code { get; } + + public CompleteCustomerRegistrationRejected(Guid customerId, string reason, string code) + { + CustomerId = customerId; + Reason = reason; + Code = code; + } + } +} diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Exceptions/AppException.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Exceptions/AppException.cs new file mode 100644 index 0000000..5a6bbdb --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Exceptions/AppException.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Application.Exceptions +{ + public class AppException : Exception + { + public virtual string Code { get; } + + protected AppException(string message) : base(message) + { + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Exceptions/CustomerAlreadyCreatedException.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Exceptions/CustomerAlreadyCreatedException.cs new file mode 100644 index 0000000..9661fba --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Exceptions/CustomerAlreadyCreatedException.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Application.Exceptions +{ + public class CustomerAlreadyCreatedException : AppException + { + public override string Code { get; } = "customer_already_created"; + public Guid CustomerId { get; } + + public CustomerAlreadyCreatedException(Guid customerId) + : base($"Customer with id: {customerId} was already created.") + { + CustomerId = customerId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Exceptions/CustomerAlreadyRegisteredException.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Exceptions/CustomerAlreadyRegisteredException.cs new file mode 100644 index 0000000..e03812e --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Exceptions/CustomerAlreadyRegisteredException.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Application.Exceptions +{ + public class CustomerAlreadyRegisteredException : AppException + { + public override string Code { get; } = "customer_already_registered"; + public Guid Id { get; } + + public CustomerAlreadyRegisteredException(Guid id) + : base($"Customer with id: {id} has already been registered.") + { + Id = id; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Exceptions/InvalidRoleException.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Exceptions/InvalidRoleException.cs new file mode 100644 index 0000000..b6b8239 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Exceptions/InvalidRoleException.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Application.Exceptions +{ + public class InvalidRoleException : AppException + { + public override string Code { get; } = "invalid_role"; + + public InvalidRoleException(Guid userId, string role, string requiredRole) + : base($"Customer account will not be created for the user with id: {userId} " + + $"due to the invalid role: {role} (required: {requiredRole}).") + { + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Extensions.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Extensions.cs new file mode 100644 index 0000000..e15ef9d --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Extensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey; +using Convey.CQRS.Commands; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Customers.Application +{ + public static class Extensions + { + public static IConveyBuilder AddApplication(this IConveyBuilder builder) + => builder + .AddCommandHandlers() + .AddEventHandlers() + .AddInMemoryCommandDispatcher() + .AddInMemoryEventDispatcher(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/IAppContext.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/IAppContext.cs new file mode 100644 index 0000000..0b96620 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/IAppContext.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Application +{ + public interface IAppContext + { + string RequestId { get; } + IIdentityContext Identity { get; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/IIdentityContext.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/IIdentityContext.cs new file mode 100644 index 0000000..9ab22ce --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/IIdentityContext.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Application +{ + public interface IIdentityContext + { + Guid Id { get; } + string Role { get; } + bool IsAuthenticated { get; } + bool IsOfficeWorker { get; } + bool IsCourier { get; } + IDictionary Claims { get; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Queries/GetCustomer.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Queries/GetCustomer.cs new file mode 100644 index 0000000..f3ff7dd --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Queries/GetCustomer.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using SwiftParcel.Services.Customers.Application.Dto; + +namespace SwiftParcel.Services.Customers.Application.Queries +{ + public class GetCustomer : IQuery + { + public Guid CustomerId { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Queries/GetCustomerState.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Queries/GetCustomerState.cs new file mode 100644 index 0000000..17c3e64 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Queries/GetCustomerState.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using SwiftParcel.Services.Customers.Application.Dto; + +namespace SwiftParcel.Services.Customers.Application.Queries +{ + public class GetCustomerState : IQuery + { + public Guid CustomerId { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Queries/GetCustomers.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Queries/GetCustomers.cs new file mode 100644 index 0000000..82ad89d --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Queries/GetCustomers.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using SwiftParcel.Services.Customers.Application.Dto; + +namespace SwiftParcel.Services.Customers.Application.Queries +{ + public class GetCustomers: IQuery> + { + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Services/IDateTimeProvider.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Services/IDateTimeProvider.cs new file mode 100644 index 0000000..e05d590 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Services/IDateTimeProvider.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Application.Services +{ + public interface IDateTimeProvider + { + DateTime Now { get; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Services/IEventMapper.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Services/IEventMapper.cs new file mode 100644 index 0000000..18d3ff4 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Services/IEventMapper.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using SwiftParcel.Services.Customers.Core; + +namespace SwiftParcel.Services.Customers.Application.Services +{ + public interface IEventMapper + { + IEvent Map(IDomainEvent @event); + IEnumerable MapAll(IEnumerable events); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Services/IMessageBroker.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Services/IMessageBroker.cs new file mode 100644 index 0000000..1668427 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/Services/IMessageBroker.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Customers.Application.Services +{ + public interface IMessageBroker + { + Task PublishAsync(params IEvent[] events); + Task PublishAsync(IEnumerable events); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application.csproj b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application.csproj new file mode 100644 index 0000000..54ea04b --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application/SwiftParcel.Services.Customers.Application.csproj @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + net6.0 + enable + disable + + + diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Entities/AggregateId.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Entities/AggregateId.cs new file mode 100644 index 0000000..115988a --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Entities/AggregateId.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Customers.Core.Exceptions; + +namespace SwiftParcel.Services.Customers.Core.Entities +{ + public class AggregateId : IEquatable + { + public Guid Value { get; } + + public AggregateId() + { + Value = Guid.NewGuid(); + } + + public AggregateId(Guid value) + { + if (value == Guid.Empty) + { + throw new InvalidAggregateIdException(); + } + + Value = value; + } + + public bool Equals(AggregateId other) + { + if (ReferenceEquals(null, other)) return false; + return ReferenceEquals(this, other) || Value.Equals(other.Value); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((AggregateId) obj); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public static implicit operator Guid(AggregateId id) + => id.Value; + + public static implicit operator AggregateId(Guid id) + => new AggregateId(id); + + public override string ToString() => Value.ToString(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Entities/AggregateRoot.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Entities/AggregateRoot.cs new file mode 100644 index 0000000..8728b43 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Entities/AggregateRoot.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Core.Entities +{ + public abstract class AggregateRoot + { + private readonly List _events = new List(); + public IEnumerable Events => _events; + public AggregateId Id { get; protected set; } + public int Version { get; protected set; } + + protected void AddEvent(IDomainEvent @event) + { + _events.Add(@event); + } + + public void ClearEvents() => _events.Clear(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Entities/Customer.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Entities/Customer.cs new file mode 100644 index 0000000..927b236 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Entities/Customer.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Customers.Core.Events; +using SwiftParcel.Services.Customers.Core.Exceptions; + +namespace SwiftParcel.Services.Customers.Core.Entities +{ + public class Customer : AggregateRoot + { + private ISet _completedOrders = new HashSet(); + + public string Email { get; private set; } + public string FirstName { get; private set; } + public string LastName { get; private set; } + public string FullName => $"{FirstName} {LastName}"; + public string Address { get; private set; } + public string SourceAddress { get; private set; } + public bool IsVip { get; private set; } + public State State { get; private set; } + public DateTime CreatedAt { get; private set; } + + public IEnumerable CompletedOrders + { + get => _completedOrders; + set => _completedOrders = new HashSet(value); + } + + public Customer(Guid id, string email, DateTime createdAt) : this(id, email, createdAt, string.Empty, + string.Empty, string.Empty, string.Empty, false, State.Incomplete, Enumerable.Empty()) + { + } + + public Customer(Guid id, string email, DateTime createdAt, string firstName, string lastName, string address, string sourceAddress, bool isVip, + State state, IEnumerable completedOrders = null) + { + Id = id; + Email = email; + CreatedAt = createdAt; + FirstName = firstName; + LastName = lastName; + Address = address; + SourceAddress = sourceAddress; + IsVip = isVip; + CompletedOrders = completedOrders ?? Enumerable.Empty(); + State = state; + } + + public void CompleteRegistration(string firstName, string lastName, string address, string sourceAddress) + { + if (string.IsNullOrWhiteSpace(firstName) || string.IsNullOrWhiteSpace(lastName)) + { + throw new InvalidCustomerFullNameException(Id, $"{firstName} {lastName}"); + } + + if (string.IsNullOrWhiteSpace(address)) + { + throw new InvalidCustomerAddressException(Id, address); + } + + if (string.IsNullOrWhiteSpace(sourceAddress)) + { + throw new InvalidCustomerSourceAddressException(Id, sourceAddress); + } + + if (State != State.Incomplete) + { + throw new CannotChangeCustomerStateException(Id, State); + } + + FirstName = firstName; + LastName = lastName; + Address = address; + SourceAddress = sourceAddress; // Ensure the source address is set during registration + State = State.Valid; + AddEvent(new CustomerRegistrationCompleted(this)); + } + + public void SetValid() => SetState(State.Valid); + + public void SetIncomplete() => SetState(State.Incomplete); + + public void Lock() => SetState(State.Locked); + + public void MarkAsSuspicious() => SetState(State.Suspicious); + + private void SetState(State state) + { + var previousState = State; + State = state; + AddEvent(new CustomerStateChanged(this, previousState)); + } + + public void SetVip() + { + if (IsVip) + { + return; + } + + IsVip = true; + AddEvent(new CustomerBecameVip(this)); + } + + public void AddCompletedOrder(Guid orderId) + { + if (orderId == Guid.Empty) + { + return; + } + + _completedOrders.Add(orderId); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Entities/State.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Entities/State.cs new file mode 100644 index 0000000..abf9582 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Entities/State.cs @@ -0,0 +1,11 @@ +namespace SwiftParcel.Services.Customers.Core.Entities +{ + public enum State + { + Unknown, + Valid, + Incomplete, + Suspicious, + Locked + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Events/CustomerBecameVip.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Events/CustomerBecameVip.cs new file mode 100644 index 0000000..08c6fab --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Events/CustomerBecameVip.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Customers.Core.Entities; + +namespace SwiftParcel.Services.Customers.Core.Events +{ + public class CustomerBecameVip: IDomainEvent + { + public Customer Customer { get; } + + public CustomerBecameVip(Customer customer) + { + Customer = customer; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Events/CustomerRegistrationCompleted.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Events/CustomerRegistrationCompleted.cs new file mode 100644 index 0000000..6f6088c --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Events/CustomerRegistrationCompleted.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Customers.Core.Entities; + +namespace SwiftParcel.Services.Customers.Core.Events +{ + public class CustomerRegistrationCompleted : IDomainEvent + { + public Customer Customer { get; } + + public CustomerRegistrationCompleted(Customer customer) + { + Customer = customer; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Events/CustomerStateChanged.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Events/CustomerStateChanged.cs new file mode 100644 index 0000000..f9f76b9 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Events/CustomerStateChanged.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Customers.Core.Entities; + +namespace SwiftParcel.Services.Customers.Core.Events +{ + public class CustomerStateChanged : IDomainEvent + { + public Customer Customer { get; } + public State PreviousState { get; } + + public CustomerStateChanged(Customer customer, State previousState) + { + Customer = customer; + PreviousState = previousState; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/CannotChangeCustomerStateException.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/CannotChangeCustomerStateException.cs new file mode 100644 index 0000000..fce7e02 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/CannotChangeCustomerStateException.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Customers.Core.Entities; +using SwiftParcel.Services.Customers.Core.Exceptions; + +namespace SwiftParcel.Services.Customers.Core +{ + public class CannotChangeCustomerStateException : DomainException + { + public override string Code { get; } = "cannot_change_customer_state"; + public Guid Id { get; } + public State State { get; } + + public CannotChangeCustomerStateException(Guid id, State state) : base( + $"Cannot change customer: {id} state to: {state}.") + { + Id = id; + State = state; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/CustomerNotFoundException.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/CustomerNotFoundException.cs new file mode 100644 index 0000000..bdd15b9 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/CustomerNotFoundException.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Core.Exceptions +{ + public class CustomerNotFoundException : DomainException + { + public override string Code { get; } = "customer_not_found"; + public Guid Id { get; } + + public CustomerNotFoundException(Guid id) : base($"Customer with id: {id} was not found.") + { + Id = id; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/DomainException.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/DomainException.cs new file mode 100644 index 0000000..7c101d2 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/DomainException.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Core.Exceptions +{ + public abstract class DomainException : Exception + { + public virtual string Code { get; } + + protected DomainException(string message) : base(message) + { + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/InvalidAggregateIdException.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/InvalidAggregateIdException.cs new file mode 100644 index 0000000..84fa748 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/InvalidAggregateIdException.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Core.Exceptions +{ + public class InvalidAggregateIdException : DomainException + { + public override string Code { get; } = "invalid_aggregate_id"; + + public InvalidAggregateIdException() : base($"Invalid aggregate id.") + { + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/InvalidCustomerAddressException.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/InvalidCustomerAddressException.cs new file mode 100644 index 0000000..0039b44 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/InvalidCustomerAddressException.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Core.Exceptions +{ + public class InvalidCustomerAddressException : DomainException + { + public override string Code { get; } = "invalid_customer_address"; + public Guid Id { get; } + public string Address { get; } + + public InvalidCustomerAddressException(Guid id, string address) : base( + $"Customer with id: {id} has invalid address.") + { + Id = id; + Address = address; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/InvalidCustomerFullNameException.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/InvalidCustomerFullNameException.cs new file mode 100644 index 0000000..2ee0022 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/InvalidCustomerFullNameException.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Core.Exceptions +{ + public class InvalidCustomerFullNameException : DomainException + { + public override string Code { get; } = "invalid_customer_fullname"; + public Guid Id { get; } + public string FullName { get; } + + public InvalidCustomerFullNameException(Guid id, string fullName) : base( + $"Customer with id: {id} has invalid full name.") + { + Id = id; + FullName = fullName; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/InvalidCustomerSourceAddressException.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/InvalidCustomerSourceAddressException.cs new file mode 100644 index 0000000..b1ff3e6 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Exceptions/InvalidCustomerSourceAddressException.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Core.Exceptions +{ + public class InvalidCustomerSourceAddressException: DomainException + { + public override string Code { get; } = "invalid_customer_source_address"; + public Guid Id { get; } + public string SourceAddress { get; } + + public InvalidCustomerSourceAddressException(Guid id, string address) : base( + $"Customer with id: {id} has invalid address.") + { + Id = id; + SourceAddress = address; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/IDomainEvent.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/IDomainEvent.cs new file mode 100644 index 0000000..f227bec --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/IDomainEvent.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Core +{ + public interface IDomainEvent + { + + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Repositories/ICustomerRepository.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Repositories/ICustomerRepository.cs new file mode 100644 index 0000000..c556dc2 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Repositories/ICustomerRepository.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Customers.Core.Entities; + +namespace SwiftParcel.Services.Customers.Core.Repositories +{ + public interface ICustomerRepository + { + Task GetAsync(Guid id); + Task AddAsync(Customer customer); + Task UpdateAsync(Customer customer); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Services/IVipPolicy.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Services/IVipPolicy.cs new file mode 100644 index 0000000..f5c7ab3 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Services/IVipPolicy.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Customers.Core.Entities; + +namespace SwiftParcel.Services.Customers.Core.Services +{ + public interface IVipPolicy + { + void ApplyVipStatusIfEligible(Customer customer); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Services/VipPolicy.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Services/VipPolicy.cs new file mode 100644 index 0000000..18cee7b --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/Services/VipPolicy.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Customers.Core.Entities; + +namespace SwiftParcel.Services.Customers.Core.Services +{ + public class VipPolicy : IVipPolicy + { + public void ApplyVipStatusIfEligible(Customer customer) + { + if (customer.IsVip) + { + return; + } + + if (customer.CompletedOrders.Count() < 20) + { + return; + } + + customer.SetVip(); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core.csproj b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core.csproj new file mode 100644 index 0000000..640868e --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core/SwiftParcel.Services.Customers.Core.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + disable + + + diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Context/AppContext.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Context/AppContext.cs new file mode 100644 index 0000000..1d9fd55 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Context/AppContext.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Customers.Application; + +namespace SwiftParcel.Services.Customers.Infrastructure.Context +{ + internal class AppContext : IAppContext + { + public string RequestId { get; } + public IIdentityContext Identity { get; } + + internal AppContext() : this(Guid.NewGuid().ToString("N"), IdentityContext.Empty) + { + } + + internal AppContext(CorrelationContext context) : this(context.CorrelationId, + context.User is null ? IdentityContext.Empty : new IdentityContext(context.User)) + { + } + + internal AppContext(string requestId, IIdentityContext identity) + { + RequestId = requestId; + Identity = identity; + } + + internal static IAppContext Empty => new AppContext(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Context/AppContextFactory.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Context/AppContextFactory.cs new file mode 100644 index 0000000..ecf6463 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Context/AppContextFactory.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.MessageBrokers; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using SwiftParcel.Services.Customers.Application; + +namespace SwiftParcel.Services.Customers.Infrastructure.Context +{ + internal sealed class AppContextFactory : IAppContextFactory + { + private readonly ICorrelationContextAccessor _contextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + + public AppContextFactory(ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor) + { + _contextAccessor = contextAccessor; + _httpContextAccessor = httpContextAccessor; + } + + public IAppContext Create() + { + if (_contextAccessor.CorrelationContext is {}) + { + var payload = JsonConvert.SerializeObject(_contextAccessor.CorrelationContext); + + return string.IsNullOrWhiteSpace(payload) + ? AppContext.Empty + : new AppContext(JsonConvert.DeserializeObject(payload)); + } + + var context = _httpContextAccessor.GetCorrelationContext(); + + return context is null ? AppContext.Empty : new AppContext(context); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Context/CorrelationContext.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Context/CorrelationContext.cs new file mode 100644 index 0000000..912c22e --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Context/CorrelationContext.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Customers.Infrastructure.Context +{ + internal class CorrelationContext + { + + public string CorrelationId { get; set; } + public string SpanContext { get; set; } + public UserContext User { get; set; } + public string ResourceId { get; set; } + public string TraceId { get; set; } + public string ConnectionId { get; set; } + public string Name { get; set; } + public DateTime CreatedAt { get; set; } + + public class UserContext + { + public string Id { get; set; } + public bool IsAuthenticated { get; set; } + public string Role { get; set; } + public IDictionary Claims { get; set; } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Context/IdentityContext.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Context/IdentityContext.cs new file mode 100644 index 0000000..7232ee4 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Context/IdentityContext.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Customers.Application; + +namespace SwiftParcel.Services.Customers.Infrastructure.Context +{ + internal class IdentityContext : IIdentityContext + { + public Guid Id { get; } + public string Role { get; } = string.Empty; + public bool IsAuthenticated { get; } + public bool IsOfficeWorker { get; } + public bool IsCourier { get; } + + public IDictionary Claims { get; } = new Dictionary(); + + internal IdentityContext() + { + } + + internal IdentityContext(CorrelationContext.UserContext context) + : this(context.Id, context.Role, context.IsAuthenticated, context.Claims) + { + } + + internal IdentityContext(string id, string role, bool isAuthenticated, IDictionary claims) + { + Id = Guid.TryParse(id, out var userId) ? userId : Guid.Empty; + Role = role ?? string.Empty; + IsAuthenticated = isAuthenticated; + IsOfficeWorker = Role.Equals("officeworker", StringComparison.InvariantCultureIgnoreCase); + IsCourier = Role.Equals("courier", StringComparison.InvariantCultureIgnoreCase); + Claims = claims ?? new Dictionary(); + } + + internal static IIdentityContext Empty => new IdentityContext(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs new file mode 100644 index 0000000..7d0c19c --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; + +namespace SwiftParcel.Services.Customers.Infrastructure.Decorators +{ + public class OutboxCommandHandlerDecorator : ICommandHandler + where TCommand : class, ICommand + { + private readonly ICommandHandler _handler; + private readonly IMessageOutbox _outbox; + private readonly string _messageId; + private readonly bool _enabled; + + public OutboxCommandHandlerDecorator(ICommandHandler handler, IMessageOutbox outbox, + OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) + { + _handler = handler; + _outbox = outbox; + _enabled = outboxOptions.Enabled; + + var messageProperties = messagePropertiesAccessor.MessageProperties; + _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId) + ? Guid.NewGuid().ToString("N") + : messageProperties.MessageId; + } + + public Task HandleAsync(TCommand command, CancellationToken cancellationToken) + => _enabled + ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(command)) + : _handler.HandleAsync(command); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs new file mode 100644 index 0000000..397e273 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; + +namespace SwiftParcel.Services.Customers.Infrastructure.Decorators +{ + public class OutboxEventHandlerDecorator : IEventHandler + where TEvent : class, IEvent + { + private readonly IEventHandler _handler; + private readonly IMessageOutbox _outbox; + private readonly string _messageId; + private readonly bool _enabled; + + public OutboxEventHandlerDecorator(IEventHandler handler, IMessageOutbox outbox, + OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) + { + _handler = handler; + _outbox = outbox; + _enabled = outboxOptions.Enabled; + + var messageProperties = messagePropertiesAccessor.MessageProperties; + _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId) + ? Guid.NewGuid().ToString("N") + : messageProperties.MessageId; + } + + public Task HandleAsync(TEvent @event, CancellationToken cancellationToken) + => _enabled + ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(@event)) + : _handler.HandleAsync(@event); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Exceptions/ExceptionToMessageMapper.cs new file mode 100644 index 0000000..cf9b8d5 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.MessageBrokers.RabbitMQ; +using SwiftParcel.Services.Customers.Application.Commands; +using SwiftParcel.Services.Customers.Application.Events.Rejected; +using SwiftParcel.Services.Customers.Application.Exceptions; +using SwiftParcel.Services.Customers.Core; +using SwiftParcel.Services.Customers.Core.Exceptions; + +namespace SwiftParcel.Services.Customers.Infrastructure.Exceptions +{ + public class ExceptionToMessageMapper : IExceptionToMessageMapper + { + public object Map(Exception exception, object message) + => exception switch + { + CannotChangeCustomerStateException ex => message switch + { + ChangeCustomerState _ => new ChangeCustomerStateRejected(ex.Id, + ex.State.ToString().ToLowerInvariant(), ex.Message, ex.Code), + CompleteCustomerRegistration _ => new CompleteCustomerRegistrationRejected(ex.Id, ex.Message, + ex.Code), + _ => null + }, + CustomerAlreadyRegisteredException ex => new CompleteCustomerRegistrationRejected(ex.Id, ex.Message, + ex.Code), + CustomerNotFoundException ex => new CompleteCustomerRegistrationRejected(ex.Id, ex.Message, ex.Code), + InvalidCustomerFullNameException ex => new CompleteCustomerRegistrationRejected(ex.Id, ex.Message, + ex.Code), + InvalidCustomerAddressException ex => new CompleteCustomerRegistrationRejected(ex.Id, ex.Message, + ex.Code), + _ => null + }; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Exceptions/ExceptionToResponseMapper.cs new file mode 100644 index 0000000..98d93f8 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Convey; +using Convey.WebApi.Exceptions; +using SwiftParcel.Services.Customers.Application.Exceptions; +using SwiftParcel.Services.Customers.Core.Exceptions; + +namespace SwiftParcel.Services.Customers.Infrastructure.Exceptions +{ + internal sealed class ExceptionToResponseMapper : IExceptionToResponseMapper + { + private static readonly ConcurrentDictionary Codes = new ConcurrentDictionary(); + + public ExceptionResponse Map(Exception exception) + => exception switch + { + DomainException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message}, + HttpStatusCode.BadRequest), + AppException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message}, + HttpStatusCode.BadRequest), + _ => new ExceptionResponse(new {code = "error", reason = "There was an error."}, + HttpStatusCode.BadRequest) + }; + + private static string GetCode(Exception exception) + { + var type = exception.GetType(); + if (Codes.TryGetValue(type, out var code)) + { + return code; + } + + var exceptionCode = exception switch + { + DomainException domainException when !string.IsNullOrWhiteSpace(domainException.Code) => domainException + .Code, + AppException appException when !string.IsNullOrWhiteSpace(appException.Code) => appException.Code, + _ => exception.GetType().Name.Underscore().Replace("_exception", string.Empty) + }; + + Codes.TryAdd(type, exceptionCode); + + return exceptionCode; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Extensions.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Extensions.cs new file mode 100644 index 0000000..3f5d902 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Extensions.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Convey; +using Convey.CQRS.Commands; +using Convey.CQRS.Events; +using Convey.CQRS.Queries; +using Convey.Discovery.Consul; +using Convey.Docs.Swagger; +using Convey.HTTP; +using Convey.LoadBalancing.Fabio; +using Convey.MessageBrokers; +using Convey.MessageBrokers.CQRS; +using Convey.MessageBrokers.Outbox; +using Convey.MessageBrokers.Outbox.Mongo; +using Convey.MessageBrokers.RabbitMQ; +using Convey.Metrics.AppMetrics; +using Convey.Persistence.MongoDB; +using Convey.Persistence.Redis; +using Convey.Security; +using Convey.Tracing.Jaeger; +using Convey.Tracing.Jaeger.RabbitMQ; +using Convey.WebApi; +using Convey.WebApi.CQRS; +using Convey.WebApi.Security; +using Convey.WebApi.Swagger; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using SwiftParcel.Services.Customers.Application; +using SwiftParcel.Services.Customers.Application.Commands; +using SwiftParcel.Services.Customers.Application.Events.External; +using SwiftParcel.Services.Customers.Application.Events.External.Handlers; +using SwiftParcel.Services.Customers.Application.Services; +using SwiftParcel.Services.Customers.Core.Repositories; +using SwiftParcel.Services.Customers.Core.Services; +using SwiftParcel.Services.Customers.Infrastructure.Context; +using SwiftParcel.Services.Customers.Infrastructure.Decorators; +using SwiftParcel.Services.Customers.Infrastructure.Exceptions; +using SwiftParcel.Services.Customers.Infrastructure.Logging; +using SwiftParcel.Services.Customers.Infrastructure.Mongo.Documents; +using SwiftParcel.Services.Customers.Infrastructure.Mongo.Repositories; +using SwiftParcel.Services.Customers.Infrastructure.Services; + +namespace SwiftParcel.Services.Customers.Infrastructure +{ + public static class Extensions + { + public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + { + builder.Services.AddSingleton(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddTransient(); + builder.Services.AddTransient(ctx => ctx.GetRequiredService().Create()); + builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>)); + builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>)); + + return builder + .AddErrorHandler() + .AddQueryHandlers() + .AddInMemoryQueryDispatcher() + .AddHttpClient() + .AddConsul() + .AddFabio() + .AddRabbitMq(plugins: p => p.AddJaegerRabbitMqPlugin()) + .AddMessageOutbox(o => o.AddMongo()) + .AddExceptionToMessageMapper() + .AddMongo() + .AddRedis() + .AddMetrics() + .AddJaeger() + .AddHandlersLogging() + .AddMongoRepository("customers") + .AddWebApiSwaggerDocs() + .AddCertificateAuthentication() + .AddSecurity(); + } + + public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app) + { + app.UseErrorHandler() + .UseSwaggerDocs() + .UseJaeger() + .UseConvey() + .UsePublicContracts() + .UseMetrics() + .UseCertificateAuthentication() + .UseRabbitMq() + .SubscribeCommand() + .SubscribeCommand() + .SubscribeEvent() + .SubscribeEvent(); + + return app; + } + + internal static CorrelationContext GetCorrelationContext(this IHttpContextAccessor accessor) + => accessor.HttpContext?.Request.Headers.TryGetValue("Correlation-Context", out var json) is true + ? JsonConvert.DeserializeObject(json.FirstOrDefault()) + : null; + + internal static IDictionary GetHeadersToForward(this IMessageProperties messageProperties) + { + const string sagaHeader = "Saga"; + if (messageProperties?.Headers is null || !messageProperties.Headers.TryGetValue(sagaHeader, out var saga)) + { + return null; + } + + return saga is null + ? null + : new Dictionary + { + [sagaHeader] = saga + }; + } + + internal static string GetSpanContext(this IMessageProperties messageProperties, string header) + { + if (messageProperties is null) + { + return string.Empty; + } + + if (messageProperties.Headers.TryGetValue(header, out var span) && span is byte[] spanBytes) + { + return Encoding.UTF8.GetString(spanBytes); + } + + return string.Empty; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/IAppContextFactory.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/IAppContextFactory.cs new file mode 100644 index 0000000..8320ed8 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/IAppContextFactory.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Customers.Application; + +namespace SwiftParcel.Services.Customers.Infrastructure +{ + public interface IAppContextFactory + { + IAppContext Create(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Logging/Extensions.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Logging/Extensions.cs new file mode 100644 index 0000000..92736ce --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Logging/Extensions.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey; +using Convey.Logging.CQRS; +using Microsoft.Extensions.DependencyInjection; +using SwiftParcel.Services.Customers.Application.Commands; + +namespace SwiftParcel.Services.Customers.Infrastructure.Logging +{ + internal static class Extensions + { + public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + { + var assembly = typeof(CompleteCustomerRegistration).Assembly; + + builder.Services.AddSingleton(new MessageToLogTemplateMapper()); + + return builder + .AddCommandHandlersLogging(assembly) + .AddEventHandlersLogging(assembly); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Logging/MessageToLogTemplateMapper.cs new file mode 100644 index 0000000..744ba98 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.Logging.CQRS; +using SwiftParcel.Services.Customers.Application.Commands; +using SwiftParcel.Services.Customers.Application.Events.External; +using SwiftParcel.Services.Customers.Application.Events.External.Handlers; + +namespace SwiftParcel.Services.Customers.Infrastructure.Logging +{ + public class MessageToLogTemplateMapper : IMessageToLogTemplateMapper + { + private static IReadOnlyDictionary MessageTemplates + => new Dictionary + { + { + typeof(CompleteCustomerRegistration), + new HandlerLogTemplate {After = "Completed a registration for the customer with id: {CustomerId}."} + }, + { + typeof(ChangeCustomerState), + new HandlerLogTemplate {After = "Changed a customer with id: {CustomerId} state to: {State}."} + }, + { + typeof(OrderCompleted), new HandlerLogTemplate + { + After = "Order with id: {OrderId} for the customer with id: {CustomerId} has been completed." + } + }, + { + typeof(SignedUp), new HandlerLogTemplate + { + After = "Created a new customer with id: {UserId}." + } + } + }; + + public HandlerLogTemplate Map(TMessage message) where TMessage : class + { + var key = message.GetType(); + return MessageTemplates.TryGetValue(key, out var template) ? template : null; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Documents/CustomerDocument.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Documents/CustomerDocument.cs new file mode 100644 index 0000000..d57749a --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Documents/CustomerDocument.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.Types; +using SwiftParcel.Services.Customers.Core.Entities; + +namespace SwiftParcel.Services.Customers.Infrastructure.Mongo.Documents +{ + public class CustomerDocument: IIdentifiable + { + public Guid Id { get; set; } + public string Email { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string Address { get; set; } + public string SourceAddress { get; set; } + public bool IsVip { get; set; } + public State State { get; set; } + public DateTime CreatedAt { get; set; } + public IEnumerable CompletedOrders { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Documents/Extensions.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Documents/Extensions.cs new file mode 100644 index 0000000..a06f846 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Documents/Extensions.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Customers.Application.Dto; +using SwiftParcel.Services.Customers.Core.Entities; + +namespace SwiftParcel.Services.Customers.Infrastructure.Mongo.Documents +{ + public static class Extensions + { + public static Customer AsEntity(this CustomerDocument document) + => new Customer(document.Id, document.Email, document.CreatedAt, document.FirstName, document.LastName, document.Address, + document.SourceAddress, document.IsVip, document.State, document.CompletedOrders); + + public static CustomerDocument AsDocument(this Customer entity) + => new CustomerDocument + { + Id = entity.Id, + Email = entity.Email, + FirstName = entity.FirstName, + LastName = entity.LastName, + Address = entity.Address, + SourceAddress = entity.SourceAddress, + IsVip = entity.IsVip, + State = entity.State, + CreatedAt = entity.CreatedAt, + CompletedOrders = entity.CompletedOrders + }; + + public static CustomerDto AsDto(this CustomerDocument document) + => new CustomerDto + { + Id = document.Id, + State = document.State.ToString().ToLowerInvariant(), + CreatedAt = document.CreatedAt, + }; + + public static CustomerDetailsDto AsDetailsDto(this CustomerDocument document) + => new CustomerDetailsDto + { + Id = document.Id, + Email = document.Email, + FirstName = document.FirstName, + LastName = document.LastName, + Address = document.Address, + SourceAddress = document.SourceAddress, + IsVip = document.IsVip, + State = document.State.ToString().ToLowerInvariant(), + CreatedAt = document.CreatedAt, + CompletedOrders = document.CompletedOrders + }; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Queries/GetCustomerHandler.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Queries/GetCustomerHandler.cs new file mode 100644 index 0000000..6505586 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Queries/GetCustomerHandler.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Customers.Application.Dto; +using SwiftParcel.Services.Customers.Application.Queries; +using SwiftParcel.Services.Customers.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Customers.Infrastructure.Mongo.Queries +{ + public class GetCustomerHandler : IQueryHandler + { + private readonly IMongoRepository _customerRepository; + + public GetCustomerHandler(IMongoRepository customerRepository) + { + _customerRepository = customerRepository; + } + + public async Task HandleAsync(GetCustomer query, CancellationToken cancellationToken) + { + var document = await _customerRepository.GetAsync(p => p.Id == query.CustomerId); + + return document?.AsDetailsDto(); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Queries/GetCustomerStateHandler.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Queries/GetCustomerStateHandler.cs new file mode 100644 index 0000000..d99ef7c --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Queries/GetCustomerStateHandler.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Customers.Application.Dto; +using SwiftParcel.Services.Customers.Application.Queries; +using SwiftParcel.Services.Customers.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Customers.Infrastructure.Mongo.Queries +{ + public class GetCustomerStateHandler : IQueryHandler + { + private readonly IMongoRepository _customerRepository; + + public GetCustomerStateHandler(IMongoRepository customerRepository) + { + _customerRepository = customerRepository; + } + + public async Task HandleAsync(GetCustomerState query, CancellationToken cancellationToken) + { + var document = await _customerRepository.GetAsync(p => p.Id == query.CustomerId); + + return document is null + ? null + : new CustomerStateDto + { + Id = document.Id, + State = document.State.ToString().ToLowerInvariant() + }; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Queries/GetCustomersHandler.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Queries/GetCustomersHandler.cs new file mode 100644 index 0000000..2ef1a65 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Queries/GetCustomersHandler.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Customers.Application.Dto; +using SwiftParcel.Services.Customers.Application.Queries; +using SwiftParcel.Services.Customers.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Customers.Infrastructure.Mongo.Queries +{ + public class GetCustomersHandler : IQueryHandler> + { + private readonly IMongoRepository _customerRepository; + + public GetCustomersHandler(IMongoRepository customerRepository) + { + _customerRepository = customerRepository; + } + + public async Task> HandleAsync(GetCustomers query, CancellationToken cancellationToken) + { + var customers = await _customerRepository.FindAsync(_ => true); + + return customers.Select(p => p.AsDto()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Repositories/CustomerMongoRepository.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Repositories/CustomerMongoRepository.cs new file mode 100644 index 0000000..d9d02cc --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Mongo/Repositories/CustomerMongoRepository.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Customers.Core.Entities; +using SwiftParcel.Services.Customers.Core.Repositories; +using SwiftParcel.Services.Customers.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Customers.Infrastructure.Mongo.Repositories +{ + public class CustomerMongoRepository : ICustomerRepository + { + private readonly IMongoRepository _repository; + + public CustomerMongoRepository(IMongoRepository repository) + { + _repository = repository; + } + + public async Task GetAsync(Guid id) + { + var customer = await _repository.GetAsync(o => o.Id == id); + + return customer?.AsEntity(); + } + + public Task AddAsync(Customer customer) => _repository.AddAsync(customer.AsDocument()); + public Task UpdateAsync(Customer customer) => _repository.UpdateAsync(customer.AsDocument()); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Services/DateTimeProvider.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Services/DateTimeProvider.cs new file mode 100644 index 0000000..9c11dbf --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Services/DateTimeProvider.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Customers.Application.Services; + +namespace SwiftParcel.Services.Customers.Infrastructure.Services +{ + public class DateTimeProvider : IDateTimeProvider + { + public DateTime Now => DateTime.UtcNow; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Services/EventMapper.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Services/EventMapper.cs new file mode 100644 index 0000000..e5fd84c --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Services/EventMapper.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using SwiftParcel.Services.Customers.Application.Services; +using SwiftParcel.Services.Customers.Core; +using SwiftParcel.Services.Customers.Core.Events; + +namespace SwiftParcel.Services.Customers.Infrastructure.Services +{ + public class EventMapper : IEventMapper + { + public IEnumerable MapAll(IEnumerable events) + => events.Select(Map); + + public IEvent Map(IDomainEvent @event) + { + switch (@event) + { + case CustomerRegistrationCompleted e: return new Application.Events.CustomerCreated(e.Customer.Id, e.Customer.Email, e.Customer.FullName); + case CustomerBecameVip e: return new Application.Events.CustomerBecameVip(e.Customer.Id); + case CustomerStateChanged e: + return new Application.Events.CustomerStateChanged(e.Customer.Id, + e.Customer.State.ToString().ToLowerInvariant(), e.PreviousState.ToString().ToLowerInvariant()); + } + + return null; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Services/MessageBroker.cs b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Services/MessageBroker.cs new file mode 100644 index 0000000..e66c522 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/Services/MessageBroker.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; +using Convey.MessageBrokers.RabbitMQ; +using DnsClient.Internal; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using OpenTracing; +using SwiftParcel.Services.Customers.Application.Services; + +namespace SwiftParcel.Services.Customers.Infrastructure.Services +{ + internal sealed class MessageBroker: IMessageBroker + { + private const string DefaultSpanContextHeader = "span_context"; + private readonly IBusPublisher _busPublisher; + private readonly IMessageOutbox _outbox; + private readonly ICorrelationContextAccessor _contextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IMessagePropertiesAccessor _messagePropertiesAccessor; + private readonly ITracer _tracer; + private readonly ILogger _logger; + private readonly string _spanContextHeader; + + public MessageBroker(IBusPublisher busPublisher, IMessageOutbox outbox, + ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor, + IMessagePropertiesAccessor messagePropertiesAccessor, RabbitMqOptions options, ITracer tracer, + ILogger logger) + { + _busPublisher = busPublisher; + _outbox = outbox; + _contextAccessor = contextAccessor; + _httpContextAccessor = httpContextAccessor; + _messagePropertiesAccessor = messagePropertiesAccessor; + _tracer = tracer; + _logger = logger; + _spanContextHeader = string.IsNullOrWhiteSpace(options.SpanContextHeader) + ? DefaultSpanContextHeader + : options.SpanContextHeader; + } + + public Task PublishAsync(params IEvent[] events) => PublishAsync(events?.AsEnumerable()); + + public async Task PublishAsync(IEnumerable events) + { + if (events is null) + { + return; + } + + var messageProperties = _messagePropertiesAccessor.MessageProperties; + var originatedMessageId = messageProperties?.MessageId; + var correlationId = messageProperties?.CorrelationId; + var spanContext = messageProperties?.GetSpanContext(_spanContextHeader); + if (string.IsNullOrWhiteSpace(spanContext)) + { + spanContext = _tracer.ActiveSpan is null ? string.Empty : _tracer.ActiveSpan.Context.ToString(); + } + + var headers = messageProperties.GetHeadersToForward(); + var correlationContext = _contextAccessor.CorrelationContext ?? + _httpContextAccessor.GetCorrelationContext(); + + foreach (var @event in events) + { + if (@event is null) + { + continue; + } + + var messageId = Guid.NewGuid().ToString("N"); + _logger.LogTrace($"Publishing integration event: {@event.GetType().Name} [id: '{messageId}']."); + if (_outbox.Enabled) + { + await _outbox.SendAsync(@event, originatedMessageId, messageId, correlationId, spanContext, + correlationContext, headers); + continue; + } + + await _busPublisher.PublishAsync(@event, messageId, correlationId, spanContext, correlationContext, + headers); + } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure.csproj b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure.csproj new file mode 100644 index 0000000..25a3d75 --- /dev/null +++ b/SwiftParcel.Services.Customers/src/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure/SwiftParcel.Services.Customers.Infrastructure.csproj @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + net6.0 + enable + disable + + + diff --git a/SwiftParcel.Services.Deliveries/SwiftParcel.Services.Deliveries.sln b/SwiftParcel.Services.Deliveries/SwiftParcel.Services.Deliveries.sln new file mode 100644 index 0000000..7fe235b --- /dev/null +++ b/SwiftParcel.Services.Deliveries/SwiftParcel.Services.Deliveries.sln @@ -0,0 +1,60 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8CF5EFC4-6281-4357-8C3A-B1E76A740B6B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Deliveries.Api", "SwiftParcel.Services.Deliveries.Api", "{9D9C5046-02C7-417D-AE23-6B1D364E7EBA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Deliveries.Api", "src\SwiftParcel.Services.Deliveries.Api\SwiftParcel.Services.Deliveries.Api\SwiftParcel.Services.Deliveries.Api.csproj", "{2924E480-08A7-4A7D-9821-E5D710660A96}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Deliveries.Application", "SwiftParcel.Services.Deliveries.Application", "{C28033BD-CF72-4566-932A-235C856CE576}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Deliveries.Application", "src\SwiftParcel.Services.Deliveries.Application\SwiftParcel.Services.Deliveries.Application\SwiftParcel.Services.Deliveries.Application.csproj", "{39B95208-BBAB-48E8-AE83-6579E2FB5503}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Deliveries.Core", "SwiftParcel.Services.Deliveries.Core", "{964A9128-B734-4C0F-87AC-54A263E1E9A8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Deliveries.Core", "src\SwiftParcel.Services.Deliveries.Core\SwiftParcel.Services.Deliveries.Core\SwiftParcel.Services.Deliveries.Core.csproj", "{417C28D1-C94A-48BC-8658-B9B4FB86219A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Deliveries.Infrastructure", "SwiftParcel.Services.Deliveries.Infrastructure", "{DC0D3516-721E-4D7F-8745-9FF0F9F38C9E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Deliveries.Infrastructure", "src\SwiftParcel.Services.Deliveries.Infrastructure\SwiftParcel.Services.Deliveries.Infrastructure\SwiftParcel.Services.Deliveries.Infrastructure.csproj", "{028BE7AA-BF51-4DC3-A9EF-37EA075A76E9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2924E480-08A7-4A7D-9821-E5D710660A96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2924E480-08A7-4A7D-9821-E5D710660A96}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2924E480-08A7-4A7D-9821-E5D710660A96}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2924E480-08A7-4A7D-9821-E5D710660A96}.Release|Any CPU.Build.0 = Release|Any CPU + {39B95208-BBAB-48E8-AE83-6579E2FB5503}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39B95208-BBAB-48E8-AE83-6579E2FB5503}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39B95208-BBAB-48E8-AE83-6579E2FB5503}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39B95208-BBAB-48E8-AE83-6579E2FB5503}.Release|Any CPU.Build.0 = Release|Any CPU + {417C28D1-C94A-48BC-8658-B9B4FB86219A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {417C28D1-C94A-48BC-8658-B9B4FB86219A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {417C28D1-C94A-48BC-8658-B9B4FB86219A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {417C28D1-C94A-48BC-8658-B9B4FB86219A}.Release|Any CPU.Build.0 = Release|Any CPU + {028BE7AA-BF51-4DC3-A9EF-37EA075A76E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {028BE7AA-BF51-4DC3-A9EF-37EA075A76E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {028BE7AA-BF51-4DC3-A9EF-37EA075A76E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {028BE7AA-BF51-4DC3-A9EF-37EA075A76E9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {9D9C5046-02C7-417D-AE23-6B1D364E7EBA} = {8CF5EFC4-6281-4357-8C3A-B1E76A740B6B} + {2924E480-08A7-4A7D-9821-E5D710660A96} = {9D9C5046-02C7-417D-AE23-6B1D364E7EBA} + {C28033BD-CF72-4566-932A-235C856CE576} = {8CF5EFC4-6281-4357-8C3A-B1E76A740B6B} + {39B95208-BBAB-48E8-AE83-6579E2FB5503} = {C28033BD-CF72-4566-932A-235C856CE576} + {964A9128-B734-4C0F-87AC-54A263E1E9A8} = {8CF5EFC4-6281-4357-8C3A-B1E76A740B6B} + {417C28D1-C94A-48BC-8658-B9B4FB86219A} = {964A9128-B734-4C0F-87AC-54A263E1E9A8} + {DC0D3516-721E-4D7F-8745-9FF0F9F38C9E} = {8CF5EFC4-6281-4357-8C3A-B1E76A740B6B} + {028BE7AA-BF51-4DC3-A9EF-37EA075A76E9} = {DC0D3516-721E-4D7F-8745-9FF0F9F38C9E} + EndGlobalSection +EndGlobal diff --git a/SwiftParcel.Services.Deliveries/scripts/build.sh b/SwiftParcel.Services.Deliveries/scripts/build.sh new file mode 100755 index 0000000..3affad0 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/scripts/build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +dotnet build -c release \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/scripts/start.sh b/SwiftParcel.Services.Deliveries/scripts/start.sh new file mode 100755 index 0000000..6d77f41 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/scripts/start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=local +cd ../src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api +dotnet run \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/scripts/test.sh b/SwiftParcel.Services.Deliveries/scripts/test.sh new file mode 100755 index 0000000..6046c35 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/scripts/test.sh @@ -0,0 +1,2 @@ +#!/bin/bash +dotnet test \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api/Program.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api/Program.cs new file mode 100644 index 0000000..02a464f --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api/Program.cs @@ -0,0 +1,47 @@ +using System.Threading.Tasks; +using Convey; +using Convey.Secrets.Vault; +using Convey.Logging; +using Convey.Types; +using Convey.WebApi; +using Convey.WebApi.CQRS; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using SwiftParcel.Services.Deliveries.Application; +using SwiftParcel.Services.Deliveries.Application.Commands; +using SwiftParcel.Services.Deliveries.Application.DTO; +using SwiftParcel.Services.Deliveries.Application.Queries; +using SwiftParcel.Services.Deliveries.Infrastructure; + +namespace SwiftParcel.Services.Deliveries.Api +{ + public class Program + { + public static async Task Main(string[] args) + => await WebHost.CreateDefaultBuilder(args) + .ConfigureServices(services => services + .AddConvey() + .AddWebApi() + .AddApplication() + .AddInfrastructure() + .Build()) + .Configure(app => app + .UseInfrastructure() + .UseDispatcherEndpoints(endpoints => endpoints + .Get("", ctx => ctx.Response.WriteAsync(ctx.RequestServices.GetService().Name)) + .Get("deliveries/{deliveryId}") + .Get>("deliveries") + .Get>("deliveries/pending") + .Post("deliveries/{deliveryId}/courier", + afterDispatch: (cmd, ctx) => ctx.Response.Ok($"deliveries/{cmd.DeliveryId}")) + .Post("deliveries/{deliveryId}/pick-up") + .Post("deliveries/{deliveryId}/fail") + .Post("deliveries/{deliveryId}/complete"))) + .UseLogging() + .UseVault() + .Build() + .RunAsync(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api/Properties/launchSettings.json b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api/Properties/launchSettings.json new file mode 100644 index 0000000..c8beaad --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:14146", + "sslPort": 44357 + } + }, + "profiles": { + "SwiftParcel.Services.Deliveries.Api": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5003", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api.csproj b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api.csproj new file mode 100644 index 0000000..e2ac8dd --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api.csproj @@ -0,0 +1,20 @@ + + + + net6.0 + disable + enable + + + + + + + + + + + + + + diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api/appsettings.Development.json b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api/appsettings.json b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api/appsettings.json new file mode 100644 index 0000000..43cd284 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Api/SwiftParcel.Services.Deliveries.Api/appsettings.json @@ -0,0 +1,194 @@ +{ + "app": { + "name": "SwiftParcel Deliveries Service", + "service": "deliveries-service", + "version": "1" + }, + "consul": { + "enabled": true, + "url": "http://localhost:8500", + "service": "deliveries-service", + "address": "docker.for.win.localhost", + "port": "5004", + "pingEnabled": true, + "pingEndpoint": "ping", + "pingInterval": 3, + "removeAfterInterval": 3 + }, + "fabio": { + "enabled": true, + "url": "http://localhost:9999", + "service": "deliveries-service" + }, + "httpClient": { + "type": "fabio", + "retries": 3, + "services": {}, + "requestMasking": { + "enabled": true, + "maskTemplate": "*****" + } + }, + "logger": { + "level": "information", + "excludePaths": ["/", "/ping", "/metrics"], + "excludeProperties": [ + "api_key", + "access_key", + "ApiKey", + "ApiSecret", + "ClientId", + "ClientSecret", + "ConnectionString", + "Password", + "Email", + "Login", + "Secret", + "Token" + ], + "console": { + "enabled": true + }, + "elk": { + "enabled": false, + "url": "http://localhost:9200" + }, + "file": { + "enabled": true, + "path": "logs/logs.txt", + "interval": "day" + }, + "seq": { + "enabled": true, + "url": "http://localhost:5341", + "apiKey": "secret" + }, + "tags": {} + }, + "jaeger": { + "enabled": true, + "serviceName": "deliveries", + "udpHost": "localhost", + "udpPort": 6831, + "maxPacketSize": 0, + "sampler": "const", + "excludePaths": ["/", "/ping", "/metrics"] + }, + "jwt": { + "certificate": { + "location": "certs/localhost.cer" + }, + "validIssuer": "swiftparcel", + "validateAudience": false, + "validateIssuer": true, + "validateLifetime": true + }, + "metrics": { + "enabled": true, + "influxEnabled": false, + "prometheusEnabled": true, + "influxUrl": "http://localhost:8086", + "database": "swiftparcel", + "env": "local", + "interval": 5 + }, + "mongo": { + "connectionString": "mongodb+srv://andrii-courier-db-user:piotr-amadeusz-andrii-2023@cluster0.br51nsv.mongodb.net/?retryWrites=true&w=majority", + "database": "deliveries-service", + "seed": false + }, + "outbox": { + "enabled": false, + "type": "sequential", + "expiry": 3600, + "intervalMilliseconds": 2000, + "inboxCollection": "inbox", + "outboxCollection": "outbox", + "disableTransactions": true + }, + "rabbitMq": { + "connectionName": "deliveries-service", + "retries": 3, + "retryInterval": 2, + "conventionsCasing": "snakeCase", + "logger": { + "enabled": true + }, + "username": "guest", + "password": "guest", + "virtualHost": "/", + "port": 5672, + "hostnames": [ + "localhost" + ], + "requestedConnectionTimeout": "00:00:30", + "requestedHeartbeat": "00:01:00", + "socketReadTimeout": "00:00:30", + "socketWriteTimeout": "00:00:30", + "continuationTimeout": "00:00:20", + "handshakeContinuationTimeout": "00:00:10", + "networkRecoveryInterval": "00:00:05", + "exchange": { + "declare": true, + "durable": true, + "autoDelete": false, + "type": "topic", + "name": "deliveries" + }, + "queue": { + "declare": true, + "durable": true, + "exclusive": false, + "autoDelete": false, + "template": "deliveries-service/{{exchange}}.{{message}}" + }, + "context": { + "enabled": true, + "header": "message_context" + }, + "spanContextHeader": "span_context" + }, + "redis": { + "connectionString": "localhost", + "instance": "deliveries:" + }, + "swagger": { + "enabled": true, + "reDocEnabled": false, + "name": "v1", + "title": "API", + "version": "v1", + "routePrefix": "docs", + "includeSecurity": true + }, + "vault": { + "enabled": false, + "url": "http://localhost:8200", + "authType": "token", + "token": "secret", + "username": "user", + "password": "secret", + "kv": { + "enabled": true, + "engineVersion": 2, + "mountPoint": "kv", + "path": "deliveries-service/settings" + }, + "pki": { + "enabled": true, + "roleName": "deliveries-service", + "commonName": "deliveries-service.swiftparcel.io" + }, + "lease": { + "mongo": { + "type": "database", + "roleName": "deliveries-service", + "enabled": true, + "autoRenewal": true, + "templates": { + "connectionString": "mongodb://{{username}}:{{password}}@localhost:27017" + } + } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/AssignCourierToDelivery.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/AssignCourierToDelivery.cs new file mode 100644 index 0000000..f80fb68 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/AssignCourierToDelivery.cs @@ -0,0 +1,15 @@ +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.Deliveries.Application.Commands +{ + public class AssignCourierToDelivery: ICommand + { + public Guid DeliveryId { get; } + public Guid CourierId { get; } + public AssignCourierToDelivery(Guid deliveryId, Guid courierId) + { + DeliveryId = deliveryId; + CourierId = courierId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/CompleteDelivery.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/CompleteDelivery.cs new file mode 100644 index 0000000..fac1fd4 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/CompleteDelivery.cs @@ -0,0 +1,16 @@ +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.Deliveries.Application.Commands +{ + public class CompleteDelivery : ICommand + { + public Guid DeliveryId { get; } + public DateTime DeliveryAttemptDate { get; } + + public CompleteDelivery(Guid deliveryId, DateTime deliveryAttemptDate) + { + DeliveryId = deliveryId; + DeliveryAttemptDate = deliveryAttemptDate; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/FailDelivery.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/FailDelivery.cs new file mode 100644 index 0000000..9c82fa1 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/FailDelivery.cs @@ -0,0 +1,18 @@ +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.Deliveries.Application.Commands +{ + public class FailDelivery : ICommand + { + public Guid DeliveryId { get; } + public DateTime DeliveryAttemptDate { get; } + public string Reason { get; } + + public FailDelivery(Guid deliveryId, DateTime deliveryAttemptDate, string reason) + { + DeliveryId = deliveryId; + DeliveryAttemptDate = deliveryAttemptDate; + Reason = reason; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/Handlers/AssignCourierToDeliveryHandler.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/Handlers/AssignCourierToDeliveryHandler.cs new file mode 100644 index 0000000..92c4bfa --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/Handlers/AssignCourierToDeliveryHandler.cs @@ -0,0 +1,37 @@ +using Convey.CQRS.Commands; +using SwiftParcel.Services.Deliveries.Application.Exceptions; +using SwiftParcel.Services.Deliveries.Application.Services; +using SwiftParcel.Services.Deliveries.Core.Repositories; + +namespace SwiftParcel.Services.Deliveries.Application.Commands.Handlers +{ + internal sealed class AssignCourierToDeliveryHandler: ICommandHandler + { + private readonly IDeliveriesRepository _repository; + private readonly IMessageBroker _messageBroker; + private readonly IEventMapper _eventMapper; + private readonly IDateTimeProvider _dateTimeProvider; + public AssignCourierToDeliveryHandler(IDeliveriesRepository repository, IMessageBroker messageBroker, + IEventMapper eventMapper, IDateTimeProvider dateTimeProvider) + { + _repository = repository; + _messageBroker = messageBroker; + _eventMapper = eventMapper; + _dateTimeProvider = dateTimeProvider; + } + + public async Task HandleAsync(AssignCourierToDelivery command) + { + var delivery = await _repository.GetAsync(command.DeliveryId); + if (delivery is null) + { + throw new DeliveryNotFoundException(command.DeliveryId); + } + + delivery.AssignCourier(_dateTimeProvider.Now, command.CourierId); + await _repository.UpdateAsync(delivery); + var events = _eventMapper.MapAll(delivery.Events); + await _messageBroker.PublishAsync(events.ToArray()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/Handlers/CompleteDeliveryHandler.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/Handlers/CompleteDeliveryHandler.cs new file mode 100644 index 0000000..5fac85a --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/Handlers/CompleteDeliveryHandler.cs @@ -0,0 +1,38 @@ +using Convey.CQRS.Commands; +using SwiftParcel.Services.Deliveries.Application.Exceptions; +using SwiftParcel.Services.Deliveries.Application.Services; +using SwiftParcel.Services.Deliveries.Core.Repositories; + +namespace SwiftParcel.Services.Deliveries.Application.Commands.Handlers +{ + internal sealed class CompleteDeliveryHandler : ICommandHandler + { + private readonly IDeliveriesRepository _repository; + private readonly IMessageBroker _messageBroker; + private readonly IEventMapper _eventMapper; + private readonly IDateTimeProvider _dateTimeProvider; + + public CompleteDeliveryHandler(IDeliveriesRepository repository, IMessageBroker messageBroker, + IEventMapper eventMapper, IDateTimeProvider dateTimeProvider) + { + _repository = repository; + _messageBroker = messageBroker; + _eventMapper = eventMapper; + _dateTimeProvider = dateTimeProvider; + } + + public async Task HandleAsync(CompleteDelivery command) + { + var delivery = await _repository.GetAsync(command.DeliveryId); + if (delivery is null) + { + throw new DeliveryNotFoundException(command.DeliveryId); + } + + delivery.Complete(_dateTimeProvider.Now, command.DeliveryAttemptDate); + await _repository.UpdateAsync(delivery); + var events = _eventMapper.MapAll(delivery.Events); + await _messageBroker.PublishAsync(events.ToArray()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/Handlers/FailDeliveryHandler.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/Handlers/FailDeliveryHandler.cs new file mode 100644 index 0000000..77e486c --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/Handlers/FailDeliveryHandler.cs @@ -0,0 +1,38 @@ +using Convey.CQRS.Commands; +using SwiftParcel.Services.Deliveries.Application.Exceptions; +using SwiftParcel.Services.Deliveries.Application.Services; +using SwiftParcel.Services.Deliveries.Core.Repositories; + +namespace SwiftParcel.Services.Deliveries.Application.Commands.Handlers +{ + internal sealed class FailDeliveryHandler : ICommandHandler + { + private readonly IDeliveriesRepository _repository; + private readonly IMessageBroker _messageBroker; + private readonly IEventMapper _eventMapper; + private readonly IDateTimeProvider _dateTimeProvider; + + public FailDeliveryHandler(IDeliveriesRepository repository, IMessageBroker messageBroker, + IEventMapper eventMapper, IDateTimeProvider dateTimeProvider) + { + _repository = repository; + _messageBroker = messageBroker; + _eventMapper = eventMapper; + _dateTimeProvider = dateTimeProvider; + } + + public async Task HandleAsync(FailDelivery command) + { + var delivery = await _repository.GetAsync(command.DeliveryId); + if (delivery is null) + { + throw new DeliveryNotFoundException(command.DeliveryId); + } + + delivery.Fail(_dateTimeProvider.Now, command.DeliveryAttemptDate, command.Reason); + await _repository.UpdateAsync(delivery); + var events = _eventMapper.MapAll(delivery.Events); + await _messageBroker.PublishAsync(events.ToArray()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/Handlers/PickUpDeliveryHandler.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/Handlers/PickUpDeliveryHandler.cs new file mode 100644 index 0000000..9bf80fd --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/Handlers/PickUpDeliveryHandler.cs @@ -0,0 +1,37 @@ +using Convey.CQRS.Commands; +using SwiftParcel.Services.Deliveries.Application.Exceptions; +using SwiftParcel.Services.Deliveries.Application.Services; +using SwiftParcel.Services.Deliveries.Core.Entities; +using SwiftParcel.Services.Deliveries.Core.Repositories; + +namespace SwiftParcel.Services.Deliveries.Application.Commands.Handlers +{ + public class PickUpDeliveryHandler : ICommandHandler + { + private readonly IDeliveriesRepository _repository; + private readonly IMessageBroker _messageBroker; + private readonly IEventMapper _eventMapper; + private readonly IDateTimeProvider _dateTimeProvider; + public PickUpDeliveryHandler(IDeliveriesRepository repository, IMessageBroker messageBroker, + IEventMapper eventMapper, IDateTimeProvider dateTimeProvider) + { + _repository = repository; + _messageBroker = messageBroker; + _eventMapper = eventMapper; + _dateTimeProvider = dateTimeProvider; + } + public async Task HandleAsync(PickUpDelivery command) + { + var delivery = await _repository.GetAsync(command.DeliveryId); + if (delivery is null) + { + throw new DeliveryNotFoundException(command.DeliveryId); + } + + delivery.PickUp(_dateTimeProvider.Now); + await _repository.UpdateAsync(delivery); + var events = _eventMapper.MapAll(delivery.Events); + await _messageBroker.PublishAsync(events.ToArray()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/Handlers/StartDeliveryHandler.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/Handlers/StartDeliveryHandler.cs new file mode 100644 index 0000000..0b0a897 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/Handlers/StartDeliveryHandler.cs @@ -0,0 +1,41 @@ +using Convey.CQRS.Commands; +using SwiftParcel.Services.Deliveries.Application.Exceptions; +using SwiftParcel.Services.Deliveries.Application.Services; +using SwiftParcel.Services.Deliveries.Core.Entities; +using SwiftParcel.Services.Deliveries.Core.Repositories; + +namespace SwiftParcel.Services.Deliveries.Application.Commands.Handlers +{ + internal sealed class StartDeliveryHandler : ICommandHandler + { + private readonly IDeliveriesRepository _repository; + private readonly IMessageBroker _messageBroker; + private readonly IEventMapper _eventMapper; + private readonly IDateTimeProvider _dateTimeProvider; + + public StartDeliveryHandler(IDeliveriesRepository repository, IMessageBroker messageBroker, + IEventMapper eventMapper, IDateTimeProvider dateTimeProvider) + { + _repository = repository; + _messageBroker = messageBroker; + _eventMapper = eventMapper; + _dateTimeProvider = dateTimeProvider; + } + + public async Task HandleAsync(StartDelivery command) + { + var delivery = await _repository.GetForOrderAsync(command.OrderId); + if (delivery is {} && delivery.Status != DeliveryStatus.CannotDeliver) + { + throw new DeliveryAlreadyStartedException(command.OrderId); + } + + delivery = Delivery.Create(command.DeliveryId, command.OrderId, _dateTimeProvider.Now, + DeliveryStatus.Unassigned, command.Volume, command.Weight, command.Source, command.Destination, + command.Priority, command.AtWeekend, command.PickupDate, command.DeliveryDate); + await _repository.AddAsync(delivery); + var events = _eventMapper.MapAll(delivery.Events); + await _messageBroker.PublishAsync(events.ToArray()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/PickUpDelivery.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/PickUpDelivery.cs new file mode 100644 index 0000000..22791fe --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/PickUpDelivery.cs @@ -0,0 +1,12 @@ +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.Deliveries.Application.Commands +{ + public class PickUpDelivery: ICommand + { + public Guid DeliveryId { get; } + + public PickUpDelivery(Guid deliveryId) + => DeliveryId = deliveryId; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/StartDelivery.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/StartDelivery.cs new file mode 100644 index 0000000..5e9bc44 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Commands/StartDelivery.cs @@ -0,0 +1,34 @@ +using Convey.CQRS.Commands; +using SwiftParcel.Services.Deliveries.Core.Entities; + +namespace SwiftParcel.Services.Deliveries.Application.Commands +{ + public class StartDelivery : ICommand + { + public Guid DeliveryId { get; } + public Guid OrderId { get; } + public double Volume { get; protected set; } + public double Weight { get; protected set; } + public Address Source { get; protected set; } + public Address Destination { get; protected set; } + public Priority Priority { get; protected set; } + public bool AtWeekend { get; protected set; } + public DateTime PickupDate { get; protected set; } + public DateTime DeliveryDate { get; protected set; } + + public StartDelivery(Guid deliveryId, Guid orderId, double volume, double weight, Address source, + Address destination, Priority priority, bool atWeekend, DateTime pickupDate, DateTime deliveryDate) + { + DeliveryId = deliveryId == Guid.Empty ? Guid.NewGuid() : deliveryId; + OrderId = orderId; + Volume = volume; + Weight = weight; + Source = source; + Destination = destination; + Priority = priority; + AtWeekend = atWeekend; + PickupDate = pickupDate; + DeliveryDate = deliveryDate; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/ContractAttribute.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/ContractAttribute.cs new file mode 100644 index 0000000..32073b3 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/ContractAttribute.cs @@ -0,0 +1,7 @@ +namespace SwiftParcel.Services.Deliveries.Application +{ + public class ContractAttribute:Attribute + { + + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/DTO/AddressDto.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/DTO/AddressDto.cs new file mode 100644 index 0000000..32e0cb2 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/DTO/AddressDto.cs @@ -0,0 +1,12 @@ +namespace SwiftParcel.Services.Deliveries.Application.DTO +{ + public class AddressDto + { + public string Street { get; set; } + public string BuildingNumber { get; set; } + public string ApartmentNumber { get; set; } + public string ZipCode { get; set; } + public string City { get; set; } + public string Country { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/DTO/DeliveryDto.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/DTO/DeliveryDto.cs new file mode 100644 index 0000000..585b7c6 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/DTO/DeliveryDto.cs @@ -0,0 +1,23 @@ +using SwiftParcel.Services.Deliveries.Core.Entities; + +namespace SwiftParcel.Services.Deliveries.Application.DTO +{ + public class DeliveryDto + { + public Guid Id { get; set; } + public Guid OrderId { get; set; } + public Guid? CourierId { get; set; } + public string Status { get; set; } + public double Volume { get; set; } + public double Weight { get; set; } + public AddressDto Source { get; set; } + public AddressDto Destination { get; set; } + public string Priority { get; set; } + public bool AtWeekend { get; set; } + public DateTime PickupDate { get; set; } + public DateTime DeliveryDate { get; set; } + public DateTime? DeliveryAttemptDate { get; set; } + public string CannotDeliverReason { get; set; } + public DateTime LastUpdate { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/DeliveryCompleted.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/DeliveryCompleted.cs new file mode 100644 index 0000000..cf2d8c3 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/DeliveryCompleted.cs @@ -0,0 +1,18 @@ +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Deliveries.Application.Events +{ + public class DeliveryCompleted : IEvent + { + public Guid DeliveryId { get; } + public Guid OrderId { get; } + public DateTime DateTime { get; } + + public DeliveryCompleted(Guid deliveryId, Guid orderId, DateTime dateTime) + { + DeliveryId = deliveryId; + OrderId = orderId; + DateTime = dateTime; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/DeliveryFailed.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/DeliveryFailed.cs new file mode 100644 index 0000000..c6b12f6 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/DeliveryFailed.cs @@ -0,0 +1,20 @@ +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Deliveries.Application.Events +{ + public class DeliveryFailed : IEvent + { + public Guid DeliveryId { get; } + public Guid OrderId { get; } + public DateTime DateTime { get; } + public string Reason { get; } + + public DeliveryFailed(Guid deliveryId, Guid orderId, DateTime dateTime, string reason) + { + DeliveryId = deliveryId; + OrderId = orderId; + DateTime = dateTime; + Reason = reason; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/DeliveryPickedUp.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/DeliveryPickedUp.cs new file mode 100644 index 0000000..ec1f430 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/DeliveryPickedUp.cs @@ -0,0 +1,18 @@ +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Deliveries.Application.Events +{ + public class DeliveryPickedUp : IEvent + { + public Guid DeliveryId { get; } + public Guid OrderId { get; } + public DateTime DateTime { get; } + + public DeliveryPickedUp(Guid deliveryId, Guid orderId, DateTime dateTime) + { + DeliveryId = deliveryId; + OrderId = orderId; + DateTime = dateTime; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/DeliveryStarted.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/DeliveryStarted.cs new file mode 100644 index 0000000..f02b471 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/DeliveryStarted.cs @@ -0,0 +1,18 @@ +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Deliveries.Application.Events +{ + public class DeliveryStarted : IEvent + { + public Guid DeliveryId { get; } + public Guid OrderId { get; } + public DateTime DateTime { get; } + + public DeliveryStarted(Guid id, Guid orderId, DateTime dateTime) + { + DeliveryId = id; + OrderId = orderId; + DateTime = dateTime; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/External/Handlers/OrderApprovedHandler.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/External/Handlers/OrderApprovedHandler.cs new file mode 100644 index 0000000..5e8b887 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/External/Handlers/OrderApprovedHandler.cs @@ -0,0 +1,22 @@ +using Convey.CQRS.Commands; +using Convey.CQRS.Events; +using SwiftParcel.Services.Deliveries.Application.Commands; + +namespace SwiftParcel.Services.Deliveries.Application.Events.External.Handlers +{ + public class OrderApprovedHandler: IEventHandler + { + ICommandDispatcher _commandDispatcher; + public OrderApprovedHandler(ICommandDispatcher commandDispatcher) + { + _commandDispatcher = commandDispatcher; + } + + public async Task HandleAsync(OrderApproved @event) + { + var volume = @event.Width * @event.Height * @event.Depth; + await _commandDispatcher.SendAsync(new StartDelivery(Guid.Empty, @event.OrderId, volume, + @event.Weight, @event.Source, @event.Destination, @event.Priority, @event.AtWeekend, @event.PickupDate, @event.DeliveryDate)); + } + } +} diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/External/OrderApproved.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/External/OrderApproved.cs new file mode 100644 index 0000000..edf06dd --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/External/OrderApproved.cs @@ -0,0 +1,37 @@ +using Convey.MessageBrokers; +using Convey.CQRS.Events; +using SwiftParcel.Services.Deliveries.Core.Entities; + +namespace SwiftParcel.Services.Deliveries.Application.Events.External +{ + [Message("orders")] + public class OrderApproved: IEvent + { + public Guid OrderId { get; set; } + public double Width { get; protected set; } + public double Height { get; protected set; } + public double Depth { get; protected set; } + public double Weight { get; set; } + public Address Source { get; set; } + public Address Destination { get; set; } + public Priority Priority { get; set; } + public bool AtWeekend { get; set; } + public DateTime PickupDate { get; set; } + public DateTime DeliveryDate { get; set; } + public OrderApproved(Guid orderId, double width, double height, double depth, double weight, Address source, + Address destination, Priority priority, bool atWeekend, DateTime pickupDate, DateTime deliveryDate) + { + OrderId = orderId; + Width = width; + Height = height; + Depth = depth; + Weight = weight; + Source = source; + Destination = destination; + Priority = priority; + AtWeekend = atWeekend; + PickupDate = pickupDate; + DeliveryDate = deliveryDate; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/Rejected/AssignCourierToDeliveryRejected.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/Rejected/AssignCourierToDeliveryRejected.cs new file mode 100644 index 0000000..6aac831 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/Rejected/AssignCourierToDeliveryRejected.cs @@ -0,0 +1,18 @@ +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Deliveries.Application.Events.Rejected +{ + public class AssignCourierToDeliveryRejected: IRejectedEvent + { + public Guid DeliveryId { get; } + public string Reason { get; } + public string Code { get; } + + public AssignCourierToDeliveryRejected(Guid deliveryId, string reason, string code) + { + DeliveryId = deliveryId; + Reason = reason; + Code = code; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/Rejected/CompleteDeliveryRejected.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/Rejected/CompleteDeliveryRejected.cs new file mode 100644 index 0000000..4c8b30f --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/Rejected/CompleteDeliveryRejected.cs @@ -0,0 +1,18 @@ +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Deliveries.Application.Events.Rejected +{ + public class CompleteDeliveryRejected : IRejectedEvent + { + public Guid DeliveryId { get; } + public string Reason { get; } + public string Code { get; } + + public CompleteDeliveryRejected(Guid deliveryId, string reason, string code) + { + DeliveryId = deliveryId; + Reason = reason; + Code = code; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/Rejected/FailDeliveryRejected.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/Rejected/FailDeliveryRejected.cs new file mode 100644 index 0000000..e815627 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/Rejected/FailDeliveryRejected.cs @@ -0,0 +1,18 @@ +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Deliveries.Application.Events.Rejected +{ + public class FailDeliveryRejected : IRejectedEvent + { + public Guid DeliveryId { get; } + public string Reason { get; } + public string Code { get; } + + public FailDeliveryRejected(Guid deliveryId, string reason, string code) + { + DeliveryId = deliveryId; + Reason = reason; + Code = code; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/Rejected/PickUpDeliveryRejected.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/Rejected/PickUpDeliveryRejected.cs new file mode 100644 index 0000000..8d717fb --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/Rejected/PickUpDeliveryRejected.cs @@ -0,0 +1,18 @@ +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Deliveries.Application.Events.Rejected +{ + public class PickUpDeliveryRejected : IRejectedEvent + { + public Guid DeliveryId { get; } + public string Reason { get; } + public string Code { get; } + + public PickUpDeliveryRejected(Guid deliveryId, string reason, string code) + { + DeliveryId = deliveryId; + Reason = reason; + Code = code; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/Rejected/StartDeliveryRejected.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/Rejected/StartDeliveryRejected.cs new file mode 100644 index 0000000..ad71710 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Events/Rejected/StartDeliveryRejected.cs @@ -0,0 +1,20 @@ +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Deliveries.Application.Events.Rejected +{ + public class StartDeliveryRejected : IRejectedEvent + { + public Guid DeliveryId { get; } + public Guid OrderId { get; } + public string Reason { get; } + public string Code { get; } + + public StartDeliveryRejected(Guid deliveryId, Guid orderId, string reason, string code) + { + DeliveryId = deliveryId; + OrderId = orderId; + Reason = reason; + Code = code; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Exceptions/AppException.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Exceptions/AppException.cs new file mode 100644 index 0000000..6278566 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Exceptions/AppException.cs @@ -0,0 +1,11 @@ +namespace SwiftParcel.Services.Deliveries.Application.Exceptions +{ + public abstract class AppException : Exception + { + public virtual string Code { get; } + + protected AppException(string message) : base(message) + { + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Exceptions/DeliveryAlreadyStarted.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Exceptions/DeliveryAlreadyStarted.cs new file mode 100644 index 0000000..766318d --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Exceptions/DeliveryAlreadyStarted.cs @@ -0,0 +1,11 @@ +namespace SwiftParcel.Services.Deliveries.Application.Exceptions +{ + public class DeliveryAlreadyStartedException : AppException + { + public override string Code { get; } = "delivery_already_started"; + + public DeliveryAlreadyStartedException(Guid orderId) : base($"Delivery for order: {orderId} was already started.") + { + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Exceptions/DeliveryNotFound.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Exceptions/DeliveryNotFound.cs new file mode 100644 index 0000000..d7a5a10 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Exceptions/DeliveryNotFound.cs @@ -0,0 +1,11 @@ +namespace SwiftParcel.Services.Deliveries.Application.Exceptions +{ + public class DeliveryNotFoundException : AppException + { + public override string Code { get; } = "delivery_not_found"; + + public DeliveryNotFoundException(Guid deliveryId) : base($"Delivery with id: {deliveryId} was not found.") + { + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Extensions.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Extensions.cs new file mode 100644 index 0000000..2ffb1ae --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Extensions.cs @@ -0,0 +1,16 @@ +using Convey; +using Convey.CQRS.Commands; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Deliveries.Application +{ + public static class Extensions + { + public static IConveyBuilder AddApplication(this IConveyBuilder builder) + => builder + .AddCommandHandlers() + .AddEventHandlers() + .AddInMemoryCommandDispatcher() + .AddInMemoryEventDispatcher(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/IAppContext.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/IAppContext.cs new file mode 100644 index 0000000..95789a1 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/IAppContext.cs @@ -0,0 +1,8 @@ +namespace SwiftParcel.Services.Deliveries.Application +{ + public interface IAppContext + { + string RequestId { get; } + IIdentityContext Identity { get; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/IIdentityContext.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/IIdentityContext.cs new file mode 100644 index 0000000..6b1aef5 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/IIdentityContext.cs @@ -0,0 +1,12 @@ +namespace SwiftParcel.Services.Deliveries.Application +{ + public interface IIdentityContext + { + Guid Id { get; } + string Role { get; } + bool IsAuthenticated { get; } + bool IsOfficeWorker { get; } + bool IsCourier { get; } + IDictionary Claims { get; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Queries/GetDeliveries.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Queries/GetDeliveries.cs new file mode 100644 index 0000000..54a1bbf --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Queries/GetDeliveries.cs @@ -0,0 +1,10 @@ +using Convey.CQRS.Queries; +using SwiftParcel.Services.Deliveries.Application.DTO; + +namespace SwiftParcel.Services.Deliveries.Application.Queries +{ + public class GetDeliveries : IQuery> + { + public Guid CourierId { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Queries/GetDeliveriesPending.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Queries/GetDeliveriesPending.cs new file mode 100644 index 0000000..cf18cba --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Queries/GetDeliveriesPending.cs @@ -0,0 +1,10 @@ +using Convey.CQRS.Queries; +using SwiftParcel.Services.Deliveries.Application.DTO; + +namespace SwiftParcel.Services.Deliveries.Application.Queries +{ + public class GetDeliveriesPending : IQuery> + { + + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Queries/GetDelivery.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Queries/GetDelivery.cs new file mode 100644 index 0000000..5abf79f --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Queries/GetDelivery.cs @@ -0,0 +1,10 @@ +using Convey.CQRS.Queries; +using SwiftParcel.Services.Deliveries.Application.DTO; + +namespace SwiftParcel.Services.Deliveries.Application.Queries +{ + public class GetDelivery : IQuery + { + public Guid DeliveryId { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Services/IDateTimeProvider.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Services/IDateTimeProvider.cs new file mode 100644 index 0000000..c5fd1fb --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Services/IDateTimeProvider.cs @@ -0,0 +1,7 @@ +namespace SwiftParcel.Services.Deliveries.Application.Services +{ + public interface IDateTimeProvider + { + DateTime Now { get; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Services/IEventMapper.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Services/IEventMapper.cs new file mode 100644 index 0000000..833cf68 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Services/IEventMapper.cs @@ -0,0 +1,11 @@ +using Convey.CQRS.Events; +using SwiftParcel.Services.Deliveries.Core.Events; + +namespace SwiftParcel.Services.Deliveries.Application.Services +{ + public interface IEventMapper + { + IEvent Map(IDomainEvent @event); + IEnumerable MapAll(IEnumerable events); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Services/IMessageBroker.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Services/IMessageBroker.cs new file mode 100644 index 0000000..c6158ae --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/Services/IMessageBroker.cs @@ -0,0 +1,10 @@ +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Deliveries.Application.Services +{ + public interface IMessageBroker + { + Task PublishAsync(params IEvent[] events); + Task PublishAsync(IEnumerable events); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application.csproj b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application.csproj new file mode 100644 index 0000000..cadc927 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application/SwiftParcel.Services.Deliveries.Application.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + enable + disable + + + + + + + + + + + + + + + + diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/Address.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/Address.cs new file mode 100644 index 0000000..3e2ea78 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/Address.cs @@ -0,0 +1,12 @@ +namespace SwiftParcel.Services.Deliveries.Core.Entities +{ + public class Address + { + public string Street { get; set; } + public string BuildingNumber { get; set; } + public string ApartmentNumber { get; set; } + public string City { get; set; } + public string ZipCode { get; set; } + public string Country { get; set;} + } +} diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/AggregateId.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/AggregateId.cs new file mode 100644 index 0000000..1a3e8c3 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/AggregateId.cs @@ -0,0 +1,51 @@ +using System; +using SwiftParcel.Services.Deliveries.Core.Exceptions; + +namespace SwiftParcel.Services.Deliveries.Core.Entities +{ + public class AggregateId : IEquatable + { + public Guid Value { get; } + + public AggregateId() + { + Value = Guid.NewGuid(); + } + + public AggregateId(Guid value) + { + if (value == Guid.Empty) + { + throw new InvalidAggregateIdException(); + } + + Value = value; + } + + public bool Equals(AggregateId other) + { + if (ReferenceEquals(null, other)) return false; + return ReferenceEquals(this, other) || Value.Equals(other.Value); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((AggregateId) obj); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public static implicit operator Guid(AggregateId id) + => id.Value; + + public static implicit operator AggregateId(Guid id) + => new AggregateId(id); + + public override string ToString() => Value.ToString(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/AggregateRoot.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/AggregateRoot.cs new file mode 100644 index 0000000..b2074e5 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/AggregateRoot.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Linq; +using SwiftParcel.Services.Deliveries.Core.Events; + +namespace SwiftParcel.Services.Deliveries.Core.Entities +{ + public interface IAggregateRoot + { + IEnumerable Events { get; } + AggregateId Id { get; } + int Version { get; } + void IncrementVersion(); + } + + public abstract class AggregateRoot : IAggregateRoot + { + private readonly List _events = new List(); + public IEnumerable Events => _events; + public AggregateId Id { get; protected set; } + public int Version { get; protected set; } + public bool HasChanged => _events.Any(); + + protected void AddEvent(IDomainEvent @event) + { + _events.Add(@event); + } + + public void ClearEvents() => _events.Clear(); + + void IAggregateRoot.IncrementVersion() + => Version++; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/Delivery.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/Delivery.cs new file mode 100644 index 0000000..c1f4b9c --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/Delivery.cs @@ -0,0 +1,110 @@ +using SwiftParcel.Services.Deliveries.Core.Exceptions; +using SwiftParcel.Services.Deliveries.Core.Events; + +namespace SwiftParcel.Services.Deliveries.Core.Entities +{ + public class Delivery : AggregateRoot + { + public Guid OrderId { get; protected set; } + public Guid? CourierId { get; protected set; } + public DeliveryStatus Status { get; protected set; } + public double Volume { get; protected set; } + public double Weight { get; protected set; } + public Address Source { get; protected set; } + public Address Destination { get; protected set; } + public Priority Priority { get; protected set; } + public bool AtWeekend { get; protected set; } + public DateTime PickupDate { get; protected set; } + public DateTime DeliveryDate { get; protected set; } + public DateTime? DeliveryAttemptDate { get; protected set; } + public string CannotDeliverReason { get; protected set; } + public DateTime LastUpdate { get; protected set; } + + public Delivery(AggregateId id, Guid orderId, Guid? courierId, DateTime createdAt, DeliveryStatus status, + double volume, double weight, Address source, Address destination, Priority priority, + bool atWeekend, DateTime pickupDate, DateTime deliveryDate, DateTime? deliveryAttemptDate, + string cannotDeliverReason) + { + Id = id; + OrderId = orderId; + LastUpdate = createdAt; + CourierId = courierId; + Status = status; + Volume = volume; + Weight = weight; + Source = source; + Destination = destination; + Priority = priority; + AtWeekend = atWeekend; + PickupDate = pickupDate; + DeliveryDate = deliveryDate; + DeliveryAttemptDate = deliveryAttemptDate; + CannotDeliverReason = cannotDeliverReason; + } + + public static Delivery Create(AggregateId id, Guid orderId, DateTime updateDateTime, DeliveryStatus status, + double volume, double weight, Address source, Address destination, Priority priority, + bool atWeekend, DateTime pickupDate, DateTime deliveryDate) + { + var delivery = new Delivery(id, orderId, null, updateDateTime, status, volume, weight, source, destination, + priority, atWeekend, pickupDate, deliveryDate, null, null); + delivery.AddEvent(new DeliveryStateChanged(delivery)); + + return delivery; + } + public void AssignCourier(DateTime updateDateTime, Guid courierId) + { + if(Status is not DeliveryStatus.Unassigned) + { + throw new CannotChangeDeliveryStateException(Id, Status, DeliveryStatus.Assigned); + } + if (CourierId != null) + { + throw new DeliveryHasAlreadyAssignedCourierException(Id, CourierId.Value); + } + + CourierId = courierId; + Status = DeliveryStatus.Assigned; + LastUpdate = updateDateTime; + AddEvent(new DeliveryStateChanged(this)); + } + public void PickUp(DateTime updateDateTime) + { + if(Status is not DeliveryStatus.Assigned) + { + throw new CannotChangeDeliveryStateException(Id, Status, DeliveryStatus.InProgress); + } + + Status = DeliveryStatus.InProgress; + LastUpdate = updateDateTime; + AddEvent(new DeliveryStateChanged(this)); + } + + public void Complete(DateTime updateDateTime, DateTime deliveryAttemptDate) + { + if (Status is not DeliveryStatus.InProgress) + { + throw new CannotChangeDeliveryStateException(Id, Status, DeliveryStatus.Completed); + } + + Status = DeliveryStatus.Completed; + LastUpdate = updateDateTime; + DeliveryAttemptDate = deliveryAttemptDate; + AddEvent(new DeliveryStateChanged(this)); + } + + public void Fail(DateTime updateDateTime, DateTime deliveryAttemptDate, string reason) + { + if(Status is not DeliveryStatus.InProgress) + { + throw new CannotChangeDeliveryStateException(Id, Status, DeliveryStatus.CannotDeliver); + } + + Status = DeliveryStatus.CannotDeliver; + LastUpdate = updateDateTime; + CannotDeliverReason = reason; + DeliveryAttemptDate = deliveryAttemptDate; + AddEvent(new DeliveryStateChanged(this)); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/DeliveryStatus.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/DeliveryStatus.cs new file mode 100644 index 0000000..ecd0548 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/DeliveryStatus.cs @@ -0,0 +1,11 @@ +namespace SwiftParcel.Services.Deliveries.Core.Entities +{ + public enum DeliveryStatus + { + Unassigned, + Assigned, + InProgress, + Completed, + CannotDeliver + } +} diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/Priority.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/Priority.cs new file mode 100644 index 0000000..69692b1 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Entities/Priority.cs @@ -0,0 +1,8 @@ +namespace SwiftParcel.Services.Deliveries.Core.Entities +{ + public enum Priority + { + Low, + High + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Events/DeliveryStateChanged.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Events/DeliveryStateChanged.cs new file mode 100644 index 0000000..5f0d148 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Events/DeliveryStateChanged.cs @@ -0,0 +1,14 @@ +using SwiftParcel.Services.Deliveries.Core.Entities; + +namespace SwiftParcel.Services.Deliveries.Core.Events +{ + public class DeliveryStateChanged : IDomainEvent + { + public Delivery Delivery { get; } + + public DeliveryStateChanged(Delivery delivery) + { + Delivery = delivery; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Events/IDomainEvent.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Events/IDomainEvent.cs new file mode 100644 index 0000000..4af8946 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Events/IDomainEvent.cs @@ -0,0 +1,6 @@ +namespace SwiftParcel.Services.Deliveries.Core.Events; + +public interface IDomainEvent +{ + +} diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Exceptions/CannotChangeDeliveryStateException.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Exceptions/CannotChangeDeliveryStateException.cs new file mode 100644 index 0000000..20e0a40 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Exceptions/CannotChangeDeliveryStateException.cs @@ -0,0 +1,14 @@ +using SwiftParcel.Services.Deliveries.Core.Entities; + +namespace SwiftParcel.Services.Deliveries.Core.Exceptions +{ + public class CannotChangeDeliveryStateException : DomainException + { + public override string Code { get; } = "cannot_change_delivery_state"; + + public CannotChangeDeliveryStateException(Guid id, DeliveryStatus currentStatus, DeliveryStatus nextStatus) : + base($"Cannot change state for delivery with id: '{id}' from {currentStatus} to {nextStatus}'") + { + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Exceptions/DeliveryHasAlreadyAssignedCourierException.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Exceptions/DeliveryHasAlreadyAssignedCourierException.cs new file mode 100644 index 0000000..69f5de6 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Exceptions/DeliveryHasAlreadyAssignedCourierException.cs @@ -0,0 +1,11 @@ +namespace SwiftParcel.Services.Deliveries.Core.Exceptions +{ + public class DeliveryHasAlreadyAssignedCourierException : DomainException + { + public override string Code { get; } = "delivery_has_already_assigned_courier"; + public DeliveryHasAlreadyAssignedCourierException(Guid id, Guid courierId) + : base($"Delivery with id:'{id}' has already assigned courier with id:'{courierId}'.") + { + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Exceptions/DomainException.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Exceptions/DomainException.cs new file mode 100644 index 0000000..5c54425 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Exceptions/DomainException.cs @@ -0,0 +1,11 @@ +namespace SwiftParcel.Services.Deliveries.Core.Exceptions +{ + public abstract class DomainException : Exception + { + public virtual string Code { get; } + + protected DomainException(string message) : base(message) + { + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Exceptions/InvalidAggregateIdException.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Exceptions/InvalidAggregateIdException.cs new file mode 100644 index 0000000..57b537f --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Exceptions/InvalidAggregateIdException.cs @@ -0,0 +1,11 @@ +namespace SwiftParcel.Services.Deliveries.Core.Exceptions +{ + public class InvalidAggregateIdException : DomainException + { + public override string Code { get; } = "invalid_aggregate_id"; + + public InvalidAggregateIdException() : base($"Invalid aggregate id.") + { + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Repositories/IDeliveriesRepository.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Repositories/IDeliveriesRepository.cs new file mode 100644 index 0000000..f19812d --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/Repositories/IDeliveriesRepository.cs @@ -0,0 +1,13 @@ +using SwiftParcel.Services.Deliveries.Core.Entities; + +namespace SwiftParcel.Services.Deliveries.Core.Repositories +{ + public interface IDeliveriesRepository + { + Task GetAsync(Guid id); + Task GetForOrderAsync(Guid number); + Task AddAsync(Delivery delivery); + Task UpdateAsync(Delivery delivery); + Task DeleteAsync(Delivery delivery); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core.csproj b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core.csproj new file mode 100644 index 0000000..141e38f --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core/SwiftParcel.Services.Deliveries.Core.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + disable + + + diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Contexts/AppContext.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Contexts/AppContext.cs new file mode 100644 index 0000000..0389ea7 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Contexts/AppContext.cs @@ -0,0 +1,27 @@ +using SwiftParcel.Services.Deliveries.Application; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Contexts +{ + internal class AppContext : IAppContext + { + public string RequestId { get; } + public IIdentityContext Identity { get; } + + internal AppContext() : this(Guid.NewGuid().ToString("N"), IdentityContext.Empty) + { + } + + internal AppContext(CorrelationContext context) : this(context.CorrelationId, + context.User is null ? IdentityContext.Empty : new IdentityContext(context.User)) + { + } + + internal AppContext(string requestId, IIdentityContext identity) + { + RequestId = requestId; + Identity = identity; + } + + internal static IAppContext Empty => new AppContext(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Contexts/AppContextFactory.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Contexts/AppContextFactory.cs new file mode 100644 index 0000000..0d5bab5 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Contexts/AppContextFactory.cs @@ -0,0 +1,35 @@ +using Convey.MessageBrokers; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using SwiftParcel.Services.Deliveries.Application; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Contexts +{ + internal sealed class AppContextFactory : IAppContextFactory + { + private readonly ICorrelationContextAccessor _contextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + + public AppContextFactory(ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor) + { + _contextAccessor = contextAccessor; + _httpContextAccessor = httpContextAccessor; + } + + public IAppContext Create() + { + if (_contextAccessor.CorrelationContext is {}) + { + var payload = JsonConvert.SerializeObject(_contextAccessor.CorrelationContext); + + return string.IsNullOrWhiteSpace(payload) + ? AppContext.Empty + : new AppContext(JsonConvert.DeserializeObject(payload)); + } + + var context = _httpContextAccessor.GetCorrelationContext(); + + return context is null ? AppContext.Empty : new AppContext(context); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Contexts/CorrelationContext.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Contexts/CorrelationContext.cs new file mode 100644 index 0000000..002fb52 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Contexts/CorrelationContext.cs @@ -0,0 +1,22 @@ +namespace SwiftParcel.Services.Deliveries.Infrastructure.Contexts +{ + internal class CorrelationContext + { + public string CorrelationId { get; set; } + public string SpanContext { get; set; } + public UserContext User { get; set; } + public string ResourceId { get; set; } + public string TraceId { get; set; } + public string ConnectionId { get; set; } + public string Name { get; set; } + public DateTime CreatedAt { get; set; } + + public class UserContext + { + public string Id { get; set; } + public bool IsAuthenticated { get; set; } + public string Role { get; set; } + public IDictionary Claims { get; set; } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Contexts/IdentityContext.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Contexts/IdentityContext.cs new file mode 100644 index 0000000..924edf0 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Contexts/IdentityContext.cs @@ -0,0 +1,35 @@ +using SwiftParcel.Services.Deliveries.Application; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Contexts +{ + internal class IdentityContext : IIdentityContext + { + public Guid Id { get; } + public string Role { get; } = string.Empty; + public bool IsAuthenticated { get; } + public bool IsOfficeWorker { get; } + public bool IsCourier { get; } + public IDictionary Claims { get; } = new Dictionary(); + + internal IdentityContext() + { + } + + internal IdentityContext(CorrelationContext.UserContext context) + : this(context.Id, context.Role, context.IsAuthenticated, context.Claims) + { + } + + internal IdentityContext(string id, string role, bool isAuthenticated, IDictionary claims) + { + Id = Guid.TryParse(id, out var userId) ? userId : Guid.Empty; + Role = role ?? string.Empty; + IsAuthenticated = isAuthenticated; + IsOfficeWorker = Role.Equals("officeworker", StringComparison.InvariantCultureIgnoreCase); + IsCourier = Role.Equals("courier", StringComparison.InvariantCultureIgnoreCase); + Claims = claims ?? new Dictionary(); + } + + internal static IIdentityContext Empty => new IdentityContext(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs new file mode 100644 index 0000000..629d291 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -0,0 +1,33 @@ +using Convey.CQRS.Commands; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Decorators +{ + internal sealed class OutboxCommandHandlerDecorator : ICommandHandler + where TCommand : class, ICommand + { + private readonly ICommandHandler _handler; + private readonly IMessageOutbox _outbox; + private readonly string _messageId; + private readonly bool _enabled; + + public OutboxCommandHandlerDecorator(ICommandHandler handler, IMessageOutbox outbox, + OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) + { + _handler = handler; + _outbox = outbox; + _enabled = outboxOptions.Enabled; + + var messageProperties = messagePropertiesAccessor.MessageProperties; + _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId) + ? Guid.NewGuid().ToString("N") + : messageProperties.MessageId; + } + + public Task HandleAsync(TCommand command) + => _enabled + ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(command)) + : _handler.HandleAsync(command); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs new file mode 100644 index 0000000..6d710b5 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -0,0 +1,33 @@ +using Convey.CQRS.Events; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Decorators +{ + internal sealed class OutboxEventHandlerDecorator : IEventHandler + where TEvent : class, IEvent + { + private readonly IEventHandler _handler; + private readonly IMessageOutbox _outbox; + private readonly string _messageId; + private readonly bool _enabled; + + public OutboxEventHandlerDecorator(IEventHandler handler, IMessageOutbox outbox, + OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) + { + _handler = handler; + _outbox = outbox; + _enabled = outboxOptions.Enabled; + + var messageProperties = messagePropertiesAccessor.MessageProperties; + _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId) + ? Guid.NewGuid().ToString("N") + : messageProperties.MessageId; + } + + public Task HandleAsync(TEvent @event) + => _enabled + ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(@event)) + : _handler.HandleAsync(@event); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Exceptions/ExceptionToMessageMapper.cs new file mode 100644 index 0000000..9e22c03 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -0,0 +1,44 @@ +using Convey.MessageBrokers.RabbitMQ; +using SwiftParcel.Services.Deliveries.Application.Commands; +using SwiftParcel.Services.Deliveries.Application.Events.Rejected; +using SwiftParcel.Services.Deliveries.Application.Exceptions; +using SwiftParcel.Services.Deliveries.Core.Exceptions; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Exceptions +{ + public class ExceptionToMessageMapper : IExceptionToMessageMapper + { + public object Map(Exception exception, object message) + => exception switch + { + CannotChangeDeliveryStateException ex => message switch + { + StartDelivery command => new StartDeliveryRejected(command.DeliveryId, command.OrderId, ex.Message, ex.Code), + CompleteDelivery command => new CompleteDeliveryRejected(command.DeliveryId, ex.Message, ex.Code), + FailDelivery command => new FailDeliveryRejected(command.DeliveryId, ex.Message, ex.Code), + AssignCourierToDelivery command => new AssignCourierToDeliveryRejected(command.DeliveryId, ex.Message, ex.Code), + PickUpDelivery command => new PickUpDeliveryRejected(command.DeliveryId, ex.Message, ex.Code), + _ => null + }, + DeliveryAlreadyStartedException ex => message switch + { + StartDelivery command => new StartDeliveryRejected(command.DeliveryId, command.OrderId, ex.Message, ex.Code), + _ => null, + }, + DeliveryHasAlreadyAssignedCourierException ex => message switch + { + AssignCourierToDelivery command => new AssignCourierToDeliveryRejected(command.DeliveryId, ex.Message, ex.Code), + _ => null, + }, + DeliveryNotFoundException ex => message switch + { + CompleteDelivery command => new CompleteDeliveryRejected(command.DeliveryId, ex.Message, ex.Code), + FailDelivery command => new FailDeliveryRejected(command.DeliveryId, ex.Message, ex.Code), + AssignCourierToDelivery command => new AssignCourierToDeliveryRejected(command.DeliveryId, ex.Message, ex.Code), + PickUpDelivery command => new PickUpDeliveryRejected(command.DeliveryId, ex.Message, ex.Code), + _ => null + }, + _ => null + }; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Exceptions/ExceptionToResponseMapper.cs new file mode 100644 index 0000000..d2d5ae7 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -0,0 +1,46 @@ +using System.Collections.Concurrent; +using System.Net; +using Convey; +using Convey.WebApi.Exceptions; +using SwiftParcel.Services.Deliveries.Core.Exceptions; +using SwiftParcel.Services.Deliveries.Application.Exceptions; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Exceptions +{ + internal sealed class ExceptionToResponseMapper : IExceptionToResponseMapper + { + private static readonly ConcurrentDictionary Codes = new ConcurrentDictionary(); + + public ExceptionResponse Map(Exception exception) + => exception switch + { + DomainException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message}, + HttpStatusCode.BadRequest), + AppException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message}, + HttpStatusCode.BadRequest), + _ => new ExceptionResponse(new {code = "error", reason = "There was an error."}, + HttpStatusCode.BadRequest) + }; + + private static string GetCode(Exception exception) + { + var type = exception.GetType(); + if (Codes.TryGetValue(type, out var code)) + { + return code; + } + + var exceptionCode = exception switch + { + DomainException domainException when !string.IsNullOrWhiteSpace(domainException.Code) => domainException + .Code, + AppException appException when !string.IsNullOrWhiteSpace(appException.Code) => appException.Code, + _ => exception.GetType().Name.Underscore().Replace("_exception", string.Empty) + }; + + Codes.TryAdd(type, exceptionCode); + + return exceptionCode; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Extensions.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Extensions.cs new file mode 100644 index 0000000..ff5dbc1 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Extensions.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Convey; +using Convey.CQRS.Commands; +using Convey.CQRS.Events; +using Convey.CQRS.Queries; +using Convey.Discovery.Consul; +using Convey.Docs.Swagger; +using Convey.HTTP; +using Convey.LoadBalancing.Fabio; +using Convey.MessageBrokers; +using Convey.MessageBrokers.CQRS; +using Convey.MessageBrokers.Outbox; +using Convey.MessageBrokers.Outbox.Mongo; +using Convey.MessageBrokers.RabbitMQ; +using Convey.Metrics.AppMetrics; +using Convey.Persistence.MongoDB; +using Convey.Persistence.Redis; +using Convey.Security; +using Convey.Tracing.Jaeger; +using Convey.Tracing.Jaeger.RabbitMQ; +using Convey.WebApi; +using Convey.WebApi.CQRS; +using Convey.WebApi.Swagger; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using SwiftParcel.Services.Deliveries.Application; +using SwiftParcel.Services.Deliveries.Application.Commands; +using SwiftParcel.Services.Deliveries.Core.Repositories; +using SwiftParcel.Services.Deliveries.Application.Services; +using SwiftParcel.Services.Deliveries.Infrastructure.Contexts; +using SwiftParcel.Services.Deliveries.Infrastructure.Decorators; +using SwiftParcel.Services.Deliveries.Infrastructure.Exceptions; +using SwiftParcel.Services.Deliveries.Infrastructure.Logging; +using SwiftParcel.Services.Deliveries.Infrastructure.Mongo.Documents; +using SwiftParcel.Services.Deliveries.Infrastructure.Mongo.Repositories; +using SwiftParcel.Services.Deliveries.Infrastructure.Services; +using SwiftParcel.Services.Deliveries.Application.Events.External; + +namespace SwiftParcel.Services.Deliveries.Infrastructure +{ + public static class Extensions + { + public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + { + builder.Services.AddSingleton(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddSingleton(); + builder.Services.AddTransient(); + builder.Services.AddTransient(ctx => ctx.GetRequiredService().Create()); + builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>)); + builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>)); + + return builder + .AddErrorHandler() + .AddQueryHandlers() + .AddInMemoryQueryDispatcher() + .AddHttpClient() + .AddConsul() + .AddFabio() + .AddRabbitMq(plugins: p => p.AddJaegerRabbitMqPlugin()) + .AddMessageOutbox(o => o.AddMongo()) + .AddExceptionToMessageMapper() + .AddMongo() + .AddRedis() + .AddMetrics() + .AddJaeger() + .AddHandlersLogging() + .AddMongoRepository("deliveries") + .AddWebApiSwaggerDocs() + .AddSecurity(); + } + + public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app) + { + app.UseErrorHandler() + .UseSwaggerDocs() + .UseJaeger() + .UseConvey() + .UsePublicContracts() + .UseMetrics() + .UseRabbitMq() + .SubscribeCommand() + .SubscribeCommand() + .SubscribeCommand() + .SubscribeCommand() + .SubscribeCommand() + .SubscribeEvent(); + + return app; + } + + internal static CorrelationContext GetCorrelationContext(this IHttpContextAccessor accessor) + => accessor.HttpContext?.Request.Headers.TryGetValue("Correlation-Context", out var json) is true + ? JsonConvert.DeserializeObject(json.FirstOrDefault()) + : null; + + internal static IDictionary GetHeadersToForward(this IMessageProperties messageProperties) + { + const string sagaHeader = "Saga"; + if (messageProperties?.Headers is null || !messageProperties.Headers.TryGetValue(sagaHeader, out var saga)) + { + return null; + } + + return saga is null + ? null + : new Dictionary + { + [sagaHeader] = saga + }; + } + + internal static string GetSpanContext(this IMessageProperties messageProperties, string header) + { + if (messageProperties is null) + { + return string.Empty; + } + + if (messageProperties.Headers.TryGetValue(header, out var span) && span is byte[] spanBytes) + { + return Encoding.UTF8.GetString(spanBytes); + } + + return string.Empty; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/IAppContextFactory.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/IAppContextFactory.cs new file mode 100644 index 0000000..c603da2 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/IAppContextFactory.cs @@ -0,0 +1,9 @@ +using SwiftParcel.Services.Deliveries.Application; + +namespace SwiftParcel.Services.Deliveries.Infrastructure +{ + public interface IAppContextFactory + { + IAppContext Create(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Logging/Extensions.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Logging/Extensions.cs new file mode 100644 index 0000000..4e670ad --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Logging/Extensions.cs @@ -0,0 +1,21 @@ +using Convey; +using Convey.Logging.CQRS; +using Microsoft.Extensions.DependencyInjection; +using SwiftParcel.Services.Deliveries.Application.Commands; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Logging +{ + internal static class Extensions + { + public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + { + var assembly = typeof(CompleteDelivery).Assembly; + + builder.Services.AddSingleton(new MessageToLogTemplateMapper()); + + return builder + .AddCommandHandlersLogging(assembly) + .AddEventHandlersLogging(assembly); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Logging/MessageToLogTemplateMapper.cs new file mode 100644 index 0000000..c521819 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -0,0 +1,49 @@ +using Convey.Logging.CQRS; +using SwiftParcel.Services.Deliveries.Application.Commands; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Logging +{ + internal sealed class MessageToLogTemplateMapper : IMessageToLogTemplateMapper + { + private static IReadOnlyDictionary MessageTemplates + => new Dictionary + { + { + typeof(CompleteDelivery), new HandlerLogTemplate + { + After = "Completed the delivery with id: {DeliveryId}." + } + }, + { + typeof(FailDelivery), new HandlerLogTemplate + { + After = "Failed the delivery with id: {DeliveryId}, reason: {Reason}" + } + }, + { + typeof(StartDelivery), new HandlerLogTemplate + { + After = "Started the delivery with id: {DeliveryId}." + } + }, + { + typeof(PickUpDelivery), new HandlerLogTemplate + { + After = "Picked up the delivery with id: {DeliveryId}." + } + }, + { + typeof(AssignCourierToDelivery), new HandlerLogTemplate + { + After = "Assigned courier with id: {CourierId} to the delivery with id: {DeliveryId}." + } + } + }; + + public HandlerLogTemplate Map(TMessage message) where TMessage : class + { + var key = message.GetType(); + return MessageTemplates.TryGetValue(key, out var template) ? template : null; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Documents/DeliveryDocument.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Documents/DeliveryDocument.cs new file mode 100644 index 0000000..19cf8b5 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Documents/DeliveryDocument.cs @@ -0,0 +1,24 @@ +using Convey.Types; +using SwiftParcel.Services.Deliveries.Core.Entities; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Mongo.Documents +{ + public class DeliveryDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid OrderId { get; set; } + public Guid? CourierId { get; set; } + public DeliveryStatus Status { get; set; } + public double Volume { get; set; } + public double Weight { get; set; } + public Address Source { get; set; } + public Address Destination { get; set; } + public Priority Priority { get; set; } + public bool AtWeekend { get; set; } + public DateTime PickupDate { get; set; } + public DateTime DeliveryDate { get; set; } + public DateTime? DeliveryAttemptDate { get; set; } + public string CannotDeliverReason { get; set; } + public DateTime LastUpdate { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Documents/Extensions.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Documents/Extensions.cs new file mode 100644 index 0000000..4290ad2 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Documents/Extensions.cs @@ -0,0 +1,72 @@ +using SwiftParcel.Services.Deliveries.Application.DTO; +using SwiftParcel.Services.Deliveries.Core.Entities; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Mongo.Documents +{ + internal static class Extensions + { + public static DeliveryDocument AsDocument(this Delivery delivery) + => new DeliveryDocument + { + Id = delivery.Id, + OrderId = delivery.OrderId, + CourierId = delivery.CourierId, + Status = delivery.Status, + Volume = delivery.Volume, + Weight = delivery.Weight, + Source = delivery.Source, + Destination = delivery.Destination, + Priority = delivery.Priority, + AtWeekend = delivery.AtWeekend, + PickupDate = delivery.PickupDate, + DeliveryDate = delivery.DeliveryDate, + DeliveryAttemptDate = delivery.DeliveryAttemptDate, + CannotDeliverReason = delivery.CannotDeliverReason, + LastUpdate = delivery.LastUpdate + }; + + public static Delivery AsEntity(this DeliveryDocument document) + => new Delivery(document.Id, document.OrderId, document.CourierId, + document.LastUpdate, document.Status, document.Volume, document.Weight, + document.Source, document.Destination, document.Priority, document.AtWeekend, + document.PickupDate, document.DeliveryDate, document.DeliveryAttemptDate, + document.CannotDeliverReason); + + public static DeliveryDto AsDto(this DeliveryDocument document) + => new DeliveryDto + { + Id = document.Id, + OrderId = document.OrderId, + CourierId = document.CourierId, + Status = document.Status.ToString().ToLowerInvariant(), + Volume = document.Volume, + Weight = document.Weight, + Source = new AddressDto + { + Street = document.Source.Street, + BuildingNumber = document.Source.BuildingNumber, + ApartmentNumber = document.Source.ApartmentNumber, + City = document.Source.City, + Country = document.Source.Country, + ZipCode = document.Source.ZipCode + }, + Destination = new AddressDto + { + Street = document.Destination.Street, + BuildingNumber = document.Destination.BuildingNumber, + ApartmentNumber = document.Destination.ApartmentNumber, + City = document.Destination.City, + Country = document.Destination.Country, + ZipCode = document.Destination.ZipCode + }, + Priority = document.Priority.ToString().ToLowerInvariant(), + AtWeekend = document.AtWeekend, + PickupDate = document.PickupDate, + DeliveryDate = document.DeliveryDate, + DeliveryAttemptDate = document.DeliveryAttemptDate, + CannotDeliverReason = document.CannotDeliverReason, + LastUpdate = document.LastUpdate + }; + + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Queries/Handlers/GetDeliveriesHandler.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Queries/Handlers/GetDeliveriesHandler.cs new file mode 100644 index 0000000..4cca6bd --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Queries/Handlers/GetDeliveriesHandler.cs @@ -0,0 +1,23 @@ +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Deliveries.Application.DTO; +using SwiftParcel.Services.Deliveries.Application.Queries; +using SwiftParcel.Services.Deliveries.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Mongo.Queries.Handlers +{ + public class GetDeliveriesHandler: IQueryHandler> + { + private readonly IMongoRepository _repository; + + public GetDeliveriesHandler(IMongoRepository repository) + => _repository = repository; + + public async Task> HandleAsync(GetDeliveries query) + { + var documents = await _repository.FindAsync(d => d.CourierId == query.CourierId); + + return documents.Select(d => d.AsDto()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Queries/Handlers/GetDeliveriesPendingHandler.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Queries/Handlers/GetDeliveriesPendingHandler.cs new file mode 100644 index 0000000..1be10d7 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Queries/Handlers/GetDeliveriesPendingHandler.cs @@ -0,0 +1,25 @@ +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Deliveries.Application.DTO; +using SwiftParcel.Services.Deliveries.Application.Queries; +using SwiftParcel.Services.Deliveries.Core.Entities; +using SwiftParcel.Services.Deliveries.Infrastructure.Mongo.Documents; + + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Mongo.Queries.Handlers +{ + public class GetDeliveriesPendingHandler: IQueryHandler> + { + private readonly IMongoRepository _repository; + + public GetDeliveriesPendingHandler(IMongoRepository repository) + => _repository = repository; + + public async Task> HandleAsync(GetDeliveriesPending query) + { + var documents = await _repository.FindAsync(d => d.Status == DeliveryStatus.Unassigned); + + return documents.Select(d => d.AsDto()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Queries/Handlers/GetDeliveryHandler.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Queries/Handlers/GetDeliveryHandler.cs new file mode 100644 index 0000000..0d19011 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Queries/Handlers/GetDeliveryHandler.cs @@ -0,0 +1,23 @@ +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Deliveries.Application.DTO; +using SwiftParcel.Services.Deliveries.Application.Queries; +using SwiftParcel.Services.Deliveries.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Mongo.Queries.Handlers +{ + public class GetDeliveryHandler : IQueryHandler + { + private readonly IMongoRepository _repository; + + public GetDeliveryHandler(IMongoRepository repository) + => _repository = repository; + + public async Task HandleAsync(GetDelivery query) + { + var document = await _repository.GetAsync(d => d.Id == query.DeliveryId); + + return document?.AsDto(); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Repositories/DeliveriesMongoRepository.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Repositories/DeliveriesMongoRepository.cs new file mode 100644 index 0000000..3b2437d --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Mongo/Repositories/DeliveriesMongoRepository.cs @@ -0,0 +1,36 @@ +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Deliveries.Core.Entities; +using SwiftParcel.Services.Deliveries.Core.Repositories; +using SwiftParcel.Services.Deliveries.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Mongo.Repositories +{ + internal class DeliveriesMongoRepository : IDeliveriesRepository + { + private readonly IMongoRepository _repository; + + public DeliveriesMongoRepository(IMongoRepository repository) + => _repository = repository; + + public async Task GetAsync(Guid id) + { + var document = await _repository.GetAsync(d => d.Id == id); + return document?.AsEntity(); + } + + public async Task GetForOrderAsync(Guid id) + { + var document = await _repository.GetAsync(d => d.OrderId == id); + return document?.AsEntity(); + } + + public Task AddAsync(Delivery delivery) + => _repository.AddAsync(delivery.AsDocument()); + + public Task UpdateAsync(Delivery delivery) + => _repository.UpdateAsync(delivery.AsDocument()); + + public Task DeleteAsync(Delivery delivery) + => _repository.DeleteAsync(delivery.Id); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Services/DateTimeProvider.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Services/DateTimeProvider.cs new file mode 100644 index 0000000..342d687 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Services/DateTimeProvider.cs @@ -0,0 +1,9 @@ +using SwiftParcel.Services.Deliveries.Application.Services; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Services +{ + public class DateTimeProvider : IDateTimeProvider + { + public DateTime Now => DateTime.UtcNow; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Services/EventMapper.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Services/EventMapper.cs new file mode 100644 index 0000000..ae3ebca --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Services/EventMapper.cs @@ -0,0 +1,34 @@ +using Convey.CQRS.Events; +using SwiftParcel.Services.Deliveries.Core.Entities; +using SwiftParcel.Services.Deliveries.Core.Events; +using SwiftParcel.Services.Deliveries.Application.Events; +using SwiftParcel.Services.Deliveries.Application.Services; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Services +{ + public class EventMapper : IEventMapper + { + public IEnumerable MapAll(IEnumerable events) + => events.Select(Map); + + public IEvent Map(IDomainEvent @event) + { + switch (@event) + { + case DeliveryStateChanged e: + switch (e.Delivery.Status) + { + case DeliveryStatus.InProgress: + return new DeliveryPickedUp(e.Delivery.Id, e.Delivery.OrderId, e.Delivery.LastUpdate); + case DeliveryStatus.Completed: + return new DeliveryCompleted(e.Delivery.Id, e.Delivery.OrderId, e.Delivery.LastUpdate); + case DeliveryStatus.CannotDeliver: + return new DeliveryFailed(e.Delivery.Id, e.Delivery.OrderId, e.Delivery.LastUpdate, e.Delivery.CannotDeliverReason); + } + break; + } + + return null; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Services/MessageBroker.cs b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Services/MessageBroker.cs new file mode 100644 index 0000000..0f9d6f9 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/Services/MessageBroker.cs @@ -0,0 +1,85 @@ +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; +using Convey.MessageBrokers.RabbitMQ; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using OpenTracing; +using SwiftParcel.Services.Deliveries.Application.Services; + +namespace SwiftParcel.Services.Deliveries.Infrastructure.Services +{ + internal sealed class MessageBroker : IMessageBroker + { + private const string DefaultSpanContextHeader = "span_context"; + private readonly IBusPublisher _busPublisher; + private readonly IMessageOutbox _outbox; + private readonly ICorrelationContextAccessor _contextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IMessagePropertiesAccessor _messagePropertiesAccessor; + private readonly ITracer _tracer; + private readonly ILogger _logger; + private readonly string _spanContextHeader; + + public MessageBroker(IBusPublisher busPublisher, IMessageOutbox outbox, + ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor, + IMessagePropertiesAccessor messagePropertiesAccessor, RabbitMqOptions options, ITracer tracer, + ILogger logger) + { + _busPublisher = busPublisher; + _outbox = outbox; + _contextAccessor = contextAccessor; + _httpContextAccessor = httpContextAccessor; + _messagePropertiesAccessor = messagePropertiesAccessor; + _tracer = tracer; + _logger = logger; + _spanContextHeader = string.IsNullOrWhiteSpace(options.SpanContextHeader) + ? DefaultSpanContextHeader + : options.SpanContextHeader; + } + + public Task PublishAsync(params IEvent[] events) => PublishAsync(events?.AsEnumerable()); + + public async Task PublishAsync(IEnumerable events) + { + if (events is null) + { + return; + } + + var messageProperties = _messagePropertiesAccessor.MessageProperties; + var originatedMessageId = messageProperties?.MessageId; + var correlationId = messageProperties?.CorrelationId; + var spanContext = messageProperties?.GetSpanContext(_spanContextHeader); + if (string.IsNullOrWhiteSpace(spanContext)) + { + spanContext = _tracer.ActiveSpan is null ? string.Empty : _tracer.ActiveSpan.Context.ToString(); + } + + var headers = messageProperties.GetHeadersToForward(); + var correlationContext = _contextAccessor.CorrelationContext ?? + _httpContextAccessor.GetCorrelationContext(); + + foreach (var @event in events) + { + if (@event is null) + { + continue; + } + + var messageId = Guid.NewGuid().ToString("N"); + _logger.LogTrace($"Publishing integration event: {@event.GetType().Name} [id: '{messageId}']."); + if (_outbox.Enabled) + { + await _outbox.SendAsync(@event, originatedMessageId, messageId, correlationId, spanContext, + correlationContext, headers); + continue; + } + + await _busPublisher.PublishAsync(@event, messageId, correlationId, spanContext, correlationContext, + headers); + } + } + } +} diff --git a/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure.csproj b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure.csproj new file mode 100644 index 0000000..822c727 --- /dev/null +++ b/SwiftParcel.Services.Deliveries/src/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure/SwiftParcel.Services.Deliveries.Infrastructure.csproj @@ -0,0 +1,37 @@ + + + + net6.0 + enable + disable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SwiftParcel.Services.Orders/SwiftParcel.Services.Orders.sln b/SwiftParcel.Services.Orders/SwiftParcel.Services.Orders.sln new file mode 100644 index 0000000..1359455 --- /dev/null +++ b/SwiftParcel.Services.Orders/SwiftParcel.Services.Orders.sln @@ -0,0 +1,60 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{32E2CA73-681D-46E3-BFF4-F9DD0126E32A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Orders.Api", "SwiftParcel.Services.Orders.Api", "{6DA713C4-A10F-4248-B54C-9AE6AFB1E882}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Orders.Api", "src\SwiftParcel.Services.Orders.Api\SwiftParcel.Services.Orders.Api\SwiftParcel.Services.Orders.Api.csproj", "{C84A6BD6-2CF0-4115-ACB7-2D3691F0B55C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Orders.Application", "SwiftParcel.Services.Orders.Application", "{DAA89118-586D-4669-B1D5-BE23736822C8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Orders.Application", "src\SwiftParcel.Services.Orders.Application\SwiftParcel.Services.Orders.Application\SwiftParcel.Services.Orders.Application.csproj", "{0CC5372D-B8AC-4380-8DBE-4B650C11D567}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Orders.Core", "SwiftParcel.Services.Orders.Core", "{58C56B7C-E6D8-4CBD-B730-3F307C1DB930}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Orders.Core", "src\SwiftParcel.Services.Orders.Core\SwiftParcel.Services.Orders.Core\SwiftParcel.Services.Orders.Core.csproj", "{E894C4D2-05B4-479B-93E3-C49CDDF49B9C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Orders.Infrastructure", "SwiftParcel.Services.Orders.Infrastructure", "{69CF0B3C-64D8-44EE-AC46-7066C12E81DB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Orders.Infrastructure", "src\SwiftParcel.Services.Orders.Infrastructure\SwiftParcel.Services.Orders.Infrastructure\SwiftParcel.Services.Orders.Infrastructure.csproj", "{1256CFDD-28BC-4233-A2E2-11CC5925C154}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C84A6BD6-2CF0-4115-ACB7-2D3691F0B55C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C84A6BD6-2CF0-4115-ACB7-2D3691F0B55C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C84A6BD6-2CF0-4115-ACB7-2D3691F0B55C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C84A6BD6-2CF0-4115-ACB7-2D3691F0B55C}.Release|Any CPU.Build.0 = Release|Any CPU + {0CC5372D-B8AC-4380-8DBE-4B650C11D567}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0CC5372D-B8AC-4380-8DBE-4B650C11D567}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0CC5372D-B8AC-4380-8DBE-4B650C11D567}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0CC5372D-B8AC-4380-8DBE-4B650C11D567}.Release|Any CPU.Build.0 = Release|Any CPU + {E894C4D2-05B4-479B-93E3-C49CDDF49B9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E894C4D2-05B4-479B-93E3-C49CDDF49B9C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E894C4D2-05B4-479B-93E3-C49CDDF49B9C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E894C4D2-05B4-479B-93E3-C49CDDF49B9C}.Release|Any CPU.Build.0 = Release|Any CPU + {1256CFDD-28BC-4233-A2E2-11CC5925C154}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1256CFDD-28BC-4233-A2E2-11CC5925C154}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1256CFDD-28BC-4233-A2E2-11CC5925C154}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1256CFDD-28BC-4233-A2E2-11CC5925C154}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {6DA713C4-A10F-4248-B54C-9AE6AFB1E882} = {32E2CA73-681D-46E3-BFF4-F9DD0126E32A} + {C84A6BD6-2CF0-4115-ACB7-2D3691F0B55C} = {6DA713C4-A10F-4248-B54C-9AE6AFB1E882} + {DAA89118-586D-4669-B1D5-BE23736822C8} = {32E2CA73-681D-46E3-BFF4-F9DD0126E32A} + {0CC5372D-B8AC-4380-8DBE-4B650C11D567} = {DAA89118-586D-4669-B1D5-BE23736822C8} + {58C56B7C-E6D8-4CBD-B730-3F307C1DB930} = {32E2CA73-681D-46E3-BFF4-F9DD0126E32A} + {E894C4D2-05B4-479B-93E3-C49CDDF49B9C} = {58C56B7C-E6D8-4CBD-B730-3F307C1DB930} + {69CF0B3C-64D8-44EE-AC46-7066C12E81DB} = {32E2CA73-681D-46E3-BFF4-F9DD0126E32A} + {1256CFDD-28BC-4233-A2E2-11CC5925C154} = {69CF0B3C-64D8-44EE-AC46-7066C12E81DB} + EndGlobalSection +EndGlobal diff --git a/SwiftParcel.Services.Orders/scripts/build.sh b/SwiftParcel.Services.Orders/scripts/build.sh new file mode 100755 index 0000000..106785c --- /dev/null +++ b/SwiftParcel.Services.Orders/scripts/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=Development +cd ../src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api +dotnet build -c release \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/scripts/start.sh b/SwiftParcel.Services.Orders/scripts/start.sh new file mode 100755 index 0000000..055bfc3 --- /dev/null +++ b/SwiftParcel.Services.Orders/scripts/start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=Development +cd ../src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api +dotnet run \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/scripts/test.sh b/SwiftParcel.Services.Orders/scripts/test.sh new file mode 100644 index 0000000..09bef2f --- /dev/null +++ b/SwiftParcel.Services.Orders/scripts/test.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=Development +cd ../src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api +dotnet test \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api/Program.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api/Program.cs new file mode 100644 index 0000000..5c460cd --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api/Program.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Convey; +using Convey.WebApi; +using Convey.WebApi.CQRS; +using Convey.Types; +using Convey.CQRS.Queries; +using Convey.Logging; +using Convey.Secrets.Vault; +using SwiftParcel.Services.Orders.Infrastructure; +using SwiftParcel.Services.Orders.Application; +using SwiftParcel.Services.Orders.Application.DTO; +using SwiftParcel.Services.Orders.Application.Queries; +using SwiftParcel.Services.Orders.Application.Commands; + +namespace SwiftParcel.Services.Orders.Api +{ + public class Program + { + public static async Task Main(string[] args) + => await WebHost.CreateDefaultBuilder(args) + .ConfigureServices(services => services + .AddConvey() + .AddWebApi() + .AddApplication() + .AddInfrastructure() + .Build()) + .Configure(app => app + .UseInfrastructure() + .UseDispatcherEndpoints(endpoints => endpoints + .Get("", ctx => ctx.Response.WriteAsync(ctx.RequestServices.GetService().Name)) + .Get("orders/{orderId}") + .Get>("orders") + .Get>("orders/office-worker") + .Get("orders/{orderId}/status") + .Delete("orders/{orderId}") + .Post("orders", + afterDispatch: (cmd, ctx) => ctx.Response.Created($"orders/{cmd.OrderId}/status")) + .Post("orders/{orderId}/customer", + afterDispatch: (cmd, ctx) => ctx.Response.Created($"orders/{cmd.OrderId}")) + .Put("orders/{orderId}/approve") + .Put ("orders/{orderId}/cancel"))) + .UseLogging() + .UseVault() + .Build() + .RunAsync(); + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api/Properties/launchSettings.json b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api/Properties/launchSettings.json new file mode 100644 index 0000000..b48d03a --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:7495", + "sslPort": 44395 + } + }, + "profiles": { + "SwiftParcel.Services.Orders.Api": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "docs", + "applicationUrl": "http://localhost:5006", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api.csproj b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api.csproj new file mode 100644 index 0000000..53f60c2 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api.csproj @@ -0,0 +1,23 @@ + + + + net6.0 + disable + enable + + + + + + + + + + + + + + + + + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api/appsettings.Development.json b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api/appsettings.Development.json new file mode 100644 index 0000000..ff66ba6 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api/appsettings.json b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api/appsettings.json new file mode 100644 index 0000000..8916e75 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Api/SwiftParcel.Services.Orders.Api/appsettings.json @@ -0,0 +1,197 @@ +{ + "app": { + "name": "SwiftParcel Orders Service", + "service": "orders-service", + "version": "1" + }, + "consul": { + "enabled": true, + "url": "http://localhost:8500", + "service": "orders-service", + "address": "localhost", + "port": "5006", + "pingEnabled": false, + "pingEndpoint": "ping", + "pingInterval": 3, + "removeAfterInterval": 3 + }, + "fabio": { + "enabled": false, + "url": "http://localhost:9999", + "service": "orders-service" + }, + "httpClient": { + "type": "", + "retries": 3, + "services": { + "parcels": "parcels-service", + "couriers": "couriers-service" + }, + "requestMasking": { + "enabled": true, + "maskTemplate": "*****" + } + }, + "logger": { + "level": "information", + "excludePaths": ["/", "/ping", "/metrics"], + "excludeProperties": [ + "api_key", + "access_key", + "ApiKey", + "ApiSecret", + "ClientId", + "ClientSecret", + "ConnectionString", + "Password", + "Email", + "Login", + "Secret", + "Token" + ], + "console": { + "enabled": true + }, + "elk": { + "enabled": false, + "url": "http://localhost:9200" + }, + "file": { + "enabled": true, + "path": "logs/logs.txt", + "interval": "day" + }, + "seq": { + "enabled": true, + "url": "http://localhost:5341", + "apiKey": "secret" + }, + "tags": {} + }, + "jaeger": { + "enabled": true, + "serviceName": "orders", + "udpHost": "localhost", + "udpPort": 6831, + "maxPacketSize": 0, + "sampler": "const", + "excludePaths": ["/", "/ping", "/metrics"] + }, + "jwt": { + "certificate": { + "location": "certs/localhost.cer" + }, + "validIssuer": "swiftparcel", + "validateAudience": false, + "validateIssuer": true, + "validateLifetime": true + }, + "metrics": { + "enabled": true, + "influxEnabled": false, + "prometheusEnabled": true, + "influxUrl": "http://localhost:8086", + "database": "swiftparcel", + "env": "local", + "interval": 5 + }, + "mongo": { + "connectionString": "mongodb+srv://andrii-courier-db-user:piotr-amadeusz-andrii-2023@cluster0.br51nsv.mongodb.net/?retryWrites=true&w=majority", + "database": "orders-service", + "seed": false + }, + "outbox": { + "enabled": false, + "type": "sequential", + "expiry": 3600, + "intervalMilliseconds": 2000, + "inboxCollection": "inbox", + "outboxCollection": "outbox", + "disableTransactions": true + }, + "rabbitMq": { + "connectionName": "orders-service", + "retries": 3, + "retryInterval": 2, + "conventionsCasing": "snakeCase", + "logger": { + "enabled": true + }, + "username": "guest", + "password": "guest", + "virtualHost": "/", + "port": 5672, + "hostnames": [ + "localhost" + ], + "requestedConnectionTimeout": "00:00:30", + "requestedHeartbeat": "00:01:00", + "socketReadTimeout": "00:00:30", + "socketWriteTimeout": "00:00:30", + "continuationTimeout": "00:00:20", + "handshakeContinuationTimeout": "00:00:10", + "networkRecoveryInterval": "00:00:05", + "exchange": { + "declare": true, + "durable": true, + "autoDelete": false, + "type": "topic", + "name": "orders" + }, + "queue": { + "declare": true, + "durable": true, + "exclusive": false, + "autoDelete": false, + "template": "orders-service/{{exchange}}.{{message}}" + }, + "context": { + "enabled": true, + "header": "message_context" + }, + "spanContextHeader": "span_context" + }, + "redis": { + "connectionString": "localhost", + "instance": "orders:" + }, + "swagger": { + "enabled": true, + "reDocEnabled": false, + "name": "v1", + "title": "API", + "version": "v1", + "routePrefix": "docs", + "includeSecurity": true + }, + "vault": { + "enabled": false, + "url": "http://localhost:8200", + "authType": "token", + "token": "secret", + "username": "user", + "password": "secret", + "kv": { + "enabled": true, + "engineVersion": 2, + "mountPoint": "kv", + "path": "orders-service/settings" + }, + "pki": { + "enabled": true, + "roleName": "orders-service", + "commonName": "orders-service.swiftparcel.io" + }, + "lease": { + "mongo": { + "type": "database", + "roleName": "orders-service", + "enabled": true, + "autoRenewal": true, + "templates": { + "connectionString": "mongodb://{{username}}:{{password}}@localhost:27017" + } + } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/AddCustomerToOrder.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/AddCustomerToOrder.cs new file mode 100644 index 0000000..479d91e --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/AddCustomerToOrder.cs @@ -0,0 +1,16 @@ +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.Orders.Application.Commands +{ + public class AddCustomerToOrder : ICommand + { + public Guid OrderId { get; } + public Guid CustomerId { get; } + + public AddCustomerToOrder(Guid orderId, Guid customerId) + { + OrderId = orderId; + CustomerId = customerId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/ApproveOrder.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/ApproveOrder.cs new file mode 100644 index 0000000..77fe123 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/ApproveOrder.cs @@ -0,0 +1,15 @@ +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.Orders.Application.Commands +{ + public class ApproveOrder: ICommand + { + public Guid OrderId { get; } + public ApproveOrder(Guid orderId) + { + OrderId = orderId; + } + } +} + + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/CancelOrder.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/CancelOrder.cs new file mode 100644 index 0000000..fbd8e79 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/CancelOrder.cs @@ -0,0 +1,17 @@ +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.Orders.Application.Commands +{ + public class CancelOrder: ICommand + { + public Guid OrderId { get; } + public string Reason { get; } + public CancelOrder(Guid orderId, string reason) + { + OrderId = orderId; + Reason = reason; + } + } +} + + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/CreateOrder.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/CreateOrder.cs new file mode 100644 index 0000000..62aaa0e --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/CreateOrder.cs @@ -0,0 +1,26 @@ +using Convey.CQRS.Commands; +using SwiftParcel.Services.Orders.Core.Entities; + +namespace SwiftParcel.Services.Orders.Application.Commands +{ + public class CreateOrder: ICommand + { + public Guid OrderId { get; } + public Guid CustomerId { get; } + public Guid ParcelId { get; } + public string Name { get; } + public string Email { get; } + public Address Address { get; } + public CreateOrder(Guid orderId, Guid customerId, Guid parcelId, string name, string email, Address address) + { + OrderId = orderId == Guid.Empty ? Guid.NewGuid() : orderId; + CustomerId = customerId; + ParcelId = parcelId; + Name = name; + Email = email; + Address = address; + } + } +} + + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/DeleteOrder.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/DeleteOrder.cs new file mode 100644 index 0000000..be7d451 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/DeleteOrder.cs @@ -0,0 +1,15 @@ +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.Orders.Application.Commands +{ + public class DeleteOrder: ICommand + { + public Guid OrderId { get; } + public DeleteOrder(Guid orderId) + { + OrderId = orderId; + } + } +} + + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/Handlers/AddCustomerToOrderHandler.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/Handlers/AddCustomerToOrderHandler.cs new file mode 100644 index 0000000..9c6ca7a --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/Handlers/AddCustomerToOrderHandler.cs @@ -0,0 +1,48 @@ +using Convey.CQRS.Commands; +using SwiftParcel.Services.Orders.Core.Repositories; +using SwiftParcel.Services.Orders.Application.Exceptions; +using SwiftParcel.Services.Orders.Core.Exceptions; +using SwiftParcel.Services.Orders.Application.Services; + +namespace SwiftParcel.Services.Orders.Application.Commands.Handlers +{ + public class AddCustomerToOrderHandler: ICommandHandler + { + private readonly IOrderRepository _orderRepository; + private readonly ICustomerRepository _customerRepository; + private readonly IAppContext _appContext; + private readonly IEventMapper _eventMapper; + private readonly IMessageBroker _messageBroker; + public AddCustomerToOrderHandler(IOrderRepository orderRepository, ICustomerRepository customerRepository, + IAppContext appContext, IEventMapper eventMapper, IMessageBroker messageBroker) + { + _orderRepository = orderRepository; + _customerRepository = customerRepository; + _appContext = appContext; + _eventMapper = eventMapper; + _messageBroker = messageBroker; + } + public async Task HandleAsync(AddCustomerToOrder command, CancellationToken cancellationToken) + { + var order = await _orderRepository.GetAsync(command.OrderId); + if (order is null) + { + throw new OrderNotFoundException(command.OrderId); + } + var customer = await _customerRepository.GetAsync(command.CustomerId); + if (customer is null) + { + throw new CustomerNotFoundException(command.CustomerId); + } + var identity = _appContext.Identity; + if (identity.IsAuthenticated && identity.Id != order.CustomerId && !identity.IsOfficeWorker) + { + throw new UnauthorizedOrderAccessException(command.OrderId, identity.Id); + } + order.AddCustomer(customer.Id); + await _orderRepository.UpdateAsync(order); + var events = _eventMapper.MapAll(order.Events); + await _messageBroker.PublishAsync(events.ToArray()); + } + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/Handlers/ApproveOrderHandler.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/Handlers/ApproveOrderHandler.cs new file mode 100644 index 0000000..873ee56 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/Handlers/ApproveOrderHandler.cs @@ -0,0 +1,55 @@ +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using SwiftParcel.Services.Orders.Application.Services; +using SwiftParcel.Services.Orders.Core.Entities; +using SwiftParcel.Services.Orders.Application.Exceptions; +using SwiftParcel.Services.Orders.Core.Repositories; + + +namespace SwiftParcel.Services.Orders.Application.Commands.Handlers +{ + public class ApproveOrderHandler: ICommandHandler + { + private readonly IOrderRepository _orderRepository; + private readonly IMessageBroker _messageBroker; + private readonly IEventMapper _eventMapper; + private readonly IAppContext _appContext; + private readonly ICommandDispatcher _commandDispatcher; + private readonly IDateTimeProvider _dateTimeProvider; + + public ApproveOrderHandler(IOrderRepository orderRepository, IMessageBroker messageBroker, + IEventMapper eventMapper, IAppContext appContext, ICommandDispatcher commandDispatcher, + IDateTimeProvider dateTimeProvider) + { + _orderRepository = orderRepository; + _messageBroker = messageBroker; + _eventMapper = eventMapper; + _appContext = appContext; + _commandDispatcher = commandDispatcher; + _dateTimeProvider = dateTimeProvider; + } + public async Task HandleAsync(ApproveOrder command, CancellationToken cancellationToken) + { + var order = await _orderRepository.GetAsync(command.OrderId); + if (order is null) + { + throw new OrderNotFoundException(command.OrderId); + } + + var identity = _appContext.Identity; + if (identity.IsAuthenticated && identity.Id != order.CustomerId && !identity.IsOfficeWorker) + { + throw new UnauthorizedOrderAccessException(command.OrderId, identity.Id); + } + var decisionDate = _dateTimeProvider.Now; + order.Approve(decisionDate); + await _orderRepository.UpdateAsync(order); + var events = _eventMapper.MapAll(order.Events); + await _messageBroker.PublishAsync(events.ToArray()); + + await _commandDispatcher.SendAsync(new SendApprovalEmail(order.Id, decisionDate, + order.BuyerName, order.BuyerEmail, order.BuyerAddress, order.Parcel)); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/Handlers/CancelOrderHandler.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/Handlers/CancelOrderHandler.cs new file mode 100644 index 0000000..aeba5de --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/Handlers/CancelOrderHandler.cs @@ -0,0 +1,55 @@ +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using SwiftParcel.Services.Orders.Application.Services; +using SwiftParcel.Services.Orders.Application.Exceptions; +using SwiftParcel.Services.Orders.Core.Repositories; +using SwiftParcel.Services.Orders.Core.Exceptions; + + +namespace SwiftParcel.Services.Orders.Application.Commands.Handlers +{ + public class CancelOrderHandler: ICommandHandler + { + private readonly IOrderRepository _orderRepository; + private readonly IMessageBroker _messageBroker; + private readonly IEventMapper _eventMapper; + private readonly IAppContext _appContext; + private readonly ICommandDispatcher _commandDispatcher; + private readonly IDateTimeProvider _dateTimeProvider; + + public CancelOrderHandler(IOrderRepository orderRepository, IMessageBroker messageBroker, + IEventMapper eventMapper, IAppContext appContext, ICommandDispatcher commandDispatcher, + IDateTimeProvider dateTimeProvider) + { + _orderRepository = orderRepository; + _messageBroker = messageBroker; + _eventMapper = eventMapper; + _appContext = appContext; + _commandDispatcher = commandDispatcher; + _dateTimeProvider = dateTimeProvider; + } + public async Task HandleAsync(CancelOrder command, CancellationToken cancellationToken) + { + var order = await _orderRepository.GetAsync(command.OrderId); + if (order is null) + { + throw new OrderNotFoundException(command.OrderId); + } + + var identity = _appContext.Identity; + if (identity.IsAuthenticated && identity.Id != order.CustomerId && !identity.IsOfficeWorker) + { + throw new UnauthorizedOrderAccessException(command.OrderId, identity.Id); + } + + order.Cancel(_dateTimeProvider.Now, command.Reason); + await _orderRepository.UpdateAsync(order); + var events = _eventMapper.MapAll(order.Events); + await _messageBroker.PublishAsync(events.ToArray()); + + await _commandDispatcher.SendAsync(new SendCancellationEmail(order.Id, order.BuyerName, + order.BuyerEmail, command.Reason)); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/Handlers/CreateOrderHandler.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/Handlers/CreateOrderHandler.cs new file mode 100644 index 0000000..e59c80f --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/Handlers/CreateOrderHandler.cs @@ -0,0 +1,62 @@ +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using SwiftParcel.Services.Orders.Application.Services; +using SwiftParcel.Services.Orders.Core.Entities; +using SwiftParcel.Services.Orders.Core.Repositories; +using SwiftParcel.Services.Orders.Core.Exceptions; +using SwiftParcel.Services.Orders.Application.Services.Clients; +using SwiftParcel.Services.Orders.Application.Exceptions; + + +namespace SwiftParcel.Services.Orders.Application.Commands.Handlers +{ + public class CreateOrderHandler : ICommandHandler + { + private readonly IOrderRepository _orderRepository; + private readonly ICustomerRepository _customerRepository; + private readonly IMessageBroker _messageBroker; + private readonly IEventMapper _eventMapper; + private readonly IDateTimeProvider _dateTimeProvider; + private readonly IParcelsServiceClient _parcelsServiceClient; + + public CreateOrderHandler(IOrderRepository orderRepository, ICustomerRepository customerRepository, + IMessageBroker messageBroker, IEventMapper eventMapper, IDateTimeProvider dateTimeProvider, + IParcelsServiceClient parcelsServiceClient) + { + _orderRepository = orderRepository; + _customerRepository = customerRepository; + _messageBroker = messageBroker; + _eventMapper = eventMapper; + _dateTimeProvider = dateTimeProvider; + _parcelsServiceClient = parcelsServiceClient; + } + public async Task HandleAsync(CreateOrder command, CancellationToken cancellationToken) + { + if (command.CustomerId != Guid.Empty && !await _customerRepository.ExistsAsync(command.CustomerId)) + { + throw new CustomerNotFoundException(command.CustomerId); + } + + var parcelDto = await _parcelsServiceClient.GetAsync(command.ParcelId); + if (parcelDto is null) + { + throw new ParcelNotFoundException(command.ParcelId); + } + var parcel = new Parcel(command.ParcelId, parcelDto.Description, + parcelDto.Width, parcelDto.Height, parcelDto.Depth, parcelDto.Weight, parcelDto.Source, parcelDto.Destination, + parcelDto.Priority, parcelDto.AtWeekend, parcelDto.PickupDate, parcelDto.DeliveryDate, parcelDto.IsCompany, + parcelDto.VipPackage, parcelDto.CreatedAt, parcelDto.ValidTo, parcelDto.CalculatedPrice); + var requestDate = _dateTimeProvider.Now; + parcel.ValidateRequest(requestDate); + + var order = Order.Create(command.OrderId, command.CustomerId, OrderStatus.WaitingForDecision, requestDate, + command.Name, command.Email, command.Address); + order.AddParcel(parcel); + + await _orderRepository.AddAsync(order); + var events = _eventMapper.MapAll(order.Events); + await _messageBroker.PublishAsync(events.ToArray()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/Handlers/DeleteOrderHandler.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/Handlers/DeleteOrderHandler.cs new file mode 100644 index 0000000..4b72ad9 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/Handlers/DeleteOrderHandler.cs @@ -0,0 +1,50 @@ +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using SwiftParcel.Services.Orders.Application.Services; +using SwiftParcel.Services.Orders.Core.Entities; +using SwiftParcel.Services.Orders.Application.Exceptions; +using SwiftParcel.Services.Orders.Core.Exceptions; +using SwiftParcel.Services.Orders.Application.Events; +using SwiftParcel.Services.Orders.Core.Repositories; + + +namespace SwiftParcel.Services.Orders.Application.Commands.Handlers +{ + public class DeleteOrderHandler: ICommandHandler + { + private readonly IOrderRepository _orderRepository; + private readonly IAppContext _appContext; + private readonly IMessageBroker _messageBroker; + public DeleteOrderHandler(IOrderRepository orderRepository, IAppContext appContext, + IMessageBroker messageBroker) + { + _orderRepository = orderRepository; + _appContext = appContext; + _messageBroker = messageBroker; + } + public async Task HandleAsync(DeleteOrder command, CancellationToken cancellationToken) + { + var order = await _orderRepository.GetAsync(command.OrderId); + if (order is null) + { + throw new OrderNotFoundException(command.OrderId); + } + + var identity = _appContext.Identity; + if (identity.IsAuthenticated && identity.Id != order.CustomerId && !identity.IsOfficeWorker) + { + throw new UnauthorizedOrderAccessException(command.OrderId, identity.Id); + } + + if (!order.CanBeDeleted) + { + throw new CannotDeleteOrderException(command.OrderId); + } + + await _orderRepository.DeleteAsync(command.OrderId); + await _messageBroker.PublishAsync(new OrderDeleted(command.OrderId)); + } + } + +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/SendApprovalEmail.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/SendApprovalEmail.cs new file mode 100644 index 0000000..f2f2d8b --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/SendApprovalEmail.cs @@ -0,0 +1,26 @@ +using System.Net.Sockets; +using Convey.CQRS.Commands; +using SwiftParcel.Services.Orders.Core.Entities; + +namespace SwiftParcel.Services.Orders.Application.Commands +{ + public class SendApprovalEmail: ICommand + { + public Guid OrderId { get; } + public DateTime IssueDate { get; } + public string CustomerName { get; } + public string CustomerEmail { get; } + public Address CustomerAddress { get; } + public Parcel Parcel { get; } + public SendApprovalEmail(Guid orderId, DateTime issueDate, string customerName, + string customerEmail, Address customerAddress, Parcel parcel) + { + OrderId = orderId; + IssueDate = issueDate; + CustomerName = customerName; + CustomerEmail = customerEmail; + CustomerAddress = customerAddress; + Parcel = parcel; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/SendCancellationEmail.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/SendCancellationEmail.cs new file mode 100644 index 0000000..5b86fac --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Commands/SendCancellationEmail.cs @@ -0,0 +1,20 @@ +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.Orders.Application.Commands +{ + public class SendCancellationEmail: ICommand + { + public Guid OrderId { get; } + public string CustomerName { get; } + public string CustomerEmail { get; } + public string Reason { get; } + public SendCancellationEmail(Guid orderId, string customerName, + string customerEmail, string reason) + { + OrderId = orderId; + CustomerName = customerName; + CustomerEmail = customerEmail; + Reason = reason; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/ContractAttribute.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/ContractAttribute.cs new file mode 100644 index 0000000..424cfd5 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/ContractAttribute.cs @@ -0,0 +1,7 @@ +namespace SwiftParcel.Services.Orders.Application +{ + public class ContractAttribute : Attribute + { + + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/DTO/AdressDto.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/DTO/AdressDto.cs new file mode 100644 index 0000000..03b9b88 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/DTO/AdressDto.cs @@ -0,0 +1,23 @@ +using SwiftParcel.Services.Orders.Core.Entities; + +namespace SwiftParcel.Services.Orders.Application.DTO +{ + public class AddressDto + { + public string Street { get; set; } + public string BuildingNumber { get; set; } + public string ApartmentNumber { get; set; } + public string City { get; set; } + public string ZipCode { get; set; } + public string Country { get; set; } + public AddressDto(Address address) + { + Street = address.Street; + BuildingNumber = address.BuildingNumber; + ApartmentNumber = address.ApartmentNumber; + City = address.City; + ZipCode = address.ZipCode; + Country = address.Country; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/DTO/OrderDto.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/DTO/OrderDto.cs new file mode 100644 index 0000000..e6728c7 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/DTO/OrderDto.cs @@ -0,0 +1,44 @@ +using SwiftParcel.Services.Orders.Core.Entities; + +namespace SwiftParcel.Services.Orders.Application.DTO +{ + public class OrderDto + { + public Guid Id { get; set; } + public Guid? CustomerId { get; set; } + public ParcelDto Parcel { get; set; } + public string Status { get; set; } + public DateTime OrderRequestDate { get; set; } + public string BuyerName { get; set; } + public string BuyerEmail { get; set; } + public AddressDto BuyerAddress { get; set; } + public DateTime? DecisionDate { get; set; } + public DateTime? PickedUpAt { get; set; } + public DateTime? DeliveredAt { get; set; } + public DateTime? CannotDeliverAt { get; set; } + public string CancellationReason { get; set; } + public string CannotDeliverReason { get; set; } + + public OrderDto() + { + } + + public OrderDto(Order order) + { + Id = order.Id; + CustomerId = order.CustomerId; + Parcel = order.Parcel == null ? null : new ParcelDto(order.Parcel); + Status = order.Status.ToString().ToLowerInvariant(); + OrderRequestDate = order.OrderRequestDate; + BuyerName = order.BuyerName; + BuyerEmail = order.BuyerEmail; + BuyerAddress = new AddressDto(order.BuyerAddress); + DecisionDate = order.DecisionDate; + PickedUpAt = order.PickedUpAt; + DeliveredAt = order.DeliveredAt; + CannotDeliverAt = order.CannotDeliverAt; + CancellationReason = order.CancellationReason; + CannotDeliverReason = order.CannotDeliverReason; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/DTO/OrderStatusDto.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/DTO/OrderStatusDto.cs new file mode 100644 index 0000000..7655dfa --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/DTO/OrderStatusDto.cs @@ -0,0 +1,9 @@ +namespace SwiftParcel.Services.Orders.Application.DTO +{ + public class OrderStatusDto + { + public Guid OrderId { get; set; } + public string Status { get; set; } + public DateTime TimeStamp { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/DTO/ParcelDto.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/DTO/ParcelDto.cs new file mode 100644 index 0000000..0fb29b4 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/DTO/ParcelDto.cs @@ -0,0 +1,52 @@ +using SwiftParcel.Services.Orders.Core.Entities; +namespace SwiftParcel.Services.Orders.Application.DTO +{ + public class ParcelDto + { + public Guid Id { get; set; } + public Guid? CustomerId { get; set; } + public string Description { get; set; } + public double Width { get; set; } + public double Height { get; set; } + public double Depth { get; set; } + public double Weight { get; set; } + public Address Source { get; set; } + public Address Destination { get; set; } + public Priority Priority { get; set; } + public bool AtWeekend { get; set; } + public DateTime PickupDate { get; set; } + public DateTime DeliveryDate { get; set; } + public bool IsCompany { get; set; } + public bool VipPackage { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime ValidTo { get; set; } + public decimal CalculatedPrice { get; set; } + + public ParcelDto() + { + } + + public ParcelDto(Parcel parcel) + { + Id = parcel.Id; + CustomerId = Guid.Empty; + Description = parcel.Description; + Width = parcel.Width; + Height = parcel.Height; + Depth = parcel.Depth; + Weight = parcel.Weight; + Source = parcel.Source; + Destination = parcel.Destination; + Priority = parcel.Priority; + AtWeekend = parcel.AtWeekend; + PickupDate = parcel.PickupDate; + DeliveryDate = parcel.DeliveryDate; + IsCompany = parcel.IsCompany; + VipPackage = parcel.VipPackage; + CreatedAt = parcel.InquireDate; + ValidTo = parcel.ValidTo; + CalculatedPrice = parcel.CalculatedPrice; + } + } +} + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/CustomerCreated.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/CustomerCreated.cs new file mode 100644 index 0000000..027e6d3 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/CustomerCreated.cs @@ -0,0 +1,20 @@ +using Convey.CQRS.Events; +using Convey.MessageBrokers; + +namespace SwiftParcel.Services.Orders.Application.Events.External +{ + [Message("customers")] + public class CustomerCreated : IEvent + { + public Guid CustomerId { get; } + public string Email { get; } + public string FullName { get; } + + public CustomerCreated(Guid customerId, string email, string fullName) + { + CustomerId = customerId; + Email = email; + FullName = fullName; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/DeliveryCompleted.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/DeliveryCompleted.cs new file mode 100644 index 0000000..7412417 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/DeliveryCompleted.cs @@ -0,0 +1,20 @@ +using Convey.CQRS.Events; +using Convey.MessageBrokers; + +namespace SwiftParcel.Services.Orders.Application.Events.External +{ + [Message("deliveries")] + public class DeliveryCompleted : IEvent + { + public Guid DeliveryId { get; } + public Guid OrderId { get; } + public DateTime DateTime { get; } + + public DeliveryCompleted(Guid deliveryId, Guid orderId, DateTime dateTime) + { + DeliveryId = deliveryId; + OrderId = orderId; + DateTime = dateTime; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/DeliveryFailed.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/DeliveryFailed.cs new file mode 100644 index 0000000..a94dc96 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/DeliveryFailed.cs @@ -0,0 +1,22 @@ +using Convey.CQRS.Events; +using Convey.MessageBrokers; + +namespace SwiftParcel.Services.Orders.Application.Events.External +{ + [Message("deliveries")] + public class DeliveryFailed : IEvent + { + public Guid DeliveryId { get; } + public Guid OrderId { get; } + public DateTime DateTime { get; } + public string Reason { get; } + + public DeliveryFailed(Guid deliveryId, Guid orderId, DateTime dateTime, string reason) + { + DeliveryId = deliveryId; + OrderId = orderId; + DateTime = dateTime; + Reason = reason; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/DeliveryPickedUp.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/DeliveryPickedUp.cs new file mode 100644 index 0000000..15741e7 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/DeliveryPickedUp.cs @@ -0,0 +1,20 @@ +using Convey.CQRS.Events; +using Convey.MessageBrokers; + +namespace SwiftParcel.Services.Orders.Application.Events.External +{ + [Message("deliveries")] + public class DeliveryPickedUp : IEvent + { + public Guid DeliveryId { get; } + public Guid OrderId { get; } + public DateTime DateTime { get; } + + public DeliveryPickedUp(Guid deliveryId, Guid orderId, DateTime dateTime) + { + DeliveryId = deliveryId; + OrderId = orderId; + DateTime = dateTime; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/Handlers/CustomerCreatedHandler.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/Handlers/CustomerCreatedHandler.cs new file mode 100644 index 0000000..d3c4451 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/Handlers/CustomerCreatedHandler.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using Convey.CQRS.Events; +using SwiftParcel.Services.Orders.Application.Exceptions; +using SwiftParcel.Services.Orders.Core.Entities; +using SwiftParcel.Services.Orders.Core.Repositories; + +namespace SwiftParcel.Services.Orders.Application.Events.External.Handlers +{ + public class CustomerCreatedHandler : IEventHandler + { + private readonly ICustomerRepository _customerRepository; + + public CustomerCreatedHandler(ICustomerRepository customerRepository) + { + _customerRepository = customerRepository; + } + + public async Task HandleAsync(CustomerCreated @event, CancellationToken cancellationToken) + { + if (await _customerRepository.ExistsAsync(@event.CustomerId)) + { + throw new CustomerAlreadyAddedException(@event.CustomerId); + } + + await _customerRepository.AddAsync(new Customer(@event.CustomerId, @event.Email, @event.FullName)); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/Handlers/DeliveryCompletedHandler.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/Handlers/DeliveryCompletedHandler.cs new file mode 100644 index 0000000..44f6762 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/Handlers/DeliveryCompletedHandler.cs @@ -0,0 +1,37 @@ +using System.Threading.Tasks; +using Convey.CQRS.Events; +using SwiftParcel.Services.Orders.Application.Exceptions; +using SwiftParcel.Services.Orders.Core.Repositories; +using SwiftParcel.Services.Orders.Application.Services; + +namespace SwiftParcel.Services.Orders.Application.Events.External.Handlers +{ + public class DeliveryCompletedHandler : IEventHandler + { + private readonly IOrderRepository _orderRepository; + private readonly IMessageBroker _messageBroker; + private readonly IEventMapper _eventMapper; + + public DeliveryCompletedHandler(IOrderRepository orderRepository, IMessageBroker messageBroker, + IEventMapper eventMapper) + { + _orderRepository = orderRepository; + _messageBroker = messageBroker; + _eventMapper = eventMapper; + } + + public async Task HandleAsync(DeliveryCompleted @event, CancellationToken cancellationToken) + { + var order = await _orderRepository.GetAsync(@event.OrderId); + if (order is null) + { + throw new OrderNotFoundException(@event.OrderId); + } + + order.Deliver(@event.DateTime); + await _orderRepository.UpdateAsync(order); + var events = _eventMapper.MapAll(order.Events); + await _messageBroker.PublishAsync(events.ToArray()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/Handlers/DeliveryFailedHandler.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/Handlers/DeliveryFailedHandler.cs new file mode 100644 index 0000000..c4e2d7a --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/Handlers/DeliveryFailedHandler.cs @@ -0,0 +1,37 @@ +using System.Threading.Tasks; +using Convey.CQRS.Events; +using SwiftParcel.Services.Orders.Application.Exceptions; +using SwiftParcel.Services.Orders.Core.Repositories; +using SwiftParcel.Services.Orders.Application.Services; + +namespace SwiftParcel.Services.Orders.Application.Events.External.Handlers +{ + public class DeliveryFailedHandler : IEventHandler + { + private readonly IOrderRepository _orderRepository; + private readonly IMessageBroker _messageBroker; + private readonly IEventMapper _eventMapper; + + public DeliveryFailedHandler(IOrderRepository orderRepository, IMessageBroker messageBroker, + IEventMapper eventMapper) + { + _orderRepository = orderRepository; + _messageBroker = messageBroker; + _eventMapper = eventMapper; + } + + public async Task HandleAsync(DeliveryFailed @event, CancellationToken cancellationToken) + { + var order = await _orderRepository.GetAsync(@event.OrderId); + if (order is null) + { + throw new OrderNotFoundException(@event.OrderId); + } + + order.SetCannotDeliver(@event.Reason, @event.DateTime); + await _orderRepository.UpdateAsync(order); + var events = _eventMapper.MapAll(order.Events); + await _messageBroker.PublishAsync(events.ToArray()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/Handlers/DeliveryPickedUpHandler.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/Handlers/DeliveryPickedUpHandler.cs new file mode 100644 index 0000000..6fbdc4c --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/External/Handlers/DeliveryPickedUpHandler.cs @@ -0,0 +1,37 @@ +using System.Threading.Tasks; +using Convey.CQRS.Events; +using SwiftParcel.Services.Orders.Application.Exceptions; +using SwiftParcel.Services.Orders.Core.Repositories; +using SwiftParcel.Services.Orders.Application.Services; + +namespace SwiftParcel.Services.Orders.Application.Events.External.Handlers +{ + public class DeliveryPickedUpHandler : IEventHandler + { + private readonly IOrderRepository _orderRepository; + private readonly IMessageBroker _messageBroker; + private readonly IEventMapper _eventMapper; + + public DeliveryPickedUpHandler(IOrderRepository orderRepository, IMessageBroker messageBroker, + IEventMapper eventMapper) + { + _orderRepository = orderRepository; + _messageBroker = messageBroker; + _eventMapper = eventMapper; + } + + public async Task HandleAsync(DeliveryPickedUp @event, CancellationToken cancellationToken) + { + var order = await _orderRepository.GetAsync(@event.OrderId); + if (order is null) + { + throw new OrderNotFoundException(@event.OrderId); + } + + order.SetPickedUp(@event.DateTime); + await _orderRepository.UpdateAsync(order); + var events = _eventMapper.MapAll(order.Events); + await _messageBroker.PublishAsync(events.ToArray()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderApproved.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderApproved.cs new file mode 100644 index 0000000..4707b5c --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderApproved.cs @@ -0,0 +1,36 @@ +using System; +using Convey.CQRS.Events; +using SwiftParcel.Services.Orders.Core.Entities; + +namespace SwiftParcel.Services.Orders.Application.Events +{ + public class OrderApproved : IEvent + { + public Guid OrderId { get; set; } + public double Width { get; protected set; } + public double Height { get; protected set; } + public double Depth { get; protected set; } + public double Weight { get; set; } + public Address Source { get; set; } + public Address Destination { get; set; } + public Priority Priority { get; set; } + public bool AtWeekend { get; set; } + public DateTime PickupDate { get; set; } + public DateTime DeliveryDate { get; set; } + public OrderApproved(Guid orderId, double width, double height, double depth, double weight, Address source, + Address destination, Priority priority, bool atWeekend, DateTime pickupDate, DateTime deliveryDate) + { + OrderId = orderId; + Width = width; + Height = height; + Depth = depth; + Weight = weight; + Source = source; + Destination = destination; + Priority = priority; + AtWeekend = atWeekend; + PickupDate = pickupDate; + DeliveryDate = deliveryDate; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderCancelled.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderCancelled.cs new file mode 100644 index 0000000..188abf8 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderCancelled.cs @@ -0,0 +1,16 @@ +using System; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Orders.Application.Events +{ + public class OrderCancelled : IEvent + { + public Guid OrderId { get; } + public string Reason { get; } + public OrderCancelled(Guid orderId, string reason) + { + OrderId = orderId; + Reason = reason; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderCouldNotBeDelivered.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderCouldNotBeDelivered.cs new file mode 100644 index 0000000..ba26211 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderCouldNotBeDelivered.cs @@ -0,0 +1,16 @@ +using System; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Orders.Application.Events +{ + public class OrderCouldNotBeDelivered : IEvent + { + public Guid OrderId { get; } + public string Reason { get; } + public OrderCouldNotBeDelivered(Guid orderId, string reason) + { + OrderId = orderId; + Reason = reason; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderCreated.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderCreated.cs new file mode 100644 index 0000000..dc7199f --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderCreated.cs @@ -0,0 +1,14 @@ +using System; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Orders.Application.Events +{ + public class OrderCreated : IEvent + { + public Guid OrderId { get; } + public OrderCreated(Guid orderId) + { + OrderId = orderId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderDeleted.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderDeleted.cs new file mode 100644 index 0000000..3a9064f --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderDeleted.cs @@ -0,0 +1,14 @@ +using System; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Orders.Application.Events +{ + public class OrderDeleted : IEvent + { + public Guid OrderId { get; } + public OrderDeleted(Guid orderId) + { + OrderId = orderId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderDelivered.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderDelivered.cs new file mode 100644 index 0000000..c53e33a --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderDelivered.cs @@ -0,0 +1,14 @@ +using System; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Orders.Application.Events +{ + public class OrderDelivered : IEvent + { + public Guid OrderId { get; } + public OrderDelivered(Guid orderId) + { + OrderId = orderId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderReceived.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderReceived.cs new file mode 100644 index 0000000..e1b740f --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/OrderReceived.cs @@ -0,0 +1,14 @@ +using System; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Orders.Application.Events +{ + public class OrderReceived : IEvent + { + public Guid OrderId { get; } + public OrderReceived(Guid orderId) + { + OrderId = orderId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/ParcelAddedToOrder.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/ParcelAddedToOrder.cs new file mode 100644 index 0000000..8490216 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/ParcelAddedToOrder.cs @@ -0,0 +1,16 @@ +using System; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Orders.Application.Events +{ + public class ParcelAddedToOrder : IEvent + { + public Guid OrderId { get; } + public Guid ParcelId { get; } + public ParcelAddedToOrder(Guid orderId, Guid parcelId) + { + OrderId = orderId; + ParcelId = parcelId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/Rejected/ApproveOrderRejected.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/Rejected/ApproveOrderRejected.cs new file mode 100644 index 0000000..4ef3983 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/Rejected/ApproveOrderRejected.cs @@ -0,0 +1,18 @@ +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Orders.Application.Events.Rejected +{ + public class ApproveOrderRejected : IRejectedEvent + { + public Guid OrderId { get; } + public string Reason { get; } + public string Code { get; } + + public ApproveOrderRejected(Guid orderId, string reason, string code) + { + OrderId = orderId; + Reason = reason; + Code = code; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/Rejected/CancelOrderRejected.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/Rejected/CancelOrderRejected.cs new file mode 100644 index 0000000..c574a1f --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/Rejected/CancelOrderRejected.cs @@ -0,0 +1,18 @@ +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Orders.Application.Events.Rejected +{ + public class CancelOrderRejected : IRejectedEvent + { + public Guid OrderId { get; } + public string Reason { get; } + public string Code { get; } + + public CancelOrderRejected(Guid orderId, string reason, string code) + { + OrderId = orderId; + Reason = reason; + Code = code; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/Rejected/CreateOrderRejected.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/Rejected/CreateOrderRejected.cs new file mode 100644 index 0000000..7d470f6 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/Rejected/CreateOrderRejected.cs @@ -0,0 +1,22 @@ +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Orders.Application.Events.Rejected +{ + public class CreateOrderRejected : IRejectedEvent + { + public Guid OrderId { get; } + public Guid? CustomerId { get; } + public Guid? ParcelId { get; } + public string Reason { get; } + public string Code { get; } + + public CreateOrderRejected(Guid orderId, Guid customerId, Guid parcelId, string reason, string code) + { + OrderId = orderId; + CustomerId = customerId; + ParcelId = parcelId; + Reason = reason; + Code = code; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/Rejected/DeleteOrderRejected.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/Rejected/DeleteOrderRejected.cs new file mode 100644 index 0000000..703eb67 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/Rejected/DeleteOrderRejected.cs @@ -0,0 +1,18 @@ +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Orders.Application.Events.Rejected +{ + public class DeleteOrderRejected : IRejectedEvent + { + public Guid OrderId { get; } + public string Reason { get; } + public string Code { get; } + + public DeleteOrderRejected(Guid orderId, string reason, string code) + { + OrderId = orderId; + Reason = reason; + Code = code; + } + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/Rejected/OrderForDeliveryNotFound.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/Rejected/OrderForDeliveryNotFound.cs new file mode 100644 index 0000000..c5a010d --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Events/Rejected/OrderForDeliveryNotFound.cs @@ -0,0 +1,18 @@ +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Orders.Application.Events.Rejected +{ + public class OrderForDeliveryNotFound : IRejectedEvent + { + public Guid OrderId { get; } + public string Reason { get; } + public string Code { get; } + + public OrderForDeliveryNotFound(Guid orderId, string reason, string code) + { + OrderId = orderId; + Reason = reason; + Code = code; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/AppException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/AppException.cs new file mode 100644 index 0000000..bf0cd17 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/AppException.cs @@ -0,0 +1,15 @@ +using System; + +namespace SwiftParcel.Services.Orders.Application.Exceptions +{ + public abstract class AppException : Exception + { + public virtual string Code { get; } + + protected AppException(string message) : base(message) + { + } + } +} + + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/CustomerAlreadyAddedException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/CustomerAlreadyAddedException.cs new file mode 100644 index 0000000..d547f4d --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/CustomerAlreadyAddedException.cs @@ -0,0 +1,16 @@ +using System; + +namespace SwiftParcel.Services.Orders.Application.Exceptions +{ + public class CustomerAlreadyAddedException : AppException + { + public override string Code { get; } = "customer_already_added"; + public Guid CustomerId { get; } + + public CustomerAlreadyAddedException(Guid customerId) + : base($"Customer with id: {customerId} was already added.") + { + CustomerId = customerId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/OrderNotFoundException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/OrderNotFoundException.cs new file mode 100644 index 0000000..d45a85a --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/OrderNotFoundException.cs @@ -0,0 +1,15 @@ +using System; + +namespace SwiftParcel.Services.Orders.Application.Exceptions +{ + public class OrderNotFoundException : AppException + { + public override string Code { get; } = "order_not_found"; + public Guid Id { get; } + + public OrderNotFoundException(Guid id) : base($"Order with id: {id} was not found.") + { + Id = id; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/ParcelNotFoundException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/ParcelNotFoundException.cs new file mode 100644 index 0000000..018977b --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/ParcelNotFoundException.cs @@ -0,0 +1,15 @@ +using System; + +namespace SwiftParcel.Services.Orders.Application.Exceptions +{ + public class ParcelNotFoundException : AppException + { + public override string Code { get; } = "parcel_not_found"; + public Guid Id { get; } + + public ParcelNotFoundException(Guid id) : base($"Parcel with id: {id} was not found.") + { + Id = id; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/SmtpException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/SmtpException.cs new file mode 100644 index 0000000..fbec792 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/SmtpException.cs @@ -0,0 +1,10 @@ +namespace SwiftParcel.Services.Orders.Application.Exceptions +{ + public class SmtpException : AppException + { + public override string Code { get; } = "smtp_exception"; + public SmtpException(string message) : base(message) + { + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/UnauthorizedOrderAccessException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/UnauthorizedOrderAccessException.cs new file mode 100644 index 0000000..02fb855 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Exceptions/UnauthorizedOrderAccessException.cs @@ -0,0 +1,18 @@ +using System; + +namespace SwiftParcel.Services.Orders.Application.Exceptions +{ + public class UnauthorizedOrderAccessException : AppException + { + public override string Code { get; } = "unauthorized_order_access"; + public Guid OrderId { get; } + public Guid CustomerId { get; } + + public UnauthorizedOrderAccessException(Guid orderId, Guid customerId) + : base($"Unauthorized access to order with id: '{orderId}' by customer with id: '{customerId}'.") + { + OrderId = orderId; + CustomerId = customerId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Extensions.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Extensions.cs new file mode 100644 index 0000000..f002df3 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Extensions.cs @@ -0,0 +1,18 @@ +using Convey; +using Convey.CQRS.Commands; +using Convey.CQRS.Events; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +namespace SwiftParcel.Services.Orders.Application +{ + public static class Extensions + { + public static IConveyBuilder AddApplication(this IConveyBuilder builder) + => builder + .AddCommandHandlers() + .AddEventHandlers() + .AddInMemoryCommandDispatcher() + .AddInMemoryEventDispatcher(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/IAppContext.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/IAppContext.cs new file mode 100644 index 0000000..812cec1 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/IAppContext.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Orders.Application +{ + public interface IAppContext + { + string RequestId { get; } + IIdentityContext Identity { get; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/IIdentityContext.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/IIdentityContext.cs new file mode 100644 index 0000000..39ca889 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/IIdentityContext.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Orders.Application +{ + public interface IIdentityContext + { + Guid Id { get; } + string Role { get; } + bool IsAuthenticated { get; } + bool IsOfficeWorker { get; } + bool IsCourier { get; } + IDictionary Claims { get; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Queries/GetOrder.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Queries/GetOrder.cs new file mode 100644 index 0000000..0545c0f --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Queries/GetOrder.cs @@ -0,0 +1,11 @@ +using System; +using Convey.CQRS.Queries; +using SwiftParcel.Services.Orders.Application.DTO; + +namespace SwiftParcel.Services.Orders.Application.Queries +{ + public class GetOrder : IQuery + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Queries/GetOrderStatus.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Queries/GetOrderStatus.cs new file mode 100644 index 0000000..576eb0c --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Queries/GetOrderStatus.cs @@ -0,0 +1,10 @@ +using Convey.CQRS.Queries; +using SwiftParcel.Services.Orders.Application.DTO; + +namespace SwiftParcel.Services.Orders.Application.Queries +{ + public class GetOrderStatus : IQuery + { + public Guid OrderId { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Queries/GetOrders.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Queries/GetOrders.cs new file mode 100644 index 0000000..ea7cdf7 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Queries/GetOrders.cs @@ -0,0 +1,10 @@ +using Convey.CQRS.Queries; +using SwiftParcel.Services.Orders.Application.DTO; + +namespace SwiftParcel.Services.Orders.Application.Queries +{ + public class GetOrders : IQuery> + { + public Guid? CustomerId { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Queries/GetOrdersOfficeWorker.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Queries/GetOrdersOfficeWorker.cs new file mode 100644 index 0000000..ffcc8be --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Queries/GetOrdersOfficeWorker.cs @@ -0,0 +1,9 @@ +using Convey.CQRS.Queries; +using SwiftParcel.Services.Orders.Application.DTO; + +namespace SwiftParcel.Services.Orders.Application.Queries +{ + public class GetOrdersOfficeWorker : IQuery> + { + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Services/Clients/IParcelsServiceClient.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Services/Clients/IParcelsServiceClient.cs new file mode 100644 index 0000000..782ae77 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Services/Clients/IParcelsServiceClient.cs @@ -0,0 +1,10 @@ +using SwiftParcel.Services.Orders.Application.DTO; + +namespace SwiftParcel.Services.Orders.Application.Services.Clients +{ + public interface IParcelsServiceClient + { + Task GetAsync(Guid id); + } +} + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Services/IDateTimeProvider.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Services/IDateTimeProvider.cs new file mode 100644 index 0000000..afc8b62 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Services/IDateTimeProvider.cs @@ -0,0 +1,7 @@ +namespace SwiftParcel.Services.Orders.Application.Services +{ + public interface IDateTimeProvider + { + DateTime Now { get; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Services/IEventMapper.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Services/IEventMapper.cs new file mode 100644 index 0000000..7fcf9a0 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Services/IEventMapper.cs @@ -0,0 +1,12 @@ +using Convey.CQRS.Events; +using SwiftParcel.Services.Orders.Core; + +namespace SwiftParcel.Services.Orders.Application.Services +{ + public interface IEventMapper + { + IEvent Map(IDomainEvent @event); + IEnumerable MapAll(IEnumerable events); + } +} + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Services/IMessageBroker.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Services/IMessageBroker.cs new file mode 100644 index 0000000..d6039ae --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/Services/IMessageBroker.cs @@ -0,0 +1,11 @@ +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Orders.Application.Services +{ + public interface IMessageBroker + { + Task PublishAsync(params IEvent[] events); + Task PublishAsync(IEnumerable events); + } +} + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application.csproj b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application.csproj new file mode 100644 index 0000000..cda6b49 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application/SwiftParcel.Services.Orders.Application.csproj @@ -0,0 +1,27 @@ + + + + + + + + net6.0 + enable + disable + + + + + + + + + + + + + + + + + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/Address.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/Address.cs new file mode 100644 index 0000000..280bbfa --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/Address.cs @@ -0,0 +1,12 @@ +namespace SwiftParcel.Services.Orders.Core.Entities +{ + public class Address + { + public string Street { get; set; } + public string BuildingNumber { get; set; } + public string ApartmentNumber { get; set; } + public string City { get; set; } + public string ZipCode { get; set; } + public string Country { get; set;} + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/AggregateId.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/AggregateId.cs new file mode 100644 index 0000000..31b2660 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/AggregateId.cs @@ -0,0 +1,51 @@ +using SwiftParcel.Services.Orders.Core.Exceptions; + +namespace SwiftParcel.Services.Orders.Core.Entities +{ + public class AggregateId : IEquatable + { + public Guid Value { get; } + + public AggregateId() + { + Value = Guid.NewGuid(); + } + + public AggregateId(Guid value) + { + if (value == Guid.Empty) + { + throw new InvalidAggregateIdException(value); + } + + Value = value; + } + + public bool Equals(AggregateId other) + { + if (ReferenceEquals(null, other)) return false; + return ReferenceEquals(this, other) || Value.Equals(other.Value); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((AggregateId) obj); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public static implicit operator Guid(AggregateId id) + => id.Value; + + public static implicit operator AggregateId(Guid id) + => new AggregateId(id); + + public override string ToString() => Value.ToString(); + } +} + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/AggregateRoot.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/AggregateRoot.cs new file mode 100644 index 0000000..0d4ee77 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/AggregateRoot.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace SwiftParcel.Services.Orders.Core.Entities +{ + public abstract class AggregateRoot + { + private readonly List _events = new List(); + public IEnumerable Events => _events; + public AggregateId Id { get; protected set; } + public int Version { get; protected set; } + + protected void AddEvent(IDomainEvent @event) + { + _events.Add(@event); + } + + public void ClearEvents() => _events.Clear(); + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/Customer.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/Customer.cs new file mode 100644 index 0000000..602a55b --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/Customer.cs @@ -0,0 +1,16 @@ +namespace SwiftParcel.Services.Orders.Core.Entities +{ + public class Customer + { + public Guid Id { get; private set; } + public string Email { get; private set; } + public string FullName { get; private set; } + + public Customer(Guid id, string email, string fullName) + { + Id = id; + Email = email; + FullName = fullName; + } + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/Order.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/Order.cs new file mode 100644 index 0000000..3e55287 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/Order.cs @@ -0,0 +1,208 @@ +using System.Text.RegularExpressions; +using SwiftParcel.Services.Orders.Core.Exceptions; +using SwiftParcel.Services.Orders.Core.Events; + +namespace SwiftParcel.Services.Orders.Core.Entities +{ + public class Order : AggregateRoot + { + public Guid? CustomerId { get; private set; } + public Parcel Parcel { get; private set; } + public OrderStatus Status { get; private set; } + public DateTime OrderRequestDate { get; private set; } + public string BuyerName { get; private set; } + public string BuyerEmail { get; private set; } + public Address BuyerAddress { get; private set; } + public DateTime? DecisionDate { get; private set; } + public DateTime? PickedUpAt { get; private set; } + public DateTime? DeliveredAt { get; private set; } + public DateTime? CannotDeliverAt { get; private set; } + public string CancellationReason { get; private set; } + public string CannotDeliverReason { get; private set; } + public bool CanRequestDelivery => Status == OrderStatus.Approved; + public bool CanBeDeleted => Status == OrderStatus.Cancelled; + public bool HasParcel => Parcel != null; + + + public Order(AggregateId id, Guid? customerId, OrderStatus status, DateTime createdAt, + string buyerName, string buyerEmail, Address buyerAddress, Parcel parcel = null) + { + Id = id; + CustomerId = customerId; + Status = status; + OrderRequestDate = createdAt; + + CheckBuyerName(buyerName); + BuyerName = buyerName; + CheckBuyerEmail(buyerEmail); + BuyerEmail = buyerEmail; + BuyerAddress = new Address(); + SetAddress(BuyerAddress, buyerAddress.Street, buyerAddress.BuildingNumber, buyerAddress.ApartmentNumber, + buyerAddress.City, buyerAddress.ZipCode, buyerAddress.Country); + + Parcel = parcel; + + DecisionDate = null; + PickedUpAt = null; + DeliveredAt = null; + CannotDeliverAt = null; + CancellationReason = string.Empty; + CannotDeliverReason = string.Empty; + } + + public static Order Create(AggregateId id, Guid customerId, OrderStatus status, DateTime createdAt, + string buyerName, string buyerEmail, Address buyerAddress) + { + var order = new Order(id, customerId == Guid.Empty ? null : customerId, + status, createdAt, buyerName, buyerEmail, buyerAddress); + order.AddEvent(new OrderStateChanged(order)); + + return order; + } + + public void AddParcel(Parcel parcel) + { + if (HasParcel) + { + throw new ParcelAlreadyAddedToOrderException(Id, parcel.Id); + } + Parcel = parcel; + AddEvent(new ParcelAdded(this, parcel)); + } + + public void Approve(DateTime decidedAt) + { + if (Status != OrderStatus.WaitingForDecision && Status != OrderStatus.Cancelled) + { + throw new CannotChangeOrderStateException(Id, Status, OrderStatus.Approved); + } + + DecisionDate = decidedAt; + Status = OrderStatus.Approved; + CancellationReason = string.Empty; + AddEvent(new OrderStateChanged(this)); + } + + public void Cancel(DateTime decidedAt, string reason) + { + if (Status == OrderStatus.Delivered || Status == OrderStatus.Cancelled) + { + throw new CannotChangeOrderStateException(Id, Status, OrderStatus.Cancelled); + } + + DecisionDate = decidedAt; + Status = OrderStatus.Cancelled; + CancellationReason = reason ?? string.Empty; + AddEvent(new OrderStateChanged(this)); + } + + public void Deliver(DateTime deliveredAt) + { + if (Status != OrderStatus.PickedUp) + { + throw new CannotChangeOrderStateException(Id, Status, OrderStatus.Delivered); + } + + DeliveredAt = deliveredAt; + Status = OrderStatus.Delivered; + AddEvent(new OrderStateChanged(this)); + } + + public void SetPickedUp(DateTime pickedUpAt) + { + if (Status != OrderStatus.Approved) + { + throw new CannotChangeOrderStateException(Id, Status, OrderStatus.PickedUp); + } + + PickedUpAt = pickedUpAt; + Status = OrderStatus.PickedUp; + AddEvent(new OrderStateChanged(this)); + } + + public void SetCannotDeliver(string reason, DateTime cannotDeliverAt) + { + if (Status != OrderStatus.PickedUp) + { + throw new CannotChangeOrderStateException(Id, Status, OrderStatus.CannotDeliver); + } + + CannotDeliverAt = cannotDeliverAt; + Status = OrderStatus.CannotDeliver; + CannotDeliverReason = reason ?? string.Empty; + AddEvent(new OrderStateChanged(this)); + } + + public void AddCustomer(Guid customerId) + { + if(CustomerId != null) + { + throw new CustomerAlreadyAddedToOrderException(customerId, Id); + } + CustomerId = customerId; + } + public void CheckBuyerName(string name) + { + if (string.IsNullOrEmpty(name)) + { + throw new InvalidBuyerNameException(name); + } + } + public void CheckBuyerEmail(string email) + { + string pattern = @"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"; + if (!Regex.IsMatch(email, pattern)) + { + throw new InvalidBuyerEmailException(email); + } + } + + private void SetAddress(Address address, string street, string buildingNumber, string apartmentNumber, + string city, string zipCode, string country) + { + CheckAddressElement("street", street); + address.Street = street; + + CheckAddressElement("building number", buildingNumber); + address.BuildingNumber = buildingNumber; + + CheckAddressApartmentNumber("apartment number", ref apartmentNumber); + address.ApartmentNumber = apartmentNumber; + + CheckAddressElement("city", city); + address.City = city; + + CheckAddressZipCode("zip code", zipCode); + address.ZipCode = zipCode; + + CheckAddressElement("country", country); + address.Country = country; + } + + public void CheckAddressElement(string element, string value) + { + if (string.IsNullOrEmpty(value)) + { + throw new InvalidAddressElementException(element, value); + } + } + + public void CheckAddressApartmentNumber(string element, ref string value) + { + if (string.IsNullOrEmpty(value)) + { + value = string.Empty; + } + } + + public void CheckAddressZipCode(string element, string value) + { + string pattern = @"\d{2}[-]\d{3}"; + if (!Regex.IsMatch(value, pattern)) + { + throw new InvalidAddressElementException(element, value); + } + } + } +} + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/OrderStatus.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/OrderStatus.cs new file mode 100644 index 0000000..e13ec62 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/OrderStatus.cs @@ -0,0 +1,14 @@ +namespace SwiftParcel.Services.Orders.Core.Entities +{ + public enum OrderStatus + { + WaitingForDecision, + Approved, + Cancelled, + PickedUp, + Delivered, + CannotDeliver, + + } +} + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/Parcel.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/Parcel.cs new file mode 100644 index 0000000..0e5113f --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/Parcel.cs @@ -0,0 +1,75 @@ +using SwiftParcel.Services.Orders.Core.Exceptions; + +namespace SwiftParcel.Services.Orders.Core.Entities +{ + public class Parcel : IEquatable + { + public Guid Id { get; protected set; } + public string Description { get; protected set; } + public double Width { get; protected set; } + public double Height { get; protected set; } + public double Depth { get; protected set; } + public double Weight { get; protected set; } + public Address Source { get; protected set; } + public Address Destination { get; protected set; } + public Priority Priority { get; protected set; } + public bool AtWeekend { get; protected set; } + public DateTime PickupDate { get; protected set; } + public DateTime DeliveryDate { get; protected set; } + public bool IsCompany { get; protected set; } + public bool VipPackage { get; protected set; } + public DateTime InquireDate { get; protected set; } + public DateTime ValidTo { get; protected set; } + public decimal CalculatedPrice { get; protected set; } + + public Parcel(Guid id, string description, double width, double height, + double depth, double weight, Address source, Address destination, Priority priority, bool atWeekend, + DateTime pickupDate, DateTime deliveryDate, bool isCompany, bool vipPackage, DateTime createdAt, + DateTime validTo, decimal calculatedPrice) + { + Id = id; + Description = description; + Width = width; + Height = height; + Depth = depth; + Weight = weight; + Source = source; + Destination = destination; + Priority = priority; + AtWeekend = atWeekend; + PickupDate = pickupDate; + DeliveryDate = deliveryDate; + IsCompany = isCompany; + VipPackage = vipPackage; + InquireDate = createdAt; + ValidTo = validTo; + CalculatedPrice = calculatedPrice; + } + public void ValidateRequest(DateTime requestDate) + { + if (requestDate > ValidTo) + { + throw new ParcelRequestExpiredException(Id, ValidTo, requestDate); + } + } + + public bool Equals(Parcel other) + { + if (ReferenceEquals(null, other)) return false; + return ReferenceEquals(this, other) || Id.Equals(other.Id); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((Parcel) obj); + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } + } +} + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/Priority.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/Priority.cs new file mode 100644 index 0000000..a9bbb65 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Entities/Priority.cs @@ -0,0 +1,8 @@ +namespace SwiftParcel.Services.Orders.Core.Entities +{ + public enum Priority + { + Low, + High + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Events/OrderStateChanged.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Events/OrderStateChanged.cs new file mode 100644 index 0000000..7dcbf51 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Events/OrderStateChanged.cs @@ -0,0 +1,14 @@ +using SwiftParcel.Services.Orders.Core.Entities; + +namespace SwiftParcel.Services.Orders.Core.Events +{ + public class OrderStateChanged : IDomainEvent + { + public Order Order { get; } + + public OrderStateChanged(Order order) + { + Order = order; + } + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Events/ParcelAdded.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Events/ParcelAdded.cs new file mode 100644 index 0000000..1305cdb --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Events/ParcelAdded.cs @@ -0,0 +1,18 @@ +using SwiftParcel.Services.Orders.Core.Entities; + +namespace SwiftParcel.Services.Orders.Core.Events +{ + public class ParcelAdded : IDomainEvent + { + public Order Order { get; } + public Parcel Parcel { get; } + + public ParcelAdded(Order order, Parcel parcel) + { + Order = order; + Parcel = parcel; + } + } +} + + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/CannotChangeOrderStateException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/CannotChangeOrderStateException.cs new file mode 100644 index 0000000..833a0b3 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/CannotChangeOrderStateException.cs @@ -0,0 +1,20 @@ +using SwiftParcel.Services.Orders.Core.Entities; + +namespace SwiftParcel.Services.Orders.Core.Exceptions +{ + public class CannotChangeOrderStateException : DomainException + { + public override string Code { get; } = "cannot_change_order_state"; + public Guid OrderId { get; } + public OrderStatus CurrentStatus { get; } + public OrderStatus NextStatus { get; } + + public CannotChangeOrderStateException(Guid orderId, OrderStatus currentStatus, OrderStatus nextStatus) : + base($"Cannot change state for order with id: '{orderId}' from {currentStatus} to {nextStatus}'") + { + OrderId = orderId; + CurrentStatus = currentStatus; + NextStatus = nextStatus; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/CannotDeleteOrderException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/CannotDeleteOrderException.cs new file mode 100644 index 0000000..249c95b --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/CannotDeleteOrderException.cs @@ -0,0 +1,14 @@ +namespace SwiftParcel.Services.Orders.Core.Exceptions +{ + public class CannotDeleteOrderException : DomainException + { + public override string Code { get; } = "cannot_delete_order"; + public Guid Id { get; } + + public CannotDeleteOrderException(Guid id) : base($"Cannot delete order with id: {id}.") + { + Id = id; + } + } +} + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/CustomerAlreadyAddedToOrderException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/CustomerAlreadyAddedToOrderException.cs new file mode 100644 index 0000000..6b9a2e2 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/CustomerAlreadyAddedToOrderException.cs @@ -0,0 +1,17 @@ +namespace SwiftParcel.Services.Orders.Core.Exceptions +{ + public class CustomerAlreadyAddedToOrderException : DomainException + { + public override string Code { get; } = "customer_already_added_to_order"; + public Guid CustomerId { get; } + public Guid OrderId { get; } + + + public CustomerAlreadyAddedToOrderException(Guid customerId, Guid orderId) + : base($"Customer with id: {customerId} was already added to order with id: {orderId}.") + { + CustomerId = customerId; + OrderId = orderId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/CustomerNotFoundException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/CustomerNotFoundException.cs new file mode 100644 index 0000000..ba093c8 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/CustomerNotFoundException.cs @@ -0,0 +1,15 @@ +namespace SwiftParcel.Services.Orders.Core.Exceptions +{ + public class CustomerNotFoundException : DomainException + { + public override string Code { get; } = "customer_not_found"; + public Guid Id { get; } + + public CustomerNotFoundException(Guid id) : base($"Customer with id: {id} was not found.") + { + Id = id; + } + } +} + + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/DomainException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/DomainException.cs new file mode 100644 index 0000000..c786ffb --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/DomainException.cs @@ -0,0 +1,12 @@ +namespace SwiftParcel.Services.Orders.Core.Exceptions +{ + public abstract class DomainException : Exception + { + public virtual string Code { get; } + + protected DomainException(string message) : base(message) + { + } + } +} + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/InvalidAddressElementException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/InvalidAddressElementException.cs new file mode 100644 index 0000000..6741d56 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/InvalidAddressElementException.cs @@ -0,0 +1,15 @@ +namespace SwiftParcel.Services.Orders.Core.Exceptions +{ + public class InvalidAddressElementException : DomainException + { + public override string Code { get; } = "invalid_address_element"; + public string AddressElement { get; } + public string Value { get; } + + public InvalidAddressElementException(string element, string value) : base($"Invalid address element {element}: {value}.") + { + AddressElement = element; + Value = value; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/InvalidAggregateIdException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/InvalidAggregateIdException.cs new file mode 100644 index 0000000..e36f426 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/InvalidAggregateIdException.cs @@ -0,0 +1,13 @@ +namespace SwiftParcel.Services.Orders.Core.Exceptions +{ + public class InvalidAggregateIdException : DomainException + { + public override string Code { get; } = "invalid_aggregate_id"; + public Guid Id { get; } + + public InvalidAggregateIdException(Guid id) : base($"Invalid aggregate id.") + { + Id = id; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/InvalidBuyerEmailException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/InvalidBuyerEmailException.cs new file mode 100644 index 0000000..5714908 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/InvalidBuyerEmailException.cs @@ -0,0 +1,13 @@ +namespace SwiftParcel.Services.Orders.Core.Exceptions +{ + public class InvalidBuyerEmailException : DomainException + { + public override string Code { get; } = "invalid_buyer_email"; + public string BuyerEmail { get; } + + public InvalidBuyerEmailException(string buyerEmail) : base($"Invalid buyer email: {buyerEmail}.") + { + BuyerEmail = buyerEmail; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/InvalidBuyerNameException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/InvalidBuyerNameException.cs new file mode 100644 index 0000000..2b35829 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/InvalidBuyerNameException.cs @@ -0,0 +1,13 @@ +namespace SwiftParcel.Services.Orders.Core.Exceptions +{ + public class InvalidBuyerNameException : DomainException + { + public override string Code { get; } = "invalid_buyer_name"; + public string BuyerName { get; } + + public InvalidBuyerNameException(string buyerName) : base($"Invalid buyer name: {buyerName}.") + { + BuyerName = buyerName; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/ParcelAlreadyAddedToOrderException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/ParcelAlreadyAddedToOrderException.cs new file mode 100644 index 0000000..5789bc7 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/ParcelAlreadyAddedToOrderException.cs @@ -0,0 +1,16 @@ +namespace SwiftParcel.Services.Orders.Core.Exceptions +{ + public class ParcelAlreadyAddedToOrderException : DomainException + { + public override string Code { get; } = "parcel_already_added_to_order"; + public Guid OrderId { get; } + public Guid ParcelId { get; } + + public ParcelAlreadyAddedToOrderException(Guid orderId, Guid parcelId) + : base($"Parcel with id: {parcelId} was already added to order: {orderId}.") + { + OrderId = orderId; + ParcelId = parcelId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/ParcelRequestExpiredException.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/ParcelRequestExpiredException.cs new file mode 100644 index 0000000..0c0dc21 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Exceptions/ParcelRequestExpiredException.cs @@ -0,0 +1,14 @@ +namespace SwiftParcel.Services.Orders.Core.Exceptions +{ + public class ParcelRequestExpiredException : DomainException + { + public override string Code { get; } = "parcel_request_expired"; + public Guid ParcelId { get; } + + public ParcelRequestExpiredException(Guid parcelId, DateTime validTo, DateTime now) : + base($"Parcel request with id: '{parcelId}' has expired. Requested at: {now}, valid to: {validTo}.") + { + ParcelId = parcelId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/IDomainEvent.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/IDomainEvent.cs new file mode 100644 index 0000000..eaf9027 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/IDomainEvent.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Orders.Core +{ + public interface IDomainEvent + { + + } +} + + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Repositories/ICustomerRepository.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Repositories/ICustomerRepository.cs new file mode 100644 index 0000000..57a60bd --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Repositories/ICustomerRepository.cs @@ -0,0 +1,12 @@ +using SwiftParcel.Services.Orders.Core.Entities; + +namespace SwiftParcel.Services.Orders.Core.Repositories +{ + public interface ICustomerRepository + { + Task GetAsync(Guid id); + Task ExistsAsync(Guid id); + Task AddAsync(Customer customer); + } +} + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Repositories/IOrderRepository.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Repositories/IOrderRepository.cs new file mode 100644 index 0000000..0d94a4b --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/Repositories/IOrderRepository.cs @@ -0,0 +1,15 @@ +using SwiftParcel.Services.Orders.Core.Entities; + +namespace SwiftParcel.Services.Orders.Core.Repositories +{ + public interface IOrderRepository + { + Task GetAsync(Guid id); + Task GetContainingParcelAsync(Guid parcelId); + Task AddAsync(Order order); + Task UpdateAsync(Order order); + Task DeleteAsync(Guid id); + } +} + + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core.csproj b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core.csproj new file mode 100644 index 0000000..640868e --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core/SwiftParcel.Services.Orders.Core.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + disable + + + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Brevo/BrevoOptions.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Brevo/BrevoOptions.cs new file mode 100644 index 0000000..f4d4aad --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Brevo/BrevoOptions.cs @@ -0,0 +1,9 @@ +namespace SwiftParcel.Services.Orders.Infrastructure.Brevo +{ + public class BrevoOptions + { + public string ApiKey { get; set; } + public string SenderEmail { get; set; } + public string SenderName { get; set; } + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Brevo/Commands/Handlers/SendApprovalEmailHandler.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Brevo/Commands/Handlers/SendApprovalEmailHandler.cs new file mode 100644 index 0000000..6a7f745 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Brevo/Commands/Handlers/SendApprovalEmailHandler.cs @@ -0,0 +1,77 @@ +using System.Windows.Input; +using Convey.CQRS.Commands; +using Microsoft.Extensions.Configuration; +using sib_api_v3_sdk.Api; +using sib_api_v3_sdk.Client; +using sib_api_v3_sdk.Model; +using SwiftParcel.Services.Orders.Application.Exceptions; +using SwiftParcel.Services.Orders.Core.Repositories; +using SwiftParcel.Services.Orders.Core.Exceptions; +using Microsoft.Extensions.Logging; +using SwiftParcel.Services.Orders.Application.Services; +using System.Reflection.Metadata; +using SwiftParcel.Services.Orders.Application.Commands; +using SwiftParcel.Services.Orders.Infrastructure.Brevo.Pdf.Documents; +using SwiftParcel.Services.Orders.Infrastructure.Brevo.Pdf.Models; +using QuestPDF.Fluent; +using QuestPDF.Infrastructure; +using QuestPDF.Previewer; + +namespace SwiftParcel.Services.Orders.Infrastructure.Brevo.Commands.Handlers +{ + public class SendApprovalEmailHandler: ICommandHandler + { + private const string _senderName = "SwiftParcel"; + private const string _senderEmail = "switfparcel2023@gmail.com"; + private readonly TransactionalEmailsApi _apiInstance; + private readonly ILogger _logger; + public SendApprovalEmailHandler(ILogger logger) + { + _apiInstance = new TransactionalEmailsApi(); + _logger = logger; + } + + public async System.Threading.Tasks.Task HandleAsync(SendApprovalEmail command, CancellationToken cancellationToken) + { + var sender = new SendSmtpEmailSender(_senderName, _senderEmail); + var to = new List + { + new SendSmtpEmailTo(command.CustomerEmail, command.CustomerName) + }; + var parameters = new Dictionary + { + { "orderId", command.OrderId.ToString()}, + }; + + var model = new InvoiceModel() + { + OrderId = command.OrderId.ToString(), + IssueDate = command.IssueDate, + Parcel = command.Parcel, + CustomerEmail = command.CustomerEmail, + CustomerName = command.CustomerName, + CustomerAddress = command.CustomerAddress + }; + + var document = new InvoiceDocument(model); + var invoice = document.GeneratePdf(); + + + var attachment = new List + { + new SendSmtpEmailAttachment(null, invoice, $"Invoice_{command.OrderId}.pdf") + }; + try + { + var sendSmtpEmail = new SendSmtpEmail(sender, to, null, null, null, null, null, + null, attachment, null, 3, parameters); + CreateSmtpEmail result = await _apiInstance.SendTransacEmailAsync(sendSmtpEmail); + _logger.LogInformation("Email sent to {email}", command.CustomerEmail); + } + catch (Exception e) + { + throw new SmtpException(e.Message); + } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Brevo/Commands/Handlers/SendCancellationEmailHandler.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Brevo/Commands/Handlers/SendCancellationEmailHandler.cs new file mode 100644 index 0000000..ffc23d5 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Brevo/Commands/Handlers/SendCancellationEmailHandler.cs @@ -0,0 +1,53 @@ +using System.Windows.Input; +using Convey.CQRS.Commands; +using Microsoft.Extensions.Configuration; +using sib_api_v3_sdk.Api; +using sib_api_v3_sdk.Client; +using sib_api_v3_sdk.Model; +using SwiftParcel.Services.Orders.Application.Exceptions; +using SwiftParcel.Services.Orders.Core.Repositories; +using SwiftParcel.Services.Orders.Core.Exceptions; +using Microsoft.Extensions.Logging; +using SwiftParcel.Services.Orders.Application.Commands; + +namespace SwiftParcel.Services.Orders.Infrastructure.Brevo.Commands.Handlers +{ + public class SendCancellationEmailHandler: ICommandHandler + { + private const string _senderName = "SwiftParcel"; + private const string _senderEmail = "switfparcel2023@gmail.com"; + private readonly TransactionalEmailsApi _apiInstance; + private readonly ILogger _logger; + public SendCancellationEmailHandler(ILogger logger) + { + _apiInstance = new TransactionalEmailsApi(); + _logger = logger; + } + + public async System.Threading.Tasks.Task HandleAsync(SendCancellationEmail command, CancellationToken cancellationToken) + { + var sender = new SendSmtpEmailSender(_senderName, _senderEmail); + var to = new List + { + new SendSmtpEmailTo(command.CustomerEmail, command.CustomerName) + }; + var parameters = new Dictionary + { + { "orderId", command.OrderId.ToString()}, + { "reason", command.Reason} + }; + + try + { + var sendSmtpEmail = new SendSmtpEmail(sender, to, null, null, null, null, null, + null, null, null, 2, parameters); + CreateSmtpEmail result = await _apiInstance.SendTransacEmailAsync(sendSmtpEmail); + _logger.LogInformation("Email sent to {email}", command.CustomerEmail); + } + catch (Exception e) + { + throw new SmtpException(e.Message); + } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Brevo/Pdf/Documents/InvoiceDocument.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Brevo/Pdf/Documents/InvoiceDocument.cs new file mode 100644 index 0000000..9397742 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Brevo/Pdf/Documents/InvoiceDocument.cs @@ -0,0 +1,210 @@ +using System.Globalization; +using System.Linq; +using QuestPDF.Drawing; +using QuestPDF.Fluent; +using QuestPDF.Helpers; +using QuestPDF.Infrastructure; +using SwiftParcel.Services.Orders.Infrastructure.Brevo.Pdf.Models; +using SwiftParcel.Services.Orders.Core.Entities; + +namespace SwiftParcel.Services.Orders.Infrastructure.Brevo.Pdf.Documents +{ + public class InvoiceDocument : IDocument + { + public InvoiceModel Model { get; } + + public InvoiceDocument(InvoiceModel model) + { + Model = model; + } + + public DocumentMetadata GetMetadata() => DocumentMetadata.Default; + + public void Compose(IDocumentContainer container) + { + container + .Page(page => + { + page.Margin(50); + + page.Header().Element(ComposeHeader); + page.Content().Element(ComposeContent); + + page.Footer().AlignCenter().Text(text => + { + text.CurrentPageNumber(); + text.Span(" / "); + text.TotalPages(); + }); + }); + } + + void ComposeHeader(IContainer container) + { + container.Row(row => + { + row.RelativeItem().Column(column => + { + column + .Item().Text($"Invoice for order with id:") + .FontSize(20).SemiBold().FontColor(Colors.Black); + column + .Item().Text($"#{Model.OrderId}") + .FontSize(20).SemiBold().FontColor(Colors.Blue.Darken2); + column.Item().Text(text => + { + text.Span("Issue date: ").SemiBold(); + text.Span($"{Model.IssueDate:h:mm tt, d MMMM yyyy}"); + }); + }); + + }); + } + + void ComposeContent(IContainer container) + { + container.PaddingVertical(40).Column(column => + { + column.Spacing(20); + + column.Item().Element(ComposeCustomerDetails); + + column.Item().Row(row => + { + row.RelativeItem().Component(new AddressComponent("Source", Model.Parcel.Source)); + row.ConstantItem(50); + row.RelativeItem().Component(new AddressComponent("Destination", Model.Parcel.Destination)); + }); + + column.Item().Element(ComposeTable); + column.Item().Element(ComposeTableDetails); + + if (!string.IsNullOrWhiteSpace(Model.Parcel.Description)) + column.Item().PaddingTop(5).Element(ComposeDescription); + + var totalPrice = Model.Parcel.CalculatedPrice; + column.Item().PaddingRight(5).AlignRight().Text($"Grand total: {totalPrice:C}").SemiBold(); + }); + } + + void ComposeCustomerDetails(IContainer container) + { + container.ShowEntire().Column(column => + { + column.Spacing(2); + + column.Item().Text("Customer details").SemiBold(); + column.Item().PaddingBottom(5).LineHorizontal(1); + + column.Item().Text($"{Model.CustomerName}"); + column.Item().Text($"{Model.CustomerEmail}"); + column.Item().Text($"{Model.CustomerAddress.Street} {Model.CustomerAddress.BuildingNumber}" + + (string.IsNullOrWhiteSpace(Model.CustomerAddress.ApartmentNumber) ? "" : $"/{Model.CustomerAddress.ApartmentNumber}")); + column.Item().Text($"{Model.CustomerAddress.City}, {Model.CustomerAddress.ZipCode}"); + column.Item().Text($"{Model.CustomerAddress.Country}"); + }); + } + + void ComposeTable(IContainer container) + { + var headerStyle = TextStyle.Default.SemiBold(); + + container.Table(table => + { + table.ColumnsDefinition(columns => + { + columns.RelativeColumn(3); + columns.RelativeColumn(); + columns.RelativeColumn(); + columns.RelativeColumn(); + }); + + table.Header(header => + { + header.Cell().Text("Inquire date").Style(headerStyle); + header.Cell().AlignRight().Text("Size [cm]").Style(headerStyle); + header.Cell().AlignRight().Text("Weight [kg]").Style(headerStyle); + header.Cell().AlignRight().Text("Priority").Style(headerStyle); + header.Cell().ColumnSpan(4).PaddingTop(5).BorderBottom(1).BorderColor(Colors.Black); + }); + + table.Cell().Element(CellStyle).Text($"{Model.Parcel.InquireDate.Date:d}"); + table.Cell().Element(CellStyle).AlignRight().Text($"{Model.Parcel.Width}x{Model.Parcel.Height}x{Model.Parcel.Depth}"); + table.Cell().Element(CellStyle).AlignRight().Text($"{Model.Parcel.Weight}"); + table.Cell().Element(CellStyle).AlignRight().Text($"{Model.Parcel.Priority}"); + + static IContainer CellStyle(IContainer container) => container.BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingVertical(5); + }); + } + void ComposeTableDetails(IContainer container) + { + var headerStyle = TextStyle.Default.SemiBold(); + container.Table(table => + { + table.ColumnsDefinition(columns => + { + columns.RelativeColumn(3); + columns.RelativeColumn(); + columns.RelativeColumn(); + columns.RelativeColumn(); + + }); + + table.Header(header => + { + header.Cell().Text("Delivery date").Style(headerStyle); + header.Cell().AlignRight().Text("Pickup date").Style(headerStyle); + header.Cell().AlignRight().Text("Delivery at weekend").Style(headerStyle); + header.Cell().AlignRight().Text("VIP package").Style(headerStyle); + + header.Cell().ColumnSpan(4).PaddingTop(5).BorderBottom(1).BorderColor(Colors.Black); + }); + + table.Cell().Element(CellStyle).Text($"{Model.Parcel.DeliveryDate.Date:d}"); + table.Cell().Element(CellStyle).AlignRight().Text($"{Model.Parcel.PickupDate.Date:d}"); + var atWeekend = Model.Parcel.AtWeekend ? "Yes" : "No"; + table.Cell().Element(CellStyle).AlignRight().Text($"{atWeekend}"); + var isVipPackage = Model.Parcel.VipPackage ? "Yes" : "No"; + table.Cell().Element(CellStyle).AlignRight().Text($"{isVipPackage}"); + + static IContainer CellStyle(IContainer container) => container.BorderBottom(1).BorderColor(Colors.Grey.Lighten2).PaddingVertical(5); + }); + } + void ComposeDescription(IContainer container) + { + container.ShowEntire().Background(Colors.Grey.Lighten3).Padding(10).Column(column => + { + column.Spacing(5); + column.Item().Text("Comments").FontSize(12).SemiBold(); + column.Item().Text(Model.Parcel.Description); + }); + } + } + + public class AddressComponent : IComponent + { + private string Title { get; } + private Address Address { get; } + public AddressComponent(string title, Address address) + { + Title = title; + Address = address; + } + + public void Compose(IContainer container) + { + container.ShowEntire().Column(column => + { + column.Spacing(2); + + column.Item().Text(Title).SemiBold(); + column.Item().PaddingBottom(5).LineHorizontal(1); + + column.Item().Text($"{Address.Street} {Address.BuildingNumber}" + + (string.IsNullOrWhiteSpace(Address.ApartmentNumber) ? "" : $"/{Address.ApartmentNumber}")); + column.Item().Text($"{Address.City}, {Address.ZipCode}"); + column.Item().Text($"{Address.Country}"); + }); + } + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Brevo/Pdf/Models/InvoiceModel.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Brevo/Pdf/Models/InvoiceModel.cs new file mode 100644 index 0000000..c9552ab --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Brevo/Pdf/Models/InvoiceModel.cs @@ -0,0 +1,14 @@ +using SwiftParcel.Services.Orders.Core.Entities; + +namespace SwiftParcel.Services.Orders.Infrastructure.Brevo.Pdf.Models +{ + public class InvoiceModel + { + public string OrderId { get; set; } + public DateTime IssueDate { get; set; } + public string CustomerEmail { get; set; } + public string CustomerName { get; set; } + public Address CustomerAddress { get; set; } + public Parcel Parcel { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Contexts/AppContext.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Contexts/AppContext.cs new file mode 100644 index 0000000..ccd4fab --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Contexts/AppContext.cs @@ -0,0 +1,28 @@ +using System; +using SwiftParcel.Services.Orders.Application; + +namespace SwiftParcel.Services.Orders.Infrastructure.Contexts +{ + internal class AppContext : IAppContext + { + public string RequestId { get; } + public IIdentityContext Identity { get; } + + internal AppContext() : this(Guid.NewGuid().ToString("N"), IdentityContext.Empty) + { + } + + internal AppContext(CorrelationContext context) : this(context.CorrelationId, + context.User is null ? IdentityContext.Empty : new IdentityContext(context.User)) + { + } + + internal AppContext(string requestId, IIdentityContext identity) + { + RequestId = requestId; + Identity = identity; + } + + internal static IAppContext Empty => new AppContext(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Contexts/AppContextFactory.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Contexts/AppContextFactory.cs new file mode 100644 index 0000000..78b0d8c --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Contexts/AppContextFactory.cs @@ -0,0 +1,35 @@ +using Convey.MessageBrokers; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using SwiftParcel.Services.Orders.Application; + +namespace SwiftParcel.Services.Orders.Infrastructure.Contexts +{ + internal sealed class AppContextFactory : IAppContextFactory + { + private readonly ICorrelationContextAccessor _contextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + + public AppContextFactory(ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor) + { + _contextAccessor = contextAccessor; + _httpContextAccessor = httpContextAccessor; + } + + public IAppContext Create() + { + if (_contextAccessor.CorrelationContext is {}) + { + var payload = JsonConvert.SerializeObject(_contextAccessor.CorrelationContext); + + return string.IsNullOrWhiteSpace(payload) + ? AppContext.Empty + : new AppContext(JsonConvert.DeserializeObject(payload)); + } + + var context = _httpContextAccessor.GetCorrelationContext(); + + return context is null ? AppContext.Empty : new AppContext(context); + } + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Contexts/CorrelationContext.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Contexts/CorrelationContext.cs new file mode 100644 index 0000000..7ea53c1 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Contexts/CorrelationContext.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; + +namespace SwiftParcel.Services.Orders.Infrastructure.Contexts +{ + internal class CorrelationContext + { + public string CorrelationId { get; set; } + public string SpanContext { get; set; } + public UserContext User { get; set; } + public string ResourceId { get; set; } + public string TraceId { get; set; } + public string ConnectionId { get; set; } + public string Name { get; set; } + public DateTime CreatedAt { get; set; } + + public class UserContext + { + public string Id { get; set; } + public bool IsAuthenticated { get; set; } + public string Role { get; set; } + public IDictionary Claims { get; set; } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Contexts/IdentityContext.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Contexts/IdentityContext.cs new file mode 100644 index 0000000..9f67813 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Contexts/IdentityContext.cs @@ -0,0 +1,35 @@ +using SwiftParcel.Services.Orders.Application; + +namespace SwiftParcel.Services.Orders.Infrastructure.Contexts +{ + internal class IdentityContext : IIdentityContext + { + public Guid Id { get; } + public string Role { get; } = string.Empty; + public bool IsAuthenticated { get; } + public bool IsOfficeWorker { get; } + public bool IsCourier { get; } + public IDictionary Claims { get; } = new Dictionary(); + + internal IdentityContext() + { + } + + internal IdentityContext(CorrelationContext.UserContext context) + : this(context.Id, context.Role, context.IsAuthenticated, context.Claims) + { + } + + internal IdentityContext(string id, string role, bool isAuthenticated, IDictionary claims) + { + Id = Guid.TryParse(id, out var userId) ? userId : Guid.Empty; + Role = role ?? string.Empty; + IsAuthenticated = isAuthenticated; + IsOfficeWorker = Role.Equals("officeworker", StringComparison.InvariantCultureIgnoreCase); + IsCourier = Role.Equals("courier", StringComparison.InvariantCultureIgnoreCase); + Claims = claims ?? new Dictionary(); + } + + internal static IIdentityContext Empty => new IdentityContext(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs new file mode 100644 index 0000000..4b7311b --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; + +namespace SwiftParcel.Services.Orders.Infrastructure.Decorators +{ + internal sealed class OutboxCommandHandlerDecorator : ICommandHandler + where TCommand : class, ICommand + { + private readonly ICommandHandler _handler; + private readonly IMessageOutbox _outbox; + private readonly string _messageId; + private readonly bool _enabled; + + public OutboxCommandHandlerDecorator(ICommandHandler handler, IMessageOutbox outbox, + OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) + { + _handler = handler; + _outbox = outbox; + _enabled = outboxOptions.Enabled; + + var messageProperties = messagePropertiesAccessor.MessageProperties; + _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId) + ? Guid.NewGuid().ToString("N") + : messageProperties.MessageId; + } + + public Task HandleAsync(TCommand command, CancellationToken cancellationToken) + => _enabled + ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(command)) + : _handler.HandleAsync(command); + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs new file mode 100644 index 0000000..37cfb87 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -0,0 +1,37 @@ +using System; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; + + +namespace SwiftParcel.Services.Orders.Infrastructure.Decorators +{ + internal sealed class OutboxEventHandlerDecorator : IEventHandler + where TEvent : class, IEvent + { + private readonly IEventHandler _handler; + private readonly IMessageOutbox _outbox; + private readonly string _messageId; + private readonly bool _enabled; + + public OutboxEventHandlerDecorator(IEventHandler handler, IMessageOutbox outbox, + OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) + { + _handler = handler; + _outbox = outbox; + _enabled = outboxOptions.Enabled; + + var messageProperties = messagePropertiesAccessor.MessageProperties; + _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId) + ? Guid.NewGuid().ToString("N") + : messageProperties.MessageId; + } + + public Task HandleAsync(TEvent @event, CancellationToken cancellationToken) + => _enabled + ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(@event)) + : _handler.HandleAsync(@event); + } +} + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Exceptions/ExceptionToMessageMapper.cs new file mode 100644 index 0000000..6ea772a --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -0,0 +1,63 @@ +using System; +using Convey.MessageBrokers.RabbitMQ; +using SwiftParcel.Services.Orders.Application.Commands; +using SwiftParcel.Services.Orders.Application.Exceptions; +using SwiftParcel.Services.Orders.Application.Events.Rejected; +using SwiftParcel.Services.Orders.Application.Events.External; +using SwiftParcel.Services.Orders.Core.Exceptions; +using SwiftParcel.Services.Orders.Application.Queries; + + +namespace SwiftParcel.Services.Orders.Infrastructure.Exceptions +{ + public class ExceptionToMessageMapper : IExceptionToMessageMapper + { + public object Map(Exception exception, object message) + => exception switch + { + CannotDeleteOrderException ex => (object) new DeleteOrderRejected(ex.Id, ex.Message, ex.Code), + ParcelRequestExpiredException ex => message switch + { + CreateOrder m => new CreateOrderRejected(m.OrderId, m.CustomerId, m.ParcelId, ex.Message, ex.Code), + _ => null + }, + + CustomerNotFoundException ex => message switch + { + CreateOrder m => new CreateOrderRejected(m.OrderId, m.CustomerId, m.ParcelId, ex.Message, ex.Code), + _ => null + }, + + OrderNotFoundException ex + => message switch + { + ApproveOrder m => new ApproveOrderRejected(m.OrderId, ex.Message, ex.Code), + CancelOrder m => new CancelOrderRejected(m.OrderId, ex.Message, ex.Code), + DeleteOrder m => new DeleteOrderRejected(m.OrderId, ex.Message, ex.Code), + DeliveryCompleted _ => new OrderForDeliveryNotFound(ex.Id, ex.Message, ex.Code), + DeliveryFailed _ => new OrderForDeliveryNotFound(ex.Id, ex.Message, ex.Code), + DeliveryPickedUp _ => new OrderForDeliveryNotFound(ex.Id, ex.Message, ex.Code), + _ => null + }, + + ParcelNotFoundException ex + => message switch + { + CreateOrder m => new CreateOrderRejected(m.OrderId, m.CustomerId, m.ParcelId, ex.Message, ex.Code), + _ => null + }, + ParcelAlreadyAddedToOrderException ex => message switch + { + CreateOrder m => new CreateOrderRejected(m.OrderId, m.CustomerId, m.ParcelId, ex.Message, ex.Code), + _ => null + }, + UnauthorizedOrderAccessException ex + => message switch + { + DeleteOrder m => new DeleteOrderRejected(m.OrderId, ex.Message, ex.Code), + _ => null + }, + _ => null, + }; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Exceptions/ExceptionToResponseMapper.cs new file mode 100644 index 0000000..e5579a8 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Concurrent; +using System.Net; +using Convey; +using Convey.WebApi.Exceptions; +using SwiftParcel.Services.Orders.Application.Exceptions; +using SwiftParcel.Services.Orders.Core.Exceptions; + +namespace SwiftParcel.Services.Orders.Infrastructure.Exceptions +{ + internal sealed class ExceptionToResponseMapper : IExceptionToResponseMapper + { + private static readonly ConcurrentDictionary Codes = new ConcurrentDictionary(); + + public ExceptionResponse Map(Exception exception) + => exception switch + { + DomainException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message}, + HttpStatusCode.BadRequest), + AppException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message}, + HttpStatusCode.BadRequest), + _ => new ExceptionResponse(new {code = "error", reason = "There was an error."}, + HttpStatusCode.BadRequest) + }; + + private static string GetCode(Exception exception) + { + var type = exception.GetType(); + if (Codes.TryGetValue(type, out var code)) + { + return code; + } + + var exceptionCode = exception switch + { + DomainException domainException when !string.IsNullOrWhiteSpace(domainException.Code) => domainException + .Code, + AppException appException when !string.IsNullOrWhiteSpace(appException.Code) => appException.Code, + _ => exception.GetType().Name.Underscore().Replace("_exception", string.Empty) + }; + + Codes.TryAdd(type, exceptionCode); + + return exceptionCode; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Extensions.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Extensions.cs new file mode 100644 index 0000000..f1f849e --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Extensions.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Convey; +using Convey.CQRS.Commands; +using Convey.CQRS.Events; +using Convey.CQRS.Queries; +using Convey.Discovery.Consul; +using Convey.Docs.Swagger; +using Convey.HTTP; +using Convey.LoadBalancing.Fabio; +using Convey.MessageBrokers; +using Convey.MessageBrokers.CQRS; +using Convey.MessageBrokers.Outbox; +using Convey.MessageBrokers.Outbox.Mongo; +using Convey.MessageBrokers.RabbitMQ; +using Convey.Metrics.AppMetrics; +using Convey.Persistence.MongoDB; +using Convey.Persistence.Redis; +using Convey.Security; +using Convey.Tracing.Jaeger; +using Convey.Tracing.Jaeger.RabbitMQ; +using Convey.WebApi; +using Convey.WebApi.CQRS; +using Convey.WebApi.Swagger; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using SwiftParcel.Services.Orders.Application; +using SwiftParcel.Services.Orders.Application.Services; +using SwiftParcel.Services.Orders.Application.Services.Clients; +using SwiftParcel.Services.Orders.Application.Commands; +using SwiftParcel.Services.Orders.Application.Events.External; +using SwiftParcel.Services.Orders.Core.Repositories; +using SwiftParcel.Services.Orders.Infrastructure.Contexts; +using SwiftParcel.Services.Orders.Infrastructure.Decorators; +using SwiftParcel.Services.Orders.Infrastructure.Exceptions; +using SwiftParcel.Services.Orders.Infrastructure.Mongo.Documents; +using SwiftParcel.Services.Orders.Infrastructure.Mongo.Repositories; +using SwiftParcel.Services.Orders.Infrastructure.Services; +using SwiftParcel.Services.Orders.Infrastructure.Services.Clients; +using SwiftParcel.Services.Orders.Infrastructure.Logging; +using SwiftParcel.Services.Orders.Infrastructure.Brevo; +using Microsoft.Extensions.Configuration; +using Jaeger; +using QuestPDF; +using QuestPDF.Infrastructure; + + +namespace SwiftParcel.Services.Orders.Infrastructure +{ + public static class Extensions + { + public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + { + builder.Services.AddSingleton(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddSingleton(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(ctx => ctx.GetRequiredService().Create()); + builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>)); + builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>)); + + return builder + .AddErrorHandler() + .AddQueryHandlers() + .AddInMemoryQueryDispatcher() + .AddHttpClient() + .AddConsul() + .AddFabio() + .AddRabbitMq(plugins: p => p.AddJaegerRabbitMqPlugin()) + .AddMessageOutbox(o => o.AddMongo()) + .AddExceptionToMessageMapper() + .AddMongo() + .AddRedis() + .AddMetrics() + .AddJaeger() + .AddBrevo() + .AddHandlersLogging() + .AddMongoRepository("customers") + .AddMongoRepository("orders") + .AddWebApiSwaggerDocs() + .AddSecurity(); + } + + public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app) + { + app.UseErrorHandler() + .UseSwaggerDocs() + .UseJaeger() + .UseConvey() + .UsePublicContracts() + .UseMetrics() + .UseRabbitMq() + .SubscribeCommand() + .SubscribeCommand() + .SubscribeCommand() + .SubscribeCommand() + .SubscribeCommand() + .SubscribeCommand() + .SubscribeEvent() + .SubscribeEvent() + .SubscribeEvent() + .SubscribeEvent(); + return app; + } + + internal static CorrelationContext GetCorrelationContext(this IHttpContextAccessor accessor) + => accessor.HttpContext?.Request.Headers.TryGetValue("Correlation-Context", out var json) is true + ? JsonConvert.DeserializeObject(json.FirstOrDefault() ?? string.Empty) + : null; + + internal static IDictionary GetHeadersToForward(this IMessageProperties messageProperties) + { + const string sagaHeader = "Saga"; + if (messageProperties?.Headers is null || !messageProperties.Headers.TryGetValue(sagaHeader, out var saga)) + { + return null; + } + + return saga is null + ? null + : new Dictionary + { + [sagaHeader] = saga + }; + } + + internal static string GetSpanContext(this IMessageProperties messageProperties, string header) + { + if (messageProperties is null) + { + return string.Empty; + } + + if (messageProperties.Headers.TryGetValue(header, out var span) && span is byte[] spanBytes) + { + return Encoding.UTF8.GetString(spanBytes); + } + + return string.Empty; + } + internal static IConveyBuilder AddBrevo(this IConveyBuilder builder) + { + // this should be replaces with some cloud key vault + var apiKey = builder.GetOptions("brevoApiKey"); + sib_api_v3_sdk.Client.Configuration.Default.ApiKey.Add("api-key", apiKey.ApiKey); + Settings.License = LicenseType.Community; + return builder; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/IAppContextFactory.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/IAppContextFactory.cs new file mode 100644 index 0000000..a5ad2b7 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/IAppContextFactory.cs @@ -0,0 +1,10 @@ +using SwiftParcel.Services.Orders.Application; + +namespace SwiftParcel.Services.Orders.Infrastructure +{ + public interface IAppContextFactory + { + IAppContext Create(); + } +} + diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Logging/Extensions.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Logging/Extensions.cs new file mode 100644 index 0000000..e9c4c30 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Logging/Extensions.cs @@ -0,0 +1,21 @@ +using Convey; +using Convey.Logging.CQRS; +using Microsoft.Extensions.DependencyInjection; +using SwiftParcel.Services.Orders.Application.Commands; + +namespace SwiftParcel.Services.Orders.Infrastructure.Logging +{ + internal static class Extensions + { + public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + { + var assembly = typeof(CancelOrder).Assembly; + + builder.Services.AddSingleton(new MessageToLogTemplateMapper()); + + return builder + .AddCommandHandlersLogging(assembly) + .AddEventHandlersLogging(assembly); + } + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Logging/MessageToLogTemplateMapper.cs new file mode 100644 index 0000000..a9613be --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using Convey.Logging.CQRS; +using SwiftParcel.Services.Orders.Application.Commands; +using SwiftParcel.Services.Orders.Application.Exceptions; +using SwiftParcel.Services.Orders.Application.Events.External; + +namespace SwiftParcel.Services.Orders.Infrastructure.Logging +{ + internal sealed class MessageToLogTemplateMapper : IMessageToLogTemplateMapper + { + private static IReadOnlyDictionary MessageTemplates + => new Dictionary + { + { + typeof(ApproveOrder), + new HandlerLogTemplate + { + After = "Order with id: {OrderId} has been approved." + } + }, + { + typeof(CancelOrder), + new HandlerLogTemplate + { + After = "Order with id: {OrderId} has been cancelled." + } + }, + { + typeof(CreateOrder), + new HandlerLogTemplate + { + After = "Order with id: {OrderId} has been created." + } + }, + { + typeof(DeleteOrder), + new HandlerLogTemplate + { + After = "Order with id: {OrderId} has been deleted." + } + }, + { + typeof(CustomerCreated), + new HandlerLogTemplate + { + After = "Added a customer with id: {CustomerId}", + OnError = new Dictionary + { + { + typeof(CustomerAlreadyAddedException), + "Customer with id: {CustomerId} was already added." + + } + } + } + }, + { + typeof(DeliveryCompleted), + new HandlerLogTemplate + { + After = "Delivery with id: {DeliveryId} for the order with id: {OrderId} has been completed." + } + }, + { + typeof(DeliveryFailed), + new HandlerLogTemplate + { + After = "Order with id: {OrderId} has been canceled due to the failed delivery, reason: {Reason}" + } + }, + { + typeof(DeliveryPickedUp), + new HandlerLogTemplate + { + After = "Delivery for the order with id: {OrderId} has started" + } + } + }; + + public HandlerLogTemplate Map(TMessage message) where TMessage : class + { + var key = message.GetType(); + return MessageTemplates.TryGetValue(key, out var template) ? template : null; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/Documents/CustomerDocument.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/Documents/CustomerDocument.cs new file mode 100644 index 0000000..b4eee99 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/Documents/CustomerDocument.cs @@ -0,0 +1,11 @@ +using Convey.Types; + +namespace SwiftParcel.Services.Orders.Infrastructure.Mongo.Documents +{ + public class CustomerDocument : IIdentifiable + { + public Guid Id { get; set; } + public string Email { get; set; } + public string FullName { get; set; } + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/Documents/Extensions.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/Documents/Extensions.cs new file mode 100644 index 0000000..a3b4f4a --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/Documents/Extensions.cs @@ -0,0 +1,62 @@ +using System.Linq; +using SwiftParcel.Services.Orders.Application.DTO; +using SwiftParcel.Services.Orders.Core.Entities; + +namespace SwiftParcel.Services.Orders.Infrastructure.Mongo.Documents +{ + public static class Extensions + { + public static Order AsEntity(this OrderDocument document) + => new Order(document.Id, document.CustomerId, document.Status, document.OrderRequestDate, + document.BuyerName, document.BuyerEmail, document.BuyerAddress, document.Parcel); + + public static OrderDocument AsDocument(this Order entity) + => new OrderDocument + { + Id = entity.Id, + CustomerId = entity.CustomerId, + Parcel = entity.Parcel, + Status = entity.Status, + OrderRequestDate = entity.OrderRequestDate, + BuyerName = entity.BuyerName, + BuyerEmail = entity.BuyerEmail, + BuyerAddress = entity.BuyerAddress, + DecisionDate = entity.DecisionDate, + PickedUpAt = entity.PickedUpAt, + DeliveredAt = entity.DeliveredAt, + CannotDeliverAt = entity.CannotDeliverAt, + CancellationReason = entity.CancellationReason, + CannotDeliverReason = entity.CannotDeliverReason, + }; + + public static OrderDto AsDto(this OrderDocument document) + => new OrderDto + { + Id = document.Id, + CustomerId = document.CustomerId, + Parcel = new ParcelDto(document.Parcel), + Status = document.Status.ToString().ToLowerInvariant(), + OrderRequestDate = document.OrderRequestDate, + BuyerName = document.BuyerName, + BuyerEmail = document.BuyerEmail, + BuyerAddress = new AddressDto(document.BuyerAddress), + DecisionDate = document.DecisionDate, + PickedUpAt = document.PickedUpAt, + DeliveredAt = document.DeliveredAt, + CannotDeliverAt = document.CannotDeliverAt, + CancellationReason = document.CancellationReason, + CannotDeliverReason = document.CannotDeliverReason + }; + + public static Customer AsEntity(this CustomerDocument document) + => new Customer(document.Id, document.Email, document.FullName); + + public static CustomerDocument AsDocument(this Customer entity) + => new CustomerDocument + { + Id = entity.Id, + Email = entity.Email, + FullName = entity.FullName + }; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/Documents/OrderDocument.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/Documents/OrderDocument.cs new file mode 100644 index 0000000..175b704 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/Documents/OrderDocument.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using Convey.Types; +using SwiftParcel.Services.Orders.Core.Entities; + +namespace SwiftParcel.Services.Orders.Infrastructure.Mongo.Documents +{ + public class OrderDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid? CustomerId { get; set; } + public Parcel Parcel { get; set; } + public OrderStatus Status { get; set; } + public DateTime OrderRequestDate { get; set; } + public string BuyerName { get; set; } + public string BuyerEmail { get; set; } + public Address BuyerAddress { get; set; } + public DateTime? DecisionDate { get; set; } + public DateTime? PickedUpAt { get; set; } + public DateTime? DeliveredAt { get; set; } + public DateTime? CannotDeliverAt { get; set; } + public string CancellationReason { get; set; } + public string CannotDeliverReason { get; set; } + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/QueriesHandlers/GetOrderHandler.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/QueriesHandlers/GetOrderHandler.cs new file mode 100644 index 0000000..ce58b64 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/QueriesHandlers/GetOrderHandler.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Orders.Application.DTO; +using SwiftParcel.Services.Orders.Application.Queries; +using SwiftParcel.Services.Orders.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Orders.Infrastructure.Mongo.QueriesHandlers +{ + public class GetOrderHandler : IQueryHandler + { + private readonly IMongoRepository _orderRepository; + + public GetOrderHandler(IMongoRepository orderRepository) + { + _orderRepository = orderRepository; + } + + public async Task HandleAsync(GetOrder query, CancellationToken cancellationToken) + { + var document = await _orderRepository.GetAsync(p => p.Id == query.OrderId); + + return document?.AsDto(); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/QueriesHandlers/GetOrderStatusHandler.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/QueriesHandlers/GetOrderStatusHandler.cs new file mode 100644 index 0000000..f97f9ac --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/QueriesHandlers/GetOrderStatusHandler.cs @@ -0,0 +1,42 @@ +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using sib_api_v3_sdk.Model; +using SwiftParcel.Services.Orders.Application.DTO; +using SwiftParcel.Services.Orders.Application.Exceptions; +using SwiftParcel.Services.Orders.Application.Queries; +using SwiftParcel.Services.Orders.Application.Services; +using SwiftParcel.Services.Orders.Infrastructure.Mongo.Documents; + + +namespace SwiftParcel.Services.Orders.Infrastructure.Mongo.QueriesHandlers +{ + public class GetOrderStatusHandler : IQueryHandler + { + private readonly IMongoRepository _orderRepository; + private readonly IDateTimeProvider _dateTimeProvider; + + public GetOrderStatusHandler(IMongoRepository orderRepository, + IDateTimeProvider dateTimeProvider) + { + _orderRepository = orderRepository; + _dateTimeProvider = dateTimeProvider; + } + + public async Task HandleAsync(GetOrderStatus query, CancellationToken cancellationToken) + { + var document = await _orderRepository.GetAsync(p => p.Id == query.OrderId); + if(document == null) + { + throw new OrderNotFoundException(query.OrderId); + } + + var orderStatus = new OrderStatusDto + { + OrderId = document.Id, + Status = document.Status.ToString(), + TimeStamp = _dateTimeProvider.Now + }; + return orderStatus; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/QueriesHandlers/GetOrdersHandler.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/QueriesHandlers/GetOrdersHandler.cs new file mode 100644 index 0000000..8630532 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/QueriesHandlers/GetOrdersHandler.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using SwiftParcel.Services.Orders.Application; +using SwiftParcel.Services.Orders.Application.DTO; +using SwiftParcel.Services.Orders.Application.Queries; +using SwiftParcel.Services.Orders.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Orders.Infrastructure.Mongo.QueriesHandlers +{ + public class GetOrdersHandler : IQueryHandler> + { + private readonly IMongoRepository _orderRepository; + private readonly IAppContext _appContext; + + public GetOrdersHandler(IMongoRepository orderRepository, IAppContext appContext) + { + _orderRepository = orderRepository; + _appContext = appContext; + } + + public async Task> HandleAsync(GetOrders query, CancellationToken cancellationToken) + { + var documents = _orderRepository.Collection.AsQueryable(); + if (query.CustomerId.HasValue) + { + var identity = _appContext.Identity; + if (identity.IsAuthenticated && identity.Id != query.CustomerId && !identity.IsOfficeWorker) + { + return Enumerable.Empty(); + } + + documents = documents.Where(p => p.CustomerId == query.CustomerId); + } + + var orders = await documents.ToListAsync(); + + return orders.Select(p => p.AsDto()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/QueriesHandlers/GetOrdersOfficeWorkerHandler.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/QueriesHandlers/GetOrdersOfficeWorkerHandler.cs new file mode 100644 index 0000000..283a882 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/QueriesHandlers/GetOrdersOfficeWorkerHandler.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using SwiftParcel.Services.Orders.Application; +using SwiftParcel.Services.Orders.Application.DTO; +using SwiftParcel.Services.Orders.Application.Queries; +using SwiftParcel.Services.Orders.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Orders.Infrastructure.Mongo.QueriesHandlers +{ + public class GetOrdersOfficeWorkerHandler : IQueryHandler> + { + private readonly IMongoRepository _orderRepository; + private readonly IAppContext _appContext; + + public GetOrdersOfficeWorkerHandler(IMongoRepository orderRepository, IAppContext appContext) + { + _orderRepository = orderRepository; + _appContext = appContext; + } + + public async Task> HandleAsync(GetOrdersOfficeWorker query, CancellationToken cancellationToken) + { + var documents = _orderRepository.Collection.AsQueryable(); + var identity = _appContext.Identity; + if (!identity.IsOfficeWorker) + { + return Enumerable.Empty(); + } + + var orders = await documents.ToListAsync(); + + return orders.Select(p => p.AsDto()); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/Repositories/CustomerMongoRepository.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/Repositories/CustomerMongoRepository.cs new file mode 100644 index 0000000..21ccb3c --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/Repositories/CustomerMongoRepository.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading.Tasks; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Orders.Core.Entities; +using SwiftParcel.Services.Orders.Core.Repositories; +using SwiftParcel.Services.Orders.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Orders.Infrastructure.Mongo.Repositories +{ + public class CustomerMongoRepository : ICustomerRepository + { + private readonly IMongoRepository _repository; + + public CustomerMongoRepository(IMongoRepository repository) + { + _repository = repository; + } + public async Task GetAsync(Guid id) + { + var customer = await _repository.GetAsync(c => c.Id == id); + + return customer?.AsEntity(); + } + public Task ExistsAsync(Guid id) => _repository.ExistsAsync(c => c.Id == id); + public Task AddAsync(Customer customer) => _repository.AddAsync(customer.AsDocument()); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/Repositories/OrderMongoRepository.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/Repositories/OrderMongoRepository.cs new file mode 100644 index 0000000..e225901 --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Mongo/Repositories/OrderMongoRepository.cs @@ -0,0 +1,39 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Orders.Core.Entities; +using SwiftParcel.Services.Orders.Core.Repositories; +using SwiftParcel.Services.Orders.Infrastructure.Mongo.Documents; + + +namespace SwiftParcel.Services.Orders.Infrastructure.Mongo.Repositories +{ + public class OrderMongoRepository : IOrderRepository + { + private readonly IMongoRepository _repository; + + public OrderMongoRepository(IMongoRepository repository) + { + _repository = repository; + } + + public async Task GetAsync(Guid id) + { + var order = await _repository.GetAsync(o => o.Id == id); + + return order?.AsEntity(); + } + + public async Task GetContainingParcelAsync(Guid parcelId) + { + var order = await _repository.GetAsync(o => o.Parcel.Id == parcelId); + + return order?.AsEntity(); + } + + public Task AddAsync(Order order) => _repository.AddAsync(order.AsDocument()); + public Task UpdateAsync(Order order) => _repository.UpdateAsync(order.AsDocument()); + public Task DeleteAsync(Guid id) => _repository.DeleteAsync(id); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Services/Clients/ParcelsServiceClient.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Services/Clients/ParcelsServiceClient.cs new file mode 100644 index 0000000..c67d0ea --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Services/Clients/ParcelsServiceClient.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; +using Convey.HTTP; +using SwiftParcel.Services.Orders.Application.DTO; +using SwiftParcel.Services.Orders.Application.Services.Clients; + +namespace SwiftParcel.Services.Orders.Infrastructure.Services.Clients +{ + public class ParcelsServiceClient : IParcelsServiceClient + { + private readonly IHttpClient _httpClient; + private readonly string _url; + + public ParcelsServiceClient(IHttpClient httpClient, HttpClientOptions options) + { + _httpClient = httpClient; + _url = options.Services["parcels"]; + } + + public Task GetAsync(Guid id) + => _httpClient.GetAsync($"{_url}/parcels/{id}"); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Services/DateTimeProvider.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Services/DateTimeProvider.cs new file mode 100644 index 0000000..de2cc1b --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Services/DateTimeProvider.cs @@ -0,0 +1,9 @@ +using SwiftParcel.Services.Orders.Application.Services; + +namespace SwiftParcel.Services.Orders.Infrastructure.Services +{ + public class DateTimeProvider : IDateTimeProvider + { + public DateTime Now => DateTime.UtcNow; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Services/EventMapper.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Services/EventMapper.cs new file mode 100644 index 0000000..f24ecda --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Services/EventMapper.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using System.Linq; +using Convey.CQRS.Events; +using SwiftParcel.Services.Orders.Core; +using SwiftParcel.Services.Orders.Core.Entities; +using SwiftParcel.Services.Orders.Core.Events; +using SwiftParcel.Services.Orders.Application.Events; +using SwiftParcel.Services.Orders.Application.Services; + +namespace SwiftParcel.Services.Orders.Infrastructure.Services +{ + public class EventMapper : IEventMapper + { + public IEnumerable MapAll(IEnumerable events) + => events.Select(Map); + + public IEvent Map(IDomainEvent @event) + { + switch (@event) + { + case OrderStateChanged e: + switch (e.Order.Status) + { + case OrderStatus.WaitingForDecision: + return new OrderCreated(e.Order.Id); + case OrderStatus.Approved: + return new OrderApproved(e.Order.Id, e.Order.Parcel.Width, e.Order.Parcel.Height, e.Order.Parcel.Depth, + e.Order.Parcel.Weight, e.Order.Parcel.Source, e.Order.Parcel.Destination, e.Order.Parcel.Priority, + e.Order.Parcel.AtWeekend, e.Order.Parcel.PickupDate, e.Order.Parcel.DeliveryDate); + case OrderStatus.PickedUp: + return new OrderReceived(e.Order.Id); + case OrderStatus.Delivered: + return new OrderDelivered(e.Order.Id); + case OrderStatus.Cancelled: + return new OrderCancelled(e.Order.Id, e.Order.CancellationReason); + case OrderStatus.CannotDeliver: + return new OrderCouldNotBeDelivered(e.Order.Id, e.Order.CannotDeliverReason); + } + + break; + case ParcelAdded e: + return new ParcelAddedToOrder(e.Order.Id, e.Parcel.Id); + } + + return null; + } + } +} diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Services/MessageBroker.cs b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Services/MessageBroker.cs new file mode 100644 index 0000000..63d1fcd --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/Services/MessageBroker.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; +using Convey.MessageBrokers.RabbitMQ; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using OpenTracing; +using SwiftParcel.Services.Orders.Application.Services; + + +namespace SwiftParcel.Services.Orders.Infrastructure.Services +{ + internal sealed class MessageBroker : IMessageBroker + { + private const string DefaultSpanContextHeader = "span_context"; + private readonly IBusPublisher _busPublisher; + private readonly IMessageOutbox _outbox; + private readonly ICorrelationContextAccessor _contextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IMessagePropertiesAccessor _messagePropertiesAccessor; + private readonly ITracer _tracer; + private readonly ILogger _logger; + private readonly string _spanContextHeader; + + public MessageBroker(IBusPublisher busPublisher, IMessageOutbox outbox, + ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor, + IMessagePropertiesAccessor messagePropertiesAccessor, RabbitMqOptions options, ITracer tracer, + ILogger logger) + { + _busPublisher = busPublisher; + _outbox = outbox; + _contextAccessor = contextAccessor; + _httpContextAccessor = httpContextAccessor; + _messagePropertiesAccessor = messagePropertiesAccessor; + _tracer = tracer; + _logger = logger; + _spanContextHeader = string.IsNullOrWhiteSpace(options.SpanContextHeader) + ? DefaultSpanContextHeader + : options.SpanContextHeader; + } + + public Task PublishAsync(params IEvent[] events) => PublishAsync(events?.AsEnumerable()); + + public async Task PublishAsync(IEnumerable events) + { + if (events is null) + { + return; + } + + var messageProperties = _messagePropertiesAccessor.MessageProperties; + var originatedMessageId = messageProperties?.MessageId; + var correlationId = messageProperties?.CorrelationId; + var spanContext = messageProperties?.GetSpanContext(_spanContextHeader); + if (string.IsNullOrWhiteSpace(spanContext)) + { + spanContext = _tracer.ActiveSpan is null ? string.Empty : _tracer.ActiveSpan.Context.ToString(); + } + + var headers = messageProperties.GetHeadersToForward(); + var correlationContext = _contextAccessor.CorrelationContext ?? + _httpContextAccessor.GetCorrelationContext(); + + foreach (var @event in events) + { + if (@event is null) + { + continue; + } + + var messageId = Guid.NewGuid().ToString("N"); + _logger.LogTrace($"Publishing integration event: {@event.GetType().Name} [id: '{messageId}']."); + if (_outbox.Enabled) + { + await _outbox.SendAsync(@event, originatedMessageId, messageId, correlationId, spanContext, + correlationContext, headers); + continue; + } + + await _busPublisher.PublishAsync(@event, messageId, correlationId, spanContext, correlationContext, + headers); + } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure.csproj b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure.csproj new file mode 100644 index 0000000..aefcfbf --- /dev/null +++ b/SwiftParcel.Services.Orders/src/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure/SwiftParcel.Services.Orders.Infrastructure.csproj @@ -0,0 +1,44 @@ + + + + + + + + net6.0 + enable + disable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SwiftParcel.Services.OrdersCreator/SwiftParcel.Services.sln b/SwiftParcel.Services.OrdersCreator/SwiftParcel.Services.sln new file mode 100644 index 0000000..060f5f3 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/SwiftParcel.Services.sln @@ -0,0 +1,27 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5037D3FC-3C3F-48FA-8A63-8DCA8E4000E5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.OrdersCreator", "src\SwiftParcel.Services.OrdersCreator\SwiftParcel.Services.OrdersCreator.csproj", "{1BFEF09A-A712-45D4-8F27-82F64146A621}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1BFEF09A-A712-45D4-8F27-82F64146A621}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1BFEF09A-A712-45D4-8F27-82F64146A621}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1BFEF09A-A712-45D4-8F27-82F64146A621}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1BFEF09A-A712-45D4-8F27-82F64146A621}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1BFEF09A-A712-45D4-8F27-82F64146A621} = {5037D3FC-3C3F-48FA-8A63-8DCA8E4000E5} + EndGlobalSection +EndGlobal diff --git a/SwiftParcel.Services.OrdersCreator/scripts/build.sh b/SwiftParcel.Services.OrdersCreator/scripts/build.sh new file mode 100755 index 0000000..cbd4e83 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/scripts/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=Development +cd ../src/SwiftParcel.Services.OrdersCreator/SwiftParcel.Services.OrdersCreator +dotnet build -c release \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/scripts/start.sh b/SwiftParcel.Services.OrdersCreator/scripts/start.sh new file mode 100755 index 0000000..262a334 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/scripts/start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=Development +cd ../src/SwiftParcel.Services.OrdersCreator +dotnet run \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/scripts/test.sh b/SwiftParcel.Services.OrdersCreator/scripts/test.sh new file mode 100644 index 0000000..f2eeca2 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/scripts/test.sh @@ -0,0 +1,3 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=Development +dotnet test \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/AddParcelToOrder.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/AddParcelToOrder.cs new file mode 100644 index 0000000..27d1a47 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/AddParcelToOrder.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.OrdersCreator.Commands.External +{ + public class AddParcelToOrder : ICommand + { + public Guid OrderId { get; } + public Guid ParcelId { get; } + public Guid CustomerId { get; } + + public AddParcelToOrder(Guid orderId, Guid parcelId, Guid customerId) + { + OrderId = orderId; + ParcelId = parcelId; + CustomerId = customerId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/ApproveOrder.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/ApproveOrder.cs new file mode 100644 index 0000000..ecd38ff --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/ApproveOrder.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.OrdersCreator.Commands.External +{ + public class ApproveOrder: ICommand + { + public Guid OrderId { get; } + + public ApproveOrder(Guid orderId) + { + OrderId = orderId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/AssignCourierToOrder.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/AssignCourierToOrder.cs new file mode 100644 index 0000000..4b960dc --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/AssignCourierToOrder.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.OrdersCreator.Commands.External +{ + public class AssignCourierToOrder: ICommand + { + public Guid OrderId { get; } + public Guid CourierId { get; } + public DateTime DeliveryDate { get; } + + public AssignCourierToOrder(Guid orderId, Guid courierId, DateTime deliveryDate) + { + OrderId = orderId; + CourierId = courierId; + DeliveryDate = deliveryDate; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/CancelOrder.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/CancelOrder.cs new file mode 100644 index 0000000..8736e34 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/CancelOrder.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.OrdersCreator.Commands.External +{ + public class CancelOrder : ICommand + { + public Guid OrderId { get; } + public string Reason { get; } + + public CancelOrder(Guid orderId, string reason) + { + OrderId = orderId; + Reason = reason; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/CreateOrder.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/CreateOrder.cs new file mode 100644 index 0000000..bad6830 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/CreateOrder.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.OrdersCreator.Commands.External +{ + public class CreateOrder: ICommand + { + public Guid OrderId { get; } + public Guid CustomerId { get; } + + public CreateOrder(Guid id, Guid customerId) + { + OrderId = id == Guid.Empty ? Guid.NewGuid() : id; + CustomerId = customerId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/ReserveResource.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/ReserveResource.cs new file mode 100644 index 0000000..7f63578 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/External/ReserveResource.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.OrdersCreator.Commands.External +{ + public class ReserveResource : ICommand + { + public Guid ResourceId { get; } + public Guid CustomerId { get; } + public DateTime DateTime { get; } + public int Priority { get; } + + public ReserveResource(Guid resourceId, Guid customerId, DateTime dateTime, int priority) + { + ResourceId = resourceId; + DateTime = dateTime; + Priority = priority; + CustomerId = customerId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/MakeOrder.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/MakeOrder.cs new file mode 100644 index 0000000..e7946b2 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Commands/MakeOrder.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; + +namespace SwiftParcel.Services.OrdersCreator.Commands +{ + public class MakeOrder: ICommand + { + public Guid OrderId { get; } + public Guid CustomerId { get; } + public Guid ParcelId { get; } + + public MakeOrder(Guid orderId, Guid customerId, Guid parcelId) + { + OrderId = orderId == Guid.Empty ? Guid.NewGuid() : orderId; + CustomerId = customerId; + ParcelId = parcelId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/CorrelationContext.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/CorrelationContext.cs new file mode 100644 index 0000000..e5878fb --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/CorrelationContext.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.OrdersCreator +{ + public class CorrelationContext + { + public string CorrelationId { get; set; } + public string SpanContext { get; set; } + public UserContext User { get; set; } + public string ResourceId { get; set; } + public string TraceId { get; set; } + public string ConnectionId { get; set; } + public string Name { get; set; } + public DateTime CreatedAt { get; set; } + + public class UserContext + { + public string Id { get; set; } + public bool IsAuthenticated { get; set; } + public string Role { get; set; } + public IDictionary Claims { get; set; } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/DTO/CourierDto.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/DTO/CourierDto.cs new file mode 100644 index 0000000..ffa2449 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/DTO/CourierDto.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.OrdersCreator.DTO +{ + public class CourierDto + { + public Guid Id { get; set; } + //public string Brand { get; set; } + //public string Model { get; set; } + public string CourierName { get; set; } + public decimal PricePerService { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/DTO/ReservationDto.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/DTO/ReservationDto.cs new file mode 100644 index 0000000..e5da5ec --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/DTO/ReservationDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.OrdersCreator.DTO +{ + public class ReservationDto + { + public DateTime DateTime { get; set; } + public int Priority { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/DTO/ResourceDto.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/DTO/ResourceDto.cs new file mode 100644 index 0000000..20cb96d --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/DTO/ResourceDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.OrdersCreator.DTO +{ + public class ResourceDto + { + public Guid Id { get; set; } + public IEnumerable Reservations { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/External/CourierAssignedToOrder.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/External/CourierAssignedToOrder.cs new file mode 100644 index 0000000..ab9f70e --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/External/CourierAssignedToOrder.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; + +namespace SwiftParcel.Services.OrdersCreator.Events.External +{ + [Message("orders")] + public class CourierAssignedToOrder : IEvent + { + public Guid OrderId { get; } + public Guid CourierId { get; } + + public CourierAssignedToOrder(Guid orderId, Guid courierId) + { + OrderId = orderId; + CourierId = courierId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/External/OrderApproved.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/External/OrderApproved.cs new file mode 100644 index 0000000..8dceca3 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/External/OrderApproved.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; + +namespace SwiftParcel.Services.OrdersCreator.Events.External +{ + [Message("orders")] + public class OrderApproved : IEvent + { + public Guid OrderId { get; } + + public OrderApproved(Guid orderId) + { + OrderId = orderId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/External/OrderCreated.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/External/OrderCreated.cs new file mode 100644 index 0000000..62272f9 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/External/OrderCreated.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; + +namespace SwiftParcel.Services.OrdersCreator.Events.External +{ + [Message("orders")] + public class OrderCreated : IEvent + { + public Guid OrderId { get; } + + public OrderCreated(Guid orderId) + { + OrderId = orderId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/External/ParcelAddedToOrder.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/External/ParcelAddedToOrder.cs new file mode 100644 index 0000000..96f9608 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/External/ParcelAddedToOrder.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; + +namespace SwiftParcel.Services.OrdersCreator.Events.External +{ + [Message("orders")] + public class ParcelAddedToOrder : IEvent + { + public Guid OrderId { get; } + public Guid ParcelId { get; } + + public ParcelAddedToOrder(Guid orderId, Guid parcelId) + { + OrderId = orderId; + ParcelId = parcelId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/External/ResourceReserved.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/External/ResourceReserved.cs new file mode 100644 index 0000000..f54e685 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/External/ResourceReserved.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; + +namespace SwiftParcel.Services.OrdersCreator.Events.External +{ + [Message("availability")] + public class ResourceReserved : IEvent + { + public Guid ResourceId { get; } + public DateTime DateTime { get; } + + public ResourceReserved(Guid resourceId, DateTime dateTime) + { + ResourceId = resourceId; + DateTime = dateTime; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/MakeOrderCompleted.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/MakeOrderCompleted.cs new file mode 100644 index 0000000..da603ee --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/MakeOrderCompleted.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.OrdersCreator.Events +{ + public class MakeOrderCompleted : IEvent + { + public Guid OrderId { get; } + + public MakeOrderCompleted(Guid orderId) + { + OrderId = orderId; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/Rejected/MakeOrderRejected.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/Rejected/MakeOrderRejected.cs new file mode 100644 index 0000000..49d3971 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Events/Rejected/MakeOrderRejected.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.OrdersCreator.Events.Rejected +{ + public class MakeOrderRejected : IRejectedEvent + { + public Guid OrderId { get; } + public string Reason { get; } + public string Code { get; } + + public MakeOrderRejected(Guid orderId, string reason, string code) + { + OrderId = orderId; + Reason = reason; + Code = code; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Exceptions/ExceptionToResponseMapper.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Exceptions/ExceptionToResponseMapper.cs new file mode 100644 index 0000000..a5ac81d --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Exceptions/ExceptionToResponseMapper.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Convey.WebApi.Exceptions; + +namespace SwiftParcel.Services.OrdersCreator.Exceptions +{ + public class ExceptionToResponseMapper: IExceptionToResponseMapper + { + public ExceptionResponse Map(Exception exception) + => exception switch + { + _ => new ExceptionResponse(new {code = "error", reason = "There was an error."}, + HttpStatusCode.BadRequest) + }; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Extensions.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Extensions.cs new file mode 100644 index 0000000..4b5b812 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Extensions.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Chronicle; +using Convey; +using Convey.CQRS.Commands; +using Convey.CQRS.Events; +using Convey.Discovery.Consul; +using Convey.Docs.Swagger; +using Convey.HTTP; +using Convey.LoadBalancing.Fabio; +using Convey.MessageBrokers.CQRS; +using Convey.MessageBrokers.RabbitMQ; +using Convey.Metrics.AppMetrics; +using Convey.Persistence.Redis; +using Convey.Security; +using Convey.WebApi; +using Convey.WebApi.Swagger; +using SwiftParcel.Services.OrdersCreator.Events.External; +using SwiftParcel.Services.OrdersCreator.Exceptions; +using SwiftParcel.Services.OrdersCreator.Services; +using SwiftParcel.Services.OrdersCreator.Services.Clients; + +namespace SwiftParcel.Services.OrdersCreator +{ + public static class Extensions + { + public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + { + builder + .AddErrorHandler() + .AddHttpClient() + .AddConsul() + .AddFabio() + .AddCommandHandlers() + .AddEventHandlers() + .AddInMemoryCommandDispatcher() + .AddInMemoryEventDispatcher() + .AddRedis() + .AddMetrics() + .AddRabbitMq() + .AddWebApiSwaggerDocs() + .AddSecurity(); + + builder.Services.AddChronicle(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + + return builder; + } + + public static IApplicationBuilder UseApp(this IApplicationBuilder app) + { + app.UseErrorHandler() + .UseSwaggerDocs() + .UseConvey() + .UseMetrics() + .UseRabbitMq() + .SubscribeEvent() + .SubscribeEvent() + .SubscribeEvent() + .SubscribeEvent() + .SubscribeEvent(); + + return app; + } + } +} diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Handlers/AIOrderMakingHandler.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Handlers/AIOrderMakingHandler.cs new file mode 100644 index 0000000..65cefa9 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Handlers/AIOrderMakingHandler.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Chronicle; +using Convey.CQRS.Commands; +using Convey.CQRS.Events; +using SwiftParcel.Services.OrdersCreator.Commands; +using SwiftParcel.Services.OrdersCreator.Events.External; + +namespace SwiftParcel.Services.OrdersCreator.Handlers +{ + public class AIOrderMakingHandler : + ICommandHandler, + IEventHandler, + IEventHandler, + IEventHandler, + IEventHandler, + IEventHandler + { + private readonly ISagaCoordinator _coordinator; + + public AIOrderMakingHandler(ISagaCoordinator coordinator) + { + _coordinator = coordinator; + } + + public Task HandleAsync(MakeOrder command, CancellationToken cancellationToken) + => _coordinator.ProcessAsync(command, SagaContext.Empty); + + public Task HandleAsync(OrderApproved @event, CancellationToken cancellationToken) + => _coordinator.ProcessAsync(@event, SagaContext.Empty); + + public Task HandleAsync(OrderCreated @event, CancellationToken cancellationToken) + => _coordinator.ProcessAsync(@event, SagaContext.Empty); + + public Task HandleAsync(ParcelAddedToOrder @event, CancellationToken cancellationToken) + => _coordinator.ProcessAsync(@event, SagaContext.Empty); + + public Task HandleAsync(CourierAssignedToOrder @event, CancellationToken cancellationToken) + => _coordinator.ProcessAsync(@event, SagaContext.Empty); + + public Task HandleAsync(ResourceReserved @event, CancellationToken cancellationToken) + => _coordinator.ProcessAsync(@event, SagaContext.Empty); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Program.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Program.cs new file mode 100644 index 0000000..ae94598 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Program.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey; +using Convey.Logging; +using Convey.WebApi; +using Convey.WebApi.CQRS; +using Microsoft.AspNetCore; +using SwiftParcel.Services.OrdersCreator.Commands; + +namespace SwiftParcel.Services.OrdersCreator +{ + public class Program + { + public static async Task Main(string[] args) + => await WebHost.CreateDefaultBuilder(args) + .ConfigureServices(services => services + .AddConvey() + .AddWebApi() + .AddInfrastructure() + .Build()) + .Configure(app => app + .UseApp() + .UseDispatcherEndpoints(endpoints => endpoints + .Get("", ctx => ctx.Response.WriteAsync("Welcome to SwiftParcel couriers AI order maker Service!")) + .Post("orders"))) + .UseLogging() + .Build() + .RunAsync(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Properties/launchSettings.json b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Properties/launchSettings.json new file mode 100644 index 0000000..82a6b86 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:61357", + "sslPort": 44333 + } + }, + "profiles": { + "SwiftParcel.Services.OrdersCreator": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7263;http://localhost:5209", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Sagas/AIMakingOrderData.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Sagas/AIMakingOrderData.cs new file mode 100644 index 0000000..2bec855 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Sagas/AIMakingOrderData.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.OrdersCreator.Sagas +{ + public class AIMakingOrderData + { + public Guid OrderId { get; set; } + public Guid CustomerId { get; set; } + public Guid CourierId { get; set; } + public DateTime ReservationDate { get; set; } + public int ReservationPriority { get; set; } + public List ParcelIds { get; set; } = new List(); + public List AddedParcelIds { get; set; } = new List(); + public bool AllPackagesAddedToOrder => AddedParcelIds.Any() && AddedParcelIds.All(ParcelIds.Contains); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Sagas/AIOrderMakingSaga.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Sagas/AIOrderMakingSaga.cs new file mode 100644 index 0000000..34d2c92 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Sagas/AIOrderMakingSaga.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Chronicle; +using Convey.MessageBrokers; +using SwiftParcel.Services.OrdersCreator.Commands; +using SwiftParcel.Services.OrdersCreator.Commands.External; +using SwiftParcel.Services.OrdersCreator.Events; +using SwiftParcel.Services.OrdersCreator.Events.External; +using SwiftParcel.Services.OrdersCreator.Services; +using SwiftParcel.Services.OrdersCreator.Services.Clients; + +namespace SwiftParcel.Services.OrdersCreator.Sagas +{ + public class AIOrderMakingSaga: Saga, + ISagaStartAction, + ISagaAction, + ISagaAction, + ISagaAction, + ISagaAction + { + private const string SagaHeader = "Saga"; + private readonly IResourceReservationsService _resourceReservationsService; + private readonly ICouriersServiceClient _couriersServiceClient; + private readonly IBusPublisher _publisher; + private readonly ICorrelationContextAccessor _accessor; + private readonly ILogger _logger; + + public AIOrderMakingSaga(IResourceReservationsService resourceReservationsService, + ICouriersServiceClient couriersServiceClient, IBusPublisher publisher, + ICorrelationContextAccessor accessor, ILogger logger) + { + _resourceReservationsService = resourceReservationsService; + _couriersServiceClient = couriersServiceClient; + _publisher = publisher; + _accessor = accessor; + _logger = logger; + _accessor.CorrelationContext = new CorrelationContext + { + User = new CorrelationContext.UserContext() + }; + } + + public override SagaId ResolveId(object message, ISagaContext context) + => message switch + { + MakeOrder m => (SagaId) m.OrderId.ToString(), + OrderCreated m => (SagaId) m.OrderId.ToString(), + ParcelAddedToOrder m => (SagaId) m.OrderId.ToString(), + CourierAssignedToOrder m => (SagaId) m.OrderId.ToString(), + OrderApproved m => m.OrderId.ToString(), + _ => base.ResolveId(message, context) + }; + + public async Task HandleAsync(MakeOrder message, ISagaContext context) + { + _logger.LogInformation($"Started a saga for order: '{message.OrderId}'."); + Data.ParcelIds.Add(message.ParcelId); + Data.OrderId = message.OrderId; + Data.CustomerId = message.CustomerId; + + await _publisher.PublishAsync(new CreateOrder(Data.OrderId, message.CustomerId), + messageContext: _accessor.CorrelationContext, + headers: new Dictionary + { + [SagaHeader] = SagaStates.Pending.ToString() + }); + } + + public async Task HandleAsync(OrderCreated message, ISagaContext context) + { + var tasks = Data.ParcelIds.Select(id => + _publisher.PublishAsync(new AddParcelToOrder(Data.OrderId, id, Data.CustomerId), + messageContext: _accessor.CorrelationContext, + headers: new Dictionary + { + [SagaHeader] = SagaStates.Pending.ToString() + })); + + await Task.WhenAll(tasks); + } + + public async Task HandleAsync(ParcelAddedToOrder message, ISagaContext context) + { + Data.AddedParcelIds.Add(message.ParcelId); + if (!Data.AllPackagesAddedToOrder) + { + return; + } + + _logger.LogInformation("Searching for a courier..."); + var courier = await _couriersServiceClient.GetBestAsync(); + Data.CourierId = courier.Id; + _logger.LogInformation($"Found a courier with id: '{courier.Id}' for {courier.PricePerService}$."); + + _logger.LogInformation($"Reserving a date for courier: '{courier.Id}'..."); + var reservation = await _resourceReservationsService.GetBestAsync(Data.CourierId); + Data.ReservationDate = reservation.DateTime; + Data.ReservationPriority = reservation.Priority; + _logger.LogInformation($"Reserved a date: {Data.ReservationDate.Date} for courier: '{courier.Id}'."); + + await _publisher.PublishAsync( + new AssignCourierToOrder(Data.OrderId, Data.CourierId, Data.ReservationDate), + messageContext: _accessor.CorrelationContext, + headers: new Dictionary + { + [SagaHeader] = SagaStates.Pending.ToString() + }); + } + + public Task HandleAsync(CourierAssignedToOrder message, ISagaContext context) + => _publisher.PublishAsync(new ReserveResource(Data.CourierId, Data.CustomerId, + Data.ReservationDate, Data.ReservationPriority), + messageContext: _accessor.CorrelationContext, + headers: new Dictionary + { + [SagaHeader] = SagaStates.Pending.ToString() + }); + + public async Task HandleAsync(OrderApproved message, ISagaContext context) + { + _logger.LogInformation($"Completed a saga for order: '{message.OrderId}'."); + await _publisher.PublishAsync(new MakeOrderCompleted(message.OrderId), + messageContext: _accessor.CorrelationContext, + headers: new Dictionary + { + [SagaHeader] = SagaStates.Completed.ToString() + }); + + await CompleteAsync(); + } + + public Task CompensateAsync(MakeOrder message, ISagaContext context) + => Task.CompletedTask; + + public Task CompensateAsync(OrderCreated message, ISagaContext context) + => Task.CompletedTask; + + public Task CompensateAsync(ParcelAddedToOrder message, ISagaContext context) + => _publisher.PublishAsync(new CancelOrder(message.OrderId, "Because I'm saga"), + messageContext: _accessor.CorrelationContext, + headers: new Dictionary + { + [SagaHeader] = SagaStates.Rejected.ToString() + }); + + public Task CompensateAsync(CourierAssignedToOrder message, ISagaContext context) + => Task.CompletedTask; + + public Task CompensateAsync(OrderApproved message, ISagaContext context) + => Task.CompletedTask; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/Clients/AvailabilityServiceClient.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/Clients/AvailabilityServiceClient.cs new file mode 100644 index 0000000..29bbe28 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/Clients/AvailabilityServiceClient.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.HTTP; +using SwiftParcel.Services.OrdersCreator.DTO; + +namespace SwiftParcel.Services.OrdersCreator.Services.Clients +{ + public class AvailabilityServiceClient : IAvailabilityServiceClient + { + private readonly IHttpClient _httpClient; + private readonly string _url; + + public AvailabilityServiceClient(IHttpClient httpClient, HttpClientOptions options) + { + _httpClient = httpClient; + _url = options.Services["availability"]; + } + + public Task GetResourceReservationsAsync(Guid resourceId) + => _httpClient.GetAsync($"{_url}/resources/{resourceId}"); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/Clients/CouriersServiceClient.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/Clients/CouriersServiceClient.cs new file mode 100644 index 0000000..d73fefd --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/Clients/CouriersServiceClient.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using Convey.HTTP; +using SwiftParcel.Services.OrdersCreator.DTO; + +namespace SwiftParcel.Services.OrdersCreator.Services.Clients +{ + public class CouriersServiceClient : ICouriersServiceClient + { + private readonly IHttpClient _httpClient; + private readonly string _url; + + public CouriersServiceClient(IHttpClient httpClient, HttpClientOptions options) + { + _httpClient = httpClient; + _url = options.Services["couriers"]; + } + + public async Task GetBestAsync() + { + // the most potential resource of the issues to resolve in the api connection + var couriers = await _httpClient.GetAsync>($"{_url}/courier"); + var bestCourier = couriers?.Items?.FirstOrDefault(); + if (bestCourier is null) + { + throw new InvalidOperationException("The best courier was not found."); + } + + return bestCourier; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/Clients/IAvailabilityServiceClient.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/Clients/IAvailabilityServiceClient.cs new file mode 100644 index 0000000..3bab54a --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/Clients/IAvailabilityServiceClient.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.OrdersCreator.DTO; + +namespace SwiftParcel.Services.OrdersCreator.Services.Clients +{ + public interface IAvailabilityServiceClient + { + Task GetResourceReservationsAsync(Guid resourceId); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/Clients/ICouriersServiceClient.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/Clients/ICouriersServiceClient.cs new file mode 100644 index 0000000..e098bca --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/Clients/ICouriersServiceClient.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.OrdersCreator.DTO; + +namespace SwiftParcel.Services.OrdersCreator.Services.Clients +{ + public interface ICouriersServiceClient + { + Task GetBestAsync(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/IResourceReservationsService.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/IResourceReservationsService.cs new file mode 100644 index 0000000..8ba2913 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/IResourceReservationsService.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.OrdersCreator.DTO; + +namespace SwiftParcel.Services.OrdersCreator.Services +{ + public interface IResourceReservationsService + { + Task GetBestAsync(Guid resourceId); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/ResourceReservationsService.cs b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/ResourceReservationsService.cs new file mode 100644 index 0000000..4e1b106 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/Services/ResourceReservationsService.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.OrdersCreator.DTO; +using SwiftParcel.Services.OrdersCreator.Services.Clients; + +namespace SwiftParcel.Services.OrdersCreator.Services +{ + public class ResourceReservationsService : IResourceReservationsService + { + private readonly IAvailabilityServiceClient _availabilityServiceClient; + + public ResourceReservationsService(IAvailabilityServiceClient availabilityServiceClient) + { + _availabilityServiceClient = availabilityServiceClient; + } + + public async Task GetBestAsync(Guid resourceId) + { + var resource = await _availabilityServiceClient.GetResourceReservationsAsync(resourceId); + if (resource is null) + { + throw new InvalidOperationException($"Resource with id: '{resourceId}' was not found."); + } + + var latestReservation = resource.Reservations.Any() + ? resource.Reservations.OrderBy(r => r.DateTime).Last() + : null; + + if (latestReservation is null) + { + return new ReservationDto + { + DateTime = DateTime.UtcNow.AddDays(1), + Priority = 0 + }; + } + + return new ReservationDto + { + DateTime = latestReservation.DateTime.AddDays(1), + Priority = latestReservation.Priority + 1 + }; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/SwiftParcel.Services.OrdersCreator.csproj b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/SwiftParcel.Services.OrdersCreator.csproj new file mode 100644 index 0000000..644a5f5 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/SwiftParcel.Services.OrdersCreator.csproj @@ -0,0 +1,32 @@ + + + + net6.0 + disable + enable + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/appsettings.Development.json b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/appsettings.Development.json new file mode 100644 index 0000000..ff66ba6 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/appsettings.json b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/appsettings.json new file mode 100644 index 0000000..3724cf9 --- /dev/null +++ b/SwiftParcel.Services.OrdersCreator/src/SwiftParcel.Services.OrdersCreator/appsettings.json @@ -0,0 +1,144 @@ +{ + "app": { + "name": "SwiftParcel OrdersCreator Service", + "service": "orderscreator-service", + "version": "1" + }, + "consul": { + "enabled": true, + "url": "http://localhost:8500", + "service": "ordermaker-service", + "address": "localhost", + "port": "5015", + "pingEnabled": false, + "pingEndpoint": "ping", + "pingInterval": 3, + "removeAfterInterval": 3 + }, + "fabio": { + "enabled": true, + "url": "http://localhost:9999", + "service": "ordermaker-service" + }, + "httpClient": { + "type": "", + "retries": 3, + "services": { + "availability": "availability-service", + "couriers": "couriers-service" + }, + "requestMasking": { + "enabled": true, + "maskTemplate": "*****" + } + }, + "logger": { + "level": "information", + "excludePaths": ["/", "/ping", "/metrics"], + "excludeProperties": [ + "api_key", + "access_key", + "ApiKey", + "ApiSecret", + "ClientId", + "ClientSecret", + "ConnectionString", + "Password", + "Email", + "Login", + "Secret", + "Token" + ], + "console": { + "enabled": true + }, + "elk": { + "enabled": false, + "url": "http://localhost:9200" + }, + "file": { + "enabled": true, + "path": "logs/logs.txt", + "interval": "day" + }, + "seq": { + "enabled": true, + "url": "http://localhost:5341", + "apiKey": "secret" + }, + "tags": {} + }, + "jwt": { + "certificate": { + "location": "certs/localhost.cer" + }, + "validIssuer": "swiftparcel", + "validateAudience": false, + "validateIssuer": true, + "validateLifetime": true + }, + "metrics": { + "enabled": true, + "influxEnabled": false, + "prometheusEnabled": true, + "influxUrl": "http://localhost:8086", + "database": "swiftparcel", + "env": "local", + "interval": 5 + }, + "rabbitMq": { + "connectionName": "ordermaker-service", + "retries": 3, + "retryInterval": 2, + "conventionsCasing": "snakeCase", + "logger": { + "enabled": true + }, + "username": "guest", + "password": "guest", + "virtualHost": "/", + "port": 5672, + "hostnames": [ + "localhost" + ], + "requestedConnectionTimeout": "00:00:30", + "requestedHeartbeat": "00:01:00", + "socketReadTimeout": "00:00:30", + "socketWriteTimeout": "00:00:30", + "continuationTimeout": "00:00:20", + "handshakeContinuationTimeout": "00:00:10", + "networkRecoveryInterval": "00:00:05", + "exchange": { + "declare": true, + "durable": true, + "autoDelete": false, + "type": "topic", + "name": "ordermaker" + }, + "queue": { + "declare": true, + "durable": true, + "exclusive": false, + "autoDelete": false, + "template": "ordermaker-service/{{exchange}}.{{message}}" + }, + "context": { + "enabled": true, + "header": "message_context" + }, + "spanContextHeader": "span_context" + }, + "redis": { + "connectionString": "localhost", + "instance": "ordermaker:" + }, + "swagger": { + "enabled": true, + "reDocEnabled": false, + "name": "v1", + "title": "API", + "version": "v1", + "routePrefix": "docs", + "includeSecurity": true + } +} diff --git a/SwiftParcel.Services.Parcels/SwiftParcel.Services.Parcels.sln b/SwiftParcel.Services.Parcels/SwiftParcel.Services.Parcels.sln new file mode 100644 index 0000000..fc22361 --- /dev/null +++ b/SwiftParcel.Services.Parcels/SwiftParcel.Services.Parcels.sln @@ -0,0 +1,60 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{676BEB4D-578A-41D1-A70C-1B59E357237E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Parcels.Api", "SwiftParcel.Services.Parcels.Api", "{DA4A44B7-960C-4E6D-AC68-EF00868B61FB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Parcels.Api", "src\SwiftParcel.Services.Parcels.Api\SwiftParcel.Services.Parcels.Api\SwiftParcel.Services.Parcels.Api.csproj", "{142357CB-BD9F-40B8-9DDD-825D8F7A2359}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Parcels.Application", "SwiftParcel.Services.Parcels.Application", "{8377E9C0-B395-4885-BBCD-0825A86E576D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Parcels.Application", "src\SwiftParcel.Services.Parcels.Application\SwiftParcel.Services.Parcels.Application\SwiftParcel.Services.Parcels.Application.csproj", "{E3ED79DA-99CB-4720-B698-5D255840B9EF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Parcels.Core", "SwiftParcel.Services.Parcels.Core", "{355ED247-1070-4DE4-BB87-148538CA28AD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Parcels.Core", "src\SwiftParcel.Services.Parcels.Core\SwiftParcel.Services.Parcels.Core\SwiftParcel.Services.Parcels.Core.csproj", "{DF4B8924-6E29-468A-98B0-B3CC5B42FF92}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Parcels.Infrastructure", "SwiftParcel.Services.Parcels.Infrastructure", "{BE086060-031B-48E4-984A-95518F25E153}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Parcels.Infrastructure", "src\SwiftParcel.Services.Parcels.Infrastructure\SwiftParcel.Services.Parcels.Infrastructure\SwiftParcel.Services.Parcels.Infrastructure.csproj", "{098CDE85-D73E-4FBC-8349-742411248CA9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {142357CB-BD9F-40B8-9DDD-825D8F7A2359}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {142357CB-BD9F-40B8-9DDD-825D8F7A2359}.Debug|Any CPU.Build.0 = Debug|Any CPU + {142357CB-BD9F-40B8-9DDD-825D8F7A2359}.Release|Any CPU.ActiveCfg = Release|Any CPU + {142357CB-BD9F-40B8-9DDD-825D8F7A2359}.Release|Any CPU.Build.0 = Release|Any CPU + {E3ED79DA-99CB-4720-B698-5D255840B9EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E3ED79DA-99CB-4720-B698-5D255840B9EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E3ED79DA-99CB-4720-B698-5D255840B9EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E3ED79DA-99CB-4720-B698-5D255840B9EF}.Release|Any CPU.Build.0 = Release|Any CPU + {DF4B8924-6E29-468A-98B0-B3CC5B42FF92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF4B8924-6E29-468A-98B0-B3CC5B42FF92}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF4B8924-6E29-468A-98B0-B3CC5B42FF92}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF4B8924-6E29-468A-98B0-B3CC5B42FF92}.Release|Any CPU.Build.0 = Release|Any CPU + {098CDE85-D73E-4FBC-8349-742411248CA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {098CDE85-D73E-4FBC-8349-742411248CA9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {098CDE85-D73E-4FBC-8349-742411248CA9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {098CDE85-D73E-4FBC-8349-742411248CA9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {DA4A44B7-960C-4E6D-AC68-EF00868B61FB} = {676BEB4D-578A-41D1-A70C-1B59E357237E} + {142357CB-BD9F-40B8-9DDD-825D8F7A2359} = {DA4A44B7-960C-4E6D-AC68-EF00868B61FB} + {8377E9C0-B395-4885-BBCD-0825A86E576D} = {676BEB4D-578A-41D1-A70C-1B59E357237E} + {E3ED79DA-99CB-4720-B698-5D255840B9EF} = {8377E9C0-B395-4885-BBCD-0825A86E576D} + {355ED247-1070-4DE4-BB87-148538CA28AD} = {676BEB4D-578A-41D1-A70C-1B59E357237E} + {DF4B8924-6E29-468A-98B0-B3CC5B42FF92} = {355ED247-1070-4DE4-BB87-148538CA28AD} + {BE086060-031B-48E4-984A-95518F25E153} = {676BEB4D-578A-41D1-A70C-1B59E357237E} + {098CDE85-D73E-4FBC-8349-742411248CA9} = {BE086060-031B-48E4-984A-95518F25E153} + EndGlobalSection +EndGlobal diff --git a/SwiftParcel.Services.Parcels/scripts/build.sh b/SwiftParcel.Services.Parcels/scripts/build.sh new file mode 100755 index 0000000..3affad0 --- /dev/null +++ b/SwiftParcel.Services.Parcels/scripts/build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +dotnet build -c release \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/scripts/start.sh b/SwiftParcel.Services.Parcels/scripts/start.sh new file mode 100755 index 0000000..fcc4f3b --- /dev/null +++ b/SwiftParcel.Services.Parcels/scripts/start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=local +cd ../src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api +dotnet run \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/scripts/test.sh b/SwiftParcel.Services.Parcels/scripts/test.sh new file mode 100644 index 0000000..6046c35 --- /dev/null +++ b/SwiftParcel.Services.Parcels/scripts/test.sh @@ -0,0 +1,2 @@ +#!/bin/bash +dotnet test \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api/Program.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api/Program.cs new file mode 100644 index 0000000..23c29c8 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api/Program.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Convey; +using Convey.Secrets.Vault; +using Convey.Logging; +using Convey.Types; +using Convey.WebApi; +using Convey.WebApi.CQRS; +using SwiftParcel.Services.Parcels.Application; +using SwiftParcel.Services.Parcels.Application.Queries; +using SwiftParcel.Services.Parcels.Application.DTO; +using SwiftParcel.Services.Parcels.Application.Commands; +using SwiftParcel.Services.Parcels.Infrastructure; +using Convey.Docs.Swagger; + +namespace SwiftParcel.Services.Parcels.Api +{ + public class Program + { + public static async Task Main(string[] args) + => await WebHost.CreateDefaultBuilder(args) + .ConfigureServices(services => services + .AddConvey() + .AddWebApi() + .AddApplication() + .AddInfrastructure() + .Build()) + .Configure(app => app + .UseSwagger() + .UseSwaggerUI() + .UseInfrastructure() + .UseDispatcherEndpoints(endpoints => endpoints + .Get("", ctx => ctx.Response.WriteAsync(ctx.RequestServices.GetService().Name)) + .Get>("parcels") + .Get>("parcels/office-worker") + .Get("parcels/{parcelId}") + .Get("parcels/{parcelId}/expiration-status") + .Post("parcels", + afterDispatch: (cmd, ctx) => ctx.Response.Created($"parcels/{cmd.ParcelId}/expiration-status")) + .Delete("parcels/{parcelId}") + )) + .UseLogging() + .UseVault() + .Build() + .RunAsync(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api/Properties/launchSettings.json b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api/Properties/launchSettings.json new file mode 100644 index 0000000..d9cf90b --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api/Properties/launchSettings.json @@ -0,0 +1,28 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:6007", + "sslPort": 44345 + } + }, + "profiles": { + "SwiftParcel.Services.Parcels": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5007", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api.csproj b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api.csproj new file mode 100644 index 0000000..2f33f62 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api.csproj @@ -0,0 +1,20 @@ + + + + net6.0 + disable + enable + + + + + + + + + + + + + + diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api/appsettings.Development.json b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api/appsettings.Development.json new file mode 100644 index 0000000..1b2d3ba --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api/appsettings.json b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api/appsettings.json new file mode 100644 index 0000000..d015f34 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Api/SwiftParcel.Services.Parcels.Api/appsettings.json @@ -0,0 +1,196 @@ +{ + "app": { + "name": "SwiftParcel Parcels Service", + "service": "parcels-service", + "version": "1" + }, + "consul": { + "enabled": true, + "url": "http://localhost:8500", + "service": "parcels-service", + "address": "localhost", + "port": "5007", + "pingEnabled": false, + "pingEndpoint": "ping", + "pingInterval": 3, + "removeAfterInterval": 3 + }, + "fabio": { + "enabled": true, + "url": "http://localhost:9999", + "service": "parcels-service" + }, + "httpClient": { + "type": "fabio", + "retries": 3, + "services": { + "pricing": "pricing-service" + }, + "requestMasking": { + "enabled": true, + "maskTemplate": "*****" + } + }, + "logger": { + "level": "information", + "excludePaths": [ "/", "/ping", "/metrics" ], + "excludeProperties": [ + "api_key", + "access_key", + "ApiKey", + "ApiSecret", + "ClientId", + "ClientSecret", + "ConnectionString", + "Password", + "Email", + "Login", + "Secret", + "Token" + ], + "console": { + "enabled": true + }, + "elk": { + "enabled": false, + "url": "http://localhost:9200" + }, + "file": { + "enabled": true, + "path": "logs/logs.txt", + "interval": "day" + }, + "seq": { + "enabled": true, + "url": "http://localhost:5341", + "apiKey": "secret" + }, + "tags": {} + }, + "jaeger": { + "enabled": true, + "serviceName": "parcels", + "udpHost": "localhost", + "udpPort": 6831, + "maxPacketSize": 0, + "sampler": "const", + "excludePaths": [ "/", "/ping", "/metrics" ] + }, + "jwt": { + "certificate": { + "location": "certs/localhost.cer" + }, + "validIssuer": "swiftparcel", + "validateAudience": false, + "validateIssuer": true, + "validateLifetime": true + }, + "metrics": { + "enabled": true, + "influxEnabled": false, + "prometheusEnabled": true, + "influxUrl": "http://localhost:8086", + "database": "swiftparcel", + "env": "local", + "interval": 5 + }, + "mongo": { + "connectionString": "mongodb+srv://andrii-courier-db-user:piotr-amadeusz-andrii-2023@cluster0.br51nsv.mongodb.net/?retryWrites=true&w=majority", + "database": "parcels-service", + "seed": false + }, + "outbox": { + "enabled": true, + "type": "sequential", + "expiry": 3600, + "intervalMilliseconds": 2000, + "inboxCollection": "inbox", + "outboxCollection": "outbox", + "disableTransactions": true + }, + "rabbitMq": { + "connectionName": "parcels-service", + "retries": 3, + "retryInterval": 2, + "conventionsCasing": "snakeCase", + "logger": { + "enabled": true + }, + "username": "guest", + "password": "guest", + "virtualHost": "/", + "port": 5672, + "hostnames": [ + "localhost" + ], + "requestedConnectionTimeout": "00:00:30", + "requestedHeartbeat": "00:01:00", + "socketReadTimeout": "00:00:30", + "socketWriteTimeout": "00:00:30", + "continuationTimeout": "00:00:20", + "handshakeContinuationTimeout": "00:00:10", + "networkRecoveryInterval": "00:00:05", + "exchange": { + "declare": true, + "durable": true, + "autoDelete": false, + "type": "topic", + "name": "parcels" + }, + "queue": { + "declare": true, + "durable": true, + "exclusive": false, + "autoDelete": false, + "template": "parcels-service/{{exchange}}.{{message}}" + }, + "context": { + "enabled": true, + "header": "message_context" + }, + "spanContextHeader": "span_context" + }, + "redis": { + "connectionString": "localhost", + "instance": "parcels:" + }, + "swagger": { + "enabled": true, + "reDocEnabled": false, + "name": "v1", + "title": "API", + "version": "v1", + "routePrefix": "docs", + "includeSecurity": true + }, + "vault": { + "enabled": false, + "url": "http://localhost:8200", + "authType": "token", + "token": "secret", + "username": "user", + "password": "secret", + "kv": { + "enabled": true, + "engineVersion": 2, + "mountPoint": "kv", + "path": "parcels-service/settings" + }, + "pki": { + "enabled": true, + "roleName": "parcels-service", + "commonName": "parcels-service.swiftparcel.io" + }, + "lease": { + "mongo": { + "type": "database", + "roleName": "orders-service", + "enabled": true, + "autoRenewal": true, + "templates": { + "connectionString": "mongodb://{{username}}:{{password}}@localhost:27017" + } + } + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Commands/AddParcel.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Commands/AddParcel.cs new file mode 100644 index 0000000..55970c6 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Commands/AddParcel.cs @@ -0,0 +1,78 @@ +using Convey.CQRS.Commands; +using SwiftParcel.Services.Parcels.Core.Entities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.Commands +{ + public class AddParcel : ICommand + { + public Guid ParcelId { get; protected set; } + public Guid CustomerId { get; protected set; } + public string Description { get; protected set; } + public double Width { get; protected set; } + public double Height { get; protected set; } + public double Depth { get; protected set; } + public double Weight { get; protected set; } + public string SourceStreet { get; protected set; } + public string SourceBuildingNumber { get; protected set; } + public string SourceApartmentNumber { get; protected set; } + public string SourceCity { get; protected set; } + public string SourceZipCode { get; protected set; } + public string SourceCountry { get; protected set; } + public string DestinationStreet { get; protected set; } + public string DestinationBuildingNumber { get; protected set; } + public string DestinationApartmentNumber { get; protected set; } + public string DestinationCity { get; protected set; } + public string DestinationZipCode { get; protected set; } + public string DestinationCountry { get; protected set; } + public string Priority { get; protected set; } + public bool AtWeekend { get; protected set; } + public string PickupDate { get; protected set; } + public string DeliveryDate { get; protected set; } + public bool IsCompany { get; protected set; } + public bool VipPackage { get; protected set; } + + public AddParcel(Guid parcelId, Guid customerId, string description, + double width, double height, double depth, double weight, + string sourceStreet, string sourceBuildingNumber, + string sourceApartmentNumber, string sourceCity, string sourceZipCode, + string sourceCountry, string destinationStreet, string destinationBuildingNumber, + string destinationApartmentNumber, string destinationCity, string destinationZipCode, + string destinationCountry, string priority, bool atWeekend, string pickupDate, + string deliveryDate, bool isCompany, bool vipPackage) + { + ParcelId = parcelId == Guid.Empty ? Guid.NewGuid() : parcelId; + CustomerId = customerId; + Description = description; + Width = width; + Height = height; + Depth = depth; + Weight = weight; + + SourceStreet = sourceStreet; + SourceBuildingNumber = sourceBuildingNumber; + SourceApartmentNumber = sourceApartmentNumber; + SourceCity = sourceCity; + SourceZipCode = sourceZipCode; + SourceCountry = sourceCountry; + + DestinationStreet = destinationStreet; + DestinationBuildingNumber = destinationBuildingNumber; + DestinationApartmentNumber = destinationApartmentNumber; + DestinationCity = destinationCity; + DestinationZipCode = destinationZipCode; + DestinationCountry = destinationCountry; + + Priority = priority; + AtWeekend = atWeekend; + PickupDate = pickupDate; + DeliveryDate = deliveryDate; + IsCompany = isCompany; + VipPackage = vipPackage; + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Commands/DeleteParcel.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Commands/DeleteParcel.cs new file mode 100644 index 0000000..ecc014a --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Commands/DeleteParcel.cs @@ -0,0 +1,16 @@ +using Convey.CQRS.Commands; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.Commands +{ + public class DeleteParcel : ICommand + { + public Guid ParcelId { get; } + + public DeleteParcel(Guid parcelId) => ParcelId = parcelId; + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Commands/Handlers/AddParcelHandler.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Commands/Handlers/AddParcelHandler.cs new file mode 100644 index 0000000..7a58eef --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Commands/Handlers/AddParcelHandler.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Globalization; +using Convey.CQRS.Commands; +using SwiftParcel.Services.Parcels.Application.Events; +using SwiftParcel.Services.Parcels.Application.Exceptions; +using SwiftParcel.Services.Parcels.Application.Services; +using SwiftParcel.Services.Parcels.Core.Entities; +using SwiftParcel.Services.Parcels.Core.Repositories; +using SwiftParcel.Services.Parcels.Core.Exceptions; +using SwiftParcel.Services.Parcels.Application.Services.Clients; + +namespace SwiftParcel.Services.Parcels.Application.Commands.Handlers +{ + public class AddParcelHandler : ICommandHandler + { + private readonly IParcelRepository _parcelRepository; + private readonly ICustomerRepository _customerRepository; + private readonly IPricingServiceClient _pricingServiceClient; + private readonly IDateTimeProvider _dateTimeProvider; + private readonly IMessageBroker _messageBroker; + private readonly string _expectedFormat = "yyyy-MM-ddTHH:mm:ss.fffZ"; + + + public AddParcelHandler(IParcelRepository parcelRepository, ICustomerRepository customerRepository, + IPricingServiceClient pricingServiceClient, IDateTimeProvider dateTimeProvider, IMessageBroker messageBroker) + { + _parcelRepository = parcelRepository; + _customerRepository = customerRepository; + _pricingServiceClient = pricingServiceClient; + _dateTimeProvider = dateTimeProvider; + _messageBroker = messageBroker; + } + + public async Task HandleAsync(AddParcel command, CancellationToken cancellationToken = default) + { + Guid? customerId = command.CustomerId == Guid.Empty ? null : command.CustomerId; + if (customerId != null && !await _customerRepository.ExistsAsync(command.CustomerId)) + { + throw new CustomerNotFoundException(command.CustomerId); + } + if (!Enum.TryParse(command.Priority, true, out var priority)) + { + throw new InvalidParcelPriorityException(command.Priority); + } + if (!DateTime.TryParseExact(command.PickupDate, _expectedFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime pickupDate)) + { + throw new InvalidParcelDateTimeException("pickup_date",command.PickupDate); + } + if (!DateTime.TryParseExact(command.DeliveryDate, _expectedFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime deliveryDate)) + { + throw new InvalidParcelDateTimeException("delivery_date", command.DeliveryDate); + } + var createdAt = _dateTimeProvider.Now; + var validTo = createdAt.AddMinutes(30); + var price = await _pricingServiceClient.GetParcelDeliveryPriceAsync(customerId ?? command.CustomerId, 0.0m, + command.Width, command.Height, command.Depth, command.Weight, priority == Priority.High, command.AtWeekend); + if (price == null) + { + throw new PricingServiceException(command.ParcelId); + } + + var parcel = new Parcel(command.ParcelId, command.Description, command.Width, + command.Height, command.Depth, command.Weight, pickupDate, deliveryDate, + createdAt, price.OrderPrice, validTo, customerId); + + parcel.SetSourceAddress(command.SourceStreet, command.SourceBuildingNumber, + command.SourceApartmentNumber, command.SourceCity, command.SourceZipCode, + command.SourceCountry); + parcel.SetDestinationAddress(command.DestinationStreet, command.DestinationBuildingNumber, + command.DestinationApartmentNumber, command.DestinationCity, command.DestinationZipCode, + command.DestinationCountry); + + parcel.SetPriority(priority); + + parcel.SetAtWeekend(command.AtWeekend); + + await _parcelRepository.AddAsync(parcel); + + await _messageBroker.PublishAsync(new ParcelAdded(command.ParcelId)); + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Commands/Handlers/DeleteParcelHandler.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Commands/Handlers/DeleteParcelHandler.cs new file mode 100644 index 0000000..f0fed12 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Commands/Handlers/DeleteParcelHandler.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using SwiftParcel.Services.Parcels.Application.Events; +using SwiftParcel.Services.Parcels.Application.Exceptions; +using SwiftParcel.Services.Parcels.Application.Services; +using SwiftParcel.Services.Parcels.Core.Repositories; + +namespace SwiftParcel.Services.Parcels.Application.Commands.Handlers +{ + public class DeleteParcelHandler : ICommandHandler + { + private readonly IParcelRepository _parcelRepository; + private readonly IAppContext _appContext; + private readonly IMessageBroker _messageBroker; + + public DeleteParcelHandler(IParcelRepository parcelRepository, IAppContext appContext, IMessageBroker messageBroker) + { + _parcelRepository = parcelRepository; + _appContext = appContext; + _messageBroker = messageBroker; + } + + public async Task HandleAsync(DeleteParcel command, CancellationToken cancellationToken = default) + { + var parcel = await _parcelRepository.GetAsync(command.ParcelId); + + if (parcel is null) + { + throw new ParcelNotFoundException(command.ParcelId); + } + + var identity = _appContext.Identity; + if (identity.IsAuthenticated && identity.Id != parcel.CustomerId && !identity.IsOfficeWorker) + { + throw new UnauthorizedParcelAccessException(command.ParcelId, identity.Id); + } + + await _parcelRepository.DeleteAsync(command.ParcelId); + + await _messageBroker.PublishAsync(new ParcelDeleted(command.ParcelId)); + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/ContractAttribute.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/ContractAttribute.cs new file mode 100644 index 0000000..798cc5f --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/ContractAttribute.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application +{ + public class ContractAttribute: Attribute + { + + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/DTO/AddressDto.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/DTO/AddressDto.cs new file mode 100644 index 0000000..6f501bc --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/DTO/AddressDto.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.DTO +{ + public class AddressDto + { + public string Street { get; set; } + public string BuildingNumber { get; set; } + public string ApartmentNumber { get; set; } + public string City { get; set; } + public string ZipCode { get; set; } + public string Country { get; set; } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/DTO/ExpirationStatusDto.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/DTO/ExpirationStatusDto.cs new file mode 100644 index 0000000..b3920b8 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/DTO/ExpirationStatusDto.cs @@ -0,0 +1,9 @@ +namespace SwiftParcel.Services.Parcels.Application.DTO +{ + public class ExpirationStatusDto + { + public Guid ParcelId { get; set; } + public decimal TotalPrice { get; set; } + public DateTime ExpiringAt { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/DTO/ParcelDeliveryPricingDto.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/DTO/ParcelDeliveryPricingDto.cs new file mode 100644 index 0000000..ccc1ad6 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/DTO/ParcelDeliveryPricingDto.cs @@ -0,0 +1,14 @@ +namespace SwiftParcel.Services.Parcels.Application.DTO +{ + public class ParcelDeliveryPricingDto + { + public Guid CustomerId { get; set; } + public decimal OrderPrice { get; set; } + public double Length { get; set; } + public double Width { get; set; } + public double Height { get; set; } + public double Weight { get; set; } + public bool HighPriority { get; set; } + public bool DeliverAtWeekend { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/DTO/ParcelDto.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/DTO/ParcelDto.cs new file mode 100644 index 0000000..e34564e --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/DTO/ParcelDto.cs @@ -0,0 +1,31 @@ +using SwiftParcel.Services.Parcels.Core.Entities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.DTO +{ + public class ParcelDto + { + public Guid Id { get; set; } + public Guid? CustomerId { get; set; } + public string Description { get; set; } + public double Width { get; set; } + public double Height { get; set; } + public double Depth { get; set; } + public double Weight { get; set; } + public AddressDto Source { get; set; } + public AddressDto Destination { get; set; } + public string Priority { get; set; } + public bool AtWeekend { get; set; } + public DateTime PickupDate { get; set; } + public DateTime DeliveryDate { get; set; } + public bool IsCompany { get; set; } + public bool VipPackage { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime ValidTo { get; set; } + public decimal CalculatedPrice { get; set; } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/External/CustomerCreated.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/External/CustomerCreated.cs new file mode 100644 index 0000000..d82fe19 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/External/CustomerCreated.cs @@ -0,0 +1,21 @@ +using Convey.CQRS.Events; +using Convey.MessageBrokers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.Events.External +{ + [Message("customers")] + public class CustomerCreated : IEvent + { + public Guid CustomerId { get; set; } + + public CustomerCreated(Guid customerId) + { + CustomerId = customerId; + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/External/Handlers/CustomerCreatedHandler.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/External/Handlers/CustomerCreatedHandler.cs new file mode 100644 index 0000000..7fbd43b --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/External/Handlers/CustomerCreatedHandler.cs @@ -0,0 +1,32 @@ +using Convey.CQRS.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SwiftParcel.Services.Parcels.Application.Exceptions; +using SwiftParcel.Services.Parcels.Core.Entities; +using SwiftParcel.Services.Parcels.Core.Repositories; + +namespace SwiftParcel.Services.Parcels.Application.Events.External.Handlers +{ + public class CustomerCreatedHandler : IEventHandler + { + private readonly ICustomerRepository _customerRepository; + + public CustomerCreatedHandler(ICustomerRepository customerRepository) + { + _customerRepository = customerRepository; + } + + public async Task HandleAsync(CustomerCreated @event, CancellationToken cancellationToken) + { + if (await _customerRepository.ExistsAsync(@event.CustomerId)) + { + throw new CustomerAlreadyAddedException(@event.CustomerId); + } + + await _customerRepository.AddAsync(new Customer(@event.CustomerId)); + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/ParcelAdded.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/ParcelAdded.cs new file mode 100644 index 0000000..9dc9a02 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/ParcelAdded.cs @@ -0,0 +1,18 @@ +using Convey.CQRS.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.Events +{ + public class ParcelAdded : IEvent + { + public Guid ParcelId { get; } + public ParcelAdded(Guid parcelId) + { + ParcelId = parcelId; + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/ParcelDeleted.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/ParcelDeleted.cs new file mode 100644 index 0000000..893cb3d --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/ParcelDeleted.cs @@ -0,0 +1,18 @@ +using Convey.CQRS.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.Events +{ + public class ParcelDeleted : IEvent + { + public Guid ParcelId { get; } + public ParcelDeleted(Guid parcelId) + { + ParcelId = parcelId; + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/Rejected/AddParcelRejected.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/Rejected/AddParcelRejected.cs new file mode 100644 index 0000000..06e05a5 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/Rejected/AddParcelRejected.cs @@ -0,0 +1,23 @@ +using Convey.CQRS.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.Events.Rejected +{ + public class AddParcelRejected : IRejectedEvent + { + public Guid ParcelId { get; } + public string Reason { get; } + public string Code { get; } + + public AddParcelRejected(Guid parcelId, string reason, string code) + { + ParcelId = parcelId; + Reason = reason; + Code = code; + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/Rejected/DeleteParcelRejected.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/Rejected/DeleteParcelRejected.cs new file mode 100644 index 0000000..6f5de15 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Events/Rejected/DeleteParcelRejected.cs @@ -0,0 +1,23 @@ +using Convey.CQRS.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.Events.Rejected +{ + public class DeleteParcelRejected : IRejectedEvent + { + public Guid ParcelId { get; } + public string Reason { get; } + public string Code { get; } + + public DeleteParcelRejected(Guid parcelId, string reason, string code) + { + ParcelId = parcelId; + Reason = reason; + Code = code; + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/AppException.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/AppException.cs new file mode 100644 index 0000000..179d756 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/AppException.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.Exceptions +{ + public class AppException : Exception + { + public virtual string Code { get; } + + protected AppException(string message) : base(message) + { + + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/CannotDeleteParcelException.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/CannotDeleteParcelException.cs new file mode 100644 index 0000000..c03a511 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/CannotDeleteParcelException.cs @@ -0,0 +1,20 @@ +using SwiftParcel.Services.Parcels.Core.Exceptions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.Exceptions +{ + public class CannotDeleteParcelException : AppException + { + public override string Code { get; } = "cannot_delete_parcel"; + public Guid Id { get; } + + public CannotDeleteParcelException(Guid id) : base($"Parcel with id: '{id}' cannot be deleted.") + { + Id = id; + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/CustomerAlreadyAddedException.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/CustomerAlreadyAddedException.cs new file mode 100644 index 0000000..c6b6235 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/CustomerAlreadyAddedException.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.Exceptions +{ + public class CustomerAlreadyAddedException : AppException + { + public override string Code { get; } = "customer_already_added"; + public Guid Id { get; } + + public CustomerAlreadyAddedException(Guid id) : base($"Customer with id: {id} has been already added.") + { + Id = id; + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/CustomerNotFoundException.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/CustomerNotFoundException.cs new file mode 100644 index 0000000..5e3b796 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/CustomerNotFoundException.cs @@ -0,0 +1,20 @@ +using SwiftParcel.Services.Parcels.Core.Exceptions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.Exceptions +{ + public class CustomerNotFoundException : AppException + { + public override string Code { get; } = "customer_not_found"; + public Guid Id { get; } + + public CustomerNotFoundException(Guid id) : base($"Customer with id: {id} was not found.") + { + Id = id; + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/InvalidParcelPriorityException.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/InvalidParcelPriorityException.cs new file mode 100644 index 0000000..3f6861d --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/InvalidParcelPriorityException.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.Exceptions +{ + public class InvalidParcelPriorityException : AppException + { + public override string Code { get; } = "invalid_parcel_priority"; + public string Priority { get; } + + public InvalidParcelPriorityException(string priority) : base($"Invalid parcel priority: {priority}") + { + Priority = priority; + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/ParcelNotFoundException.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/ParcelNotFoundException.cs new file mode 100644 index 0000000..52f2822 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/ParcelNotFoundException.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.Exceptions +{ + public class ParcelNotFoundException : AppException + { + public override string Code { get; } = "parcel_not_found"; + public Guid Id { get; } + + public ParcelNotFoundException(Guid id) : base($"Parcel with id: {id} was not found.") + { + Id = id; + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/PricingServiceException.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/PricingServiceException.cs new file mode 100644 index 0000000..9f499ed --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/PricingServiceException.cs @@ -0,0 +1,13 @@ +namespace SwiftParcel.Services.Parcels.Application.Exceptions +{ + public class PricingServiceException : AppException + { + public override string Code { get; } = "pricing_service_error"; + public Guid ParcelId { get; } + + public PricingServiceException(Guid parcelId) : base($"Pricing service error occured for parcel with id: {parcelId}.") + { + ParcelId = parcelId; + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/UnauthorizedParcelAccessException.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/UnauthorizedParcelAccessException.cs new file mode 100644 index 0000000..a0dac16 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Exceptions/UnauthorizedParcelAccessException.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.Exceptions +{ + public class UnauthorizedParcelAccessException : AppException + { + public override string Code { get; } = "unauthorized_parcel_access"; + public Guid ParcelId { get; } + public Guid CustomerId { get; } + + public UnauthorizedParcelAccessException(Guid parcelId, Guid customerId) + : base($"Unauthorized access to parcel with id: '{parcelId}' by customer with id: '{customerId}'.") + { + ParcelId = parcelId; + CustomerId = customerId; + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Extensions.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Extensions.cs new file mode 100644 index 0000000..239f874 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Extensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey; +using Convey.CQRS.Commands; +using Convey.CQRS.Events; +using Microsoft.Extensions.DependencyInjection; +using SwiftParcel.Services.Parcels.Core; + +namespace SwiftParcel.Services.Parcels.Application +{ + public static class Extensions + { + public static IConveyBuilder AddApplication(this IConveyBuilder builder) + { + builder + .AddCommandHandlers() + .AddEventHandlers() + .AddInMemoryCommandDispatcher() + .AddInMemoryEventDispatcher(); + + return builder; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/IAppContext.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/IAppContext.cs new file mode 100644 index 0000000..f61fde9 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/IAppContext.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application +{ + public interface IAppContext + { + string RequestId { get; } + IIdentityContext Identity { get; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/IIdentityContext.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/IIdentityContext.cs new file mode 100644 index 0000000..2274a3d --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/IIdentityContext.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application +{ + public interface IIdentityContext + { + Guid Id { get; } + string Role { get; } + bool IsAuthenticated { get; } + bool IsOfficeWorker { get; } + bool IsCourier { get; } + IDictionary Claims { get; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Queries/GetParcel.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Queries/GetParcel.cs new file mode 100644 index 0000000..84e9018 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Queries/GetParcel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using SwiftParcel.Services.Parcels.Application.DTO; + +namespace SwiftParcel.Services.Parcels.Application.Queries +{ + public class GetParcel : IQuery + { + public Guid ParcelId { get; set; } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Queries/GetParcelExpirationStatus.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Queries/GetParcelExpirationStatus.cs new file mode 100644 index 0000000..ca227c2 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Queries/GetParcelExpirationStatus.cs @@ -0,0 +1,10 @@ +using Convey.CQRS.Queries; +using SwiftParcel.Services.Parcels.Application.DTO; + +namespace SwiftParcel.Services.Parcels.Application.Queries +{ + public class GetParcelExpirationStatus : IQuery + { + public Guid ParcelId { get; set; } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Queries/GetParcels.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Queries/GetParcels.cs new file mode 100644 index 0000000..a73597b --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Queries/GetParcels.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using SwiftParcel.Services.Parcels.Application.DTO; + +namespace SwiftParcel.Services.Parcels.Application.Queries +{ + public class GetParcels : IQuery> + { + public Guid? CustomerId { get; set; } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Queries/GetParcelsOfficeWorker.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Queries/GetParcelsOfficeWorker.cs new file mode 100644 index 0000000..007276e --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Queries/GetParcelsOfficeWorker.cs @@ -0,0 +1,10 @@ +using Convey.CQRS.Queries; +using SwiftParcel.Services.Parcels.Application.DTO; + +namespace SwiftParcel.Services.Parcels.Application.Queries +{ + public class GetParcelsOfficeWorker : IQuery> + { + public Guid? CustomerId { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Services/Clients/IPricingServiceClient.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Services/Clients/IPricingServiceClient.cs new file mode 100644 index 0000000..d432c10 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Services/Clients/IPricingServiceClient.cs @@ -0,0 +1,10 @@ +using SwiftParcel.Services.Parcels.Application.DTO; + +namespace SwiftParcel.Services.Parcels.Application.Services.Clients +{ + public interface IPricingServiceClient + { + Task GetParcelDeliveryPriceAsync(Guid parcelId, decimal calculatedPrice, + double length, double width, double height, double weight, bool highPriority, bool deliverAtWeekend); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Services/IDateTimeProvider.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Services/IDateTimeProvider.cs new file mode 100644 index 0000000..dbe66c3 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Services/IDateTimeProvider.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Application.Services +{ + public interface IDateTimeProvider + { + DateTime Now { get; } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Services/IMessageBroker.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Services/IMessageBroker.cs new file mode 100644 index 0000000..874fd29 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/Services/IMessageBroker.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; + +namespace SwiftParcel.Services.Parcels.Application.Services +{ + public interface IMessageBroker + { + Task PublishAsync(params IEvent[] events); + Task PublishAsync(IEnumerable events); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application.csproj b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application.csproj new file mode 100644 index 0000000..b79778e --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application/SwiftParcel.Services.Parcels.Application.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + enable + disable + + + + + + + + + + + + + + + diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/Address.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/Address.cs new file mode 100644 index 0000000..2c07b27 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/Address.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Core.Entities +{ + public class Address + { + public string Street { get; set; } + public string BuildingNumber { get; set; } + public string ApartmentNumber { get; set; } + public string City { get; set; } + public string ZipCode { get; set; } + public string Country { get; set;} + + public Address() + { + Street = string.Empty; + BuildingNumber = string.Empty; + ApartmentNumber = string.Empty; + City = string.Empty; + ZipCode = string.Empty; + Country = string.Empty; + } + + public Address(string street, string buildingNumber, string apartmentNumber, string city, string zipCode, string country) + { + Street = street; + BuildingNumber = buildingNumber; + ApartmentNumber = apartmentNumber; + City = city; + ZipCode = zipCode; + Country = country; + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/AggregateId.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/AggregateId.cs new file mode 100644 index 0000000..d506c63 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/AggregateId.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Parcels.Core.Exceptions; + +namespace SwiftParcel.Services.Parcels.Core.Entities +{ + public class AggregateId: IEquatable + { + public Guid Value { get; } + + public AggregateId() + { + Value = Guid.NewGuid(); + } + + public AggregateId(Guid value) + { + if (value == Guid.Empty) + { + throw new InvalidAggregateIdException(); + } + + Value = value; + } + + public bool Equals(AggregateId? other) + { + if (ReferenceEquals(null, other)) return false; + return ReferenceEquals(this, other) || Value.Equals(other.Value); + } + + public override bool Equals(object? obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((AggregateId) obj); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public static implicit operator Guid(AggregateId id) + => id.Value; + + public static implicit operator AggregateId(Guid id) + => new AggregateId(id); + + public override string ToString() => Value.ToString(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/AggregateRoot.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/AggregateRoot.cs new file mode 100644 index 0000000..d83114c --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/AggregateRoot.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Core.Entities +{ + public class AggregateRoot + { + private readonly List _events = new List(); + public IEnumerable Events => _events; + public AggregateId Id { get; protected set; } = new AggregateId(); // Initialize Id here + public int Version { get; protected set; } + + protected void AddEvent(IDomainEvent @event) + { + _events.Add(@event); + } + + public void ClearEvents() => _events.Clear(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/Customer.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/Customer.cs new file mode 100644 index 0000000..a1d309f --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/Customer.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Core.Entities +{ + public class Customer + { + public Guid Id { get; private set; } + + public Customer(Guid id) + { + Id = id; + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/Parcel.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/Parcel.cs new file mode 100644 index 0000000..e3060f2 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/Parcel.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Text.RegularExpressions; +using System.Globalization; +using System.Threading.Tasks; +using SwiftParcel.Services.Parcels.Core.Exceptions; + +namespace SwiftParcel.Services.Parcels.Core.Entities +{ + public class Parcel + { + public Guid Id { get; protected set; } + public Guid? CustomerId { get; protected set; } + public string Description { get; protected set; } + public double Width { get; protected set; } + public double Height { get; protected set; } + public double Depth { get; protected set; } + public double Weight { get; protected set; } + public Address Source { get; protected set; } + public Address Destination { get; protected set; } + public Priority Priority { get; protected set; } + public bool AtWeekend { get; protected set; } + public DateTime PickupDate { get; protected set; } + public DateTime DeliveryDate { get; protected set; } + public bool IsCompany { get; protected set; } + public bool VipPackage { get; protected set; } + public DateTime CreatedAt { get; protected set; } + public DateTime ValidTo { get; protected set; } + public decimal CalculatedPrice { get; protected set; } + + public Parcel(AggregateId id, string description, double width, + double height, double depth, double weight, DateTime pickupDate, DateTime deliveryDate, + DateTime createdAt, decimal calculatedPrice, DateTime validTo, Guid? customerId) + : this(id, description, width, height, depth, weight, new Address(), new Address(), + Priority.Low, false, pickupDate, deliveryDate, false, false, createdAt, calculatedPrice, validTo, customerId) + { + + } + + public Parcel(AggregateId id, string description, double width, double height, + double depth, double weight, Address source, Address destination, + Priority priority, bool atWeekend, DateTime pickupDate, DateTime deliveryDate, bool isCompany, bool vipPackage, + DateTime createdAt, decimal calculatedPrice, DateTime validTo, Guid? customerId) + { + Id = id; + CustomerId = customerId; + + CheckDescription(description); + Description = description; + + CheckDimensions(width, height, depth); + Width = width; + Height = height; + Depth = depth; + + CheckWeight(weight); + Weight = weight; + + Source = source; + Destination = destination; + Priority = priority; + AtWeekend = atWeekend; + PickupDate = pickupDate; + DeliveryDate = deliveryDate; + IsCompany = isCompany; + VipPackage = vipPackage; + CreatedAt = createdAt; + ValidTo = validTo; + CalculatedPrice = calculatedPrice; + } + + public void CheckDescription(string description) + { + if (string.IsNullOrEmpty(description)) + { + throw new InvalidParcelDescriptionException(description); + } + } + + public void CheckDimensions(double width, double height, double depth) + { + if (width <= 0) + { + throw new InvalidParcelDimensionException("width", width); + } + + if (height <= 0) + { + throw new InvalidParcelDimensionException("height", height); + } + + if (depth <= 0) + { + throw new InvalidParcelDimensionException("depth", depth); + } + } + + public void CheckWeight(double weight) + { + if (weight <= 0) + { + throw new InvalidParcelWeightException(weight); + } + } + + public void CheckPrice(decimal price) + { + if (price <= 0) + { + throw new InvalidParcelPriceException(price); + } + } + public void SetCalculatedPrice(decimal price) + { + CheckPrice(price); + CalculatedPrice = price; + } + + public void SetSourceAddress(string street, string buildingNumber, string apartmentNumber, string city, string zipCode, string country) + { + SetAddress(Source, street, buildingNumber, apartmentNumber, city, zipCode, country); + } + + public void SetDestinationAddress(string street, string buildingNumber, string apartmentNumber, string city, string zipCode, string country) + { + SetAddress(Destination, street, buildingNumber, apartmentNumber, city, zipCode, country); + } + + private void SetAddress(Address address, string street, string buildingNumber, string apartmentNumber, + string city, string zipCode, string country) + { + CheckAddressElement("street", street); + address.Street = street; + + CheckAddressElement("building number", buildingNumber); + address.BuildingNumber = buildingNumber; + + CheckAddressApartmentNumber("apartment number", ref apartmentNumber); + address.ApartmentNumber = apartmentNumber; + + CheckAddressElement("city", city); + address.City = city; + + CheckAddressZipCode("zip code", zipCode); + address.ZipCode = zipCode; + + CheckAddressElement("country", country); + address.Country = country; + } + + public void CheckAddressElement(string element, string value) + { + if (string.IsNullOrEmpty(value)) + { + throw new InvalidAddressElementException(element, value); + } + } + + public void CheckAddressApartmentNumber(string element, ref string value) + { + if (string.IsNullOrEmpty(value)) + { + value = string.Empty; + } + } + + public void CheckAddressZipCode(string element, string value) + { + string pattern = @"\d{2}[-]\d{3}"; + if (!Regex.IsMatch(value, pattern)) + { + throw new InvalidAddressElementException(element, value); + } + } + public void SetPriority(Priority priority) => Priority = priority; + + public void SetAtWeekend(bool atWeekend) => AtWeekend = atWeekend; + + + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/Priority.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/Priority.cs new file mode 100644 index 0000000..c58da81 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Entities/Priority.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Core.Entities +{ + public enum Priority + { + Low, + High + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/DomainException.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/DomainException.cs new file mode 100644 index 0000000..cd05599 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/DomainException.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Core.Exceptions +{ + public abstract class DomainException : Exception + { + public virtual string Code { get; } + + protected DomainException(string code, string message) : base(message) + { + Code = code; + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidAddressElementException.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidAddressElementException.cs new file mode 100644 index 0000000..7960fea --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidAddressElementException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Core.Exceptions +{ + public class InvalidAddressElementException : DomainException + { + public InvalidAddressElementException(string element, string description) + : base("invalid_address_element", $"Address element ({element}) is invalid: {description}.") + { + + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidAggregateIdException.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidAggregateIdException.cs new file mode 100644 index 0000000..3dffcb9 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidAggregateIdException.cs @@ -0,0 +1,16 @@ +using SwiftParcel.Services.Parcels.Core.Exceptions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Core.Exceptions +{ + public class InvalidAggregateIdException : DomainException + { + public InvalidAggregateIdException() + : base("invalid_aggregate_id", $"Invalid aggregate id.") + { + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidParcelDateTimeException.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidParcelDateTimeException.cs new file mode 100644 index 0000000..3cd4a45 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidParcelDateTimeException.cs @@ -0,0 +1,10 @@ +namespace SwiftParcel.Services.Parcels.Core.Exceptions +{ + public class InvalidParcelDateTimeException : DomainException + { + public InvalidParcelDateTimeException(string element, string description) + : base("invalid_parcel_datetime_property", $"Parcel DateTime property ({element}) is invalid: {description}.") + { + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidParcelDescriptionException.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidParcelDescriptionException.cs new file mode 100644 index 0000000..34738c1 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidParcelDescriptionException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Core.Exceptions +{ + public class InvalidParcelDescriptionException : DomainException + { + public InvalidParcelDescriptionException(string description) + : base("invalid_parcel_description", $"Parcel description is invalid: {description}.") + { + + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidParcelDimensionException.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidParcelDimensionException.cs new file mode 100644 index 0000000..1d6440f --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidParcelDimensionException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Core.Exceptions +{ + public class InvalidParcelDimensionException : DomainException + { + public InvalidParcelDimensionException(string dimensionType, double dimensionValue) + : base("invalid_parcel_dimension", $"Parcel dimension ({dimensionType}) is invalid: {dimensionValue}.") + { + + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidParcelPriceException.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidParcelPriceException.cs new file mode 100644 index 0000000..2b8d47f --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidParcelPriceException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Core.Exceptions +{ + public class InvalidParcelPriceException : DomainException + { + public InvalidParcelPriceException(decimal price) + : base("invalid_parcel_price", $"Parcel price is invalid: {price}.") + { + + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidParcelWeightException.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidParcelWeightException.cs new file mode 100644 index 0000000..d84f5b0 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Exceptions/InvalidParcelWeightException.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Core.Exceptions +{ + public class InvalidParcelWeightException : DomainException + { + public InvalidParcelWeightException(double weight) + : base("invalid_parcel_weight", $"Parcel weight is invalid: {weight}.") + { + + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/IDomainEvent.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/IDomainEvent.cs new file mode 100644 index 0000000..b1062fc --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/IDomainEvent.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Core +{ + public interface IDomainEvent + { + + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Repositories/ICustomerRepository.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Repositories/ICustomerRepository.cs new file mode 100644 index 0000000..4c91156 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Repositories/ICustomerRepository.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SwiftParcel.Services.Parcels.Core.Entities; + +namespace SwiftParcel.Services.Parcels.Core.Repositories +{ + public interface ICustomerRepository + { + Task ExistsAsync(Guid id); + Task AddAsync(Customer customer); + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Repositories/IParcelRepository.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Repositories/IParcelRepository.cs new file mode 100644 index 0000000..bb8e8d6 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/Repositories/IParcelRepository.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Metrics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SwiftParcel.Services.Parcels.Core.Entities; + +namespace SwiftParcel.Services.Parcels.Core.Repositories +{ + public interface IParcelRepository + { + Task GetAsync(Guid id); + Task AddAsync(Parcel parcel); + Task DeleteAsync(Guid id); + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core.csproj b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core.csproj new file mode 100644 index 0000000..27ac386 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core/SwiftParcel.Services.Parcels.Core.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Contexts/AppContext.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Contexts/AppContext.cs new file mode 100644 index 0000000..19d2d23 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Contexts/AppContext.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Parcels.Application; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Contexts +{ + internal class AppContext: IAppContext + { + public string RequestId { get; } + public IIdentityContext Identity { get; } + + internal AppContext() : this(Guid.NewGuid().ToString("N"), IdentityContext.Empty) + { + } + + internal AppContext(CorrelationContext context) : this(context.CorrelationId, + context.User is null ? IdentityContext.Empty : new IdentityContext(context.User)) + { + } + + internal AppContext(string requestId, IIdentityContext identity) + { + RequestId = requestId; + Identity = identity; + } + + internal static IAppContext Empty => new AppContext(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Contexts/AppContextFactory.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Contexts/AppContextFactory.cs new file mode 100644 index 0000000..1b5f5dc --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Contexts/AppContextFactory.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.MessageBrokers; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using SwiftParcel.Services.Parcels.Application; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Contexts +{ + internal sealed class AppContextFactory : IAppContextFactory + { + private readonly ICorrelationContextAccessor _contextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + + public AppContextFactory(ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor) + { + _contextAccessor = contextAccessor; + _httpContextAccessor = httpContextAccessor; + } + + public IAppContext Create() + { + if (_contextAccessor.CorrelationContext is {}) + { + var payload = JsonConvert.SerializeObject(_contextAccessor.CorrelationContext); + + return string.IsNullOrWhiteSpace(payload) + ? AppContext.Empty + : new AppContext(JsonConvert.DeserializeObject(payload)); + } + + var context = _httpContextAccessor.GetCorrelationContext(); + + return context is null ? AppContext.Empty : new AppContext(context); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Contexts/CorrelationContext.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Contexts/CorrelationContext.cs new file mode 100644 index 0000000..fc902a2 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Contexts/CorrelationContext.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Contexts +{ + internal class CorrelationContext + { + public string CorrelationId { get; set; } + public string SpanContext { get; set; } + public UserContext User { get; set; } + public string ResourceId { get; set; } + public string TraceId { get; set; } + public string ConnectionId { get; set; } + public string Name { get; set; } + public DateTime CreatedAt { get; set; } + + public class UserContext + { + public string Id { get; set; } + public bool IsAuthenticated { get; set; } + public string Role { get; set; } + public IDictionary Claims { get; set; } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Contexts/IdentityContext.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Contexts/IdentityContext.cs new file mode 100644 index 0000000..31d746b --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Contexts/IdentityContext.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using System.Threading.Tasks; +using SwiftParcel.Services.Parcels.Application; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Contexts +{ + public class IdentityContext : IIdentityContext + { + public Guid Id { get; } + public string Role { get; } = string.Empty; + public bool IsAuthenticated { get; } + public bool IsOfficeWorker { get; } + public bool IsCourier { get; } + public IDictionary Claims { get; } = new Dictionary(); + + internal IdentityContext() + { + } + + internal IdentityContext(CorrelationContext.UserContext context) + : this(context.Id, context.Role, context.IsAuthenticated, context.Claims) + { + } + + internal IdentityContext(string id, string role, bool isAuthenticated, IDictionary claims) + { + Id = Guid.TryParse(id, out var userId) ? userId : Guid.Empty; + Role = role ?? string.Empty; + IsAuthenticated = isAuthenticated; + IsOfficeWorker = Role.Equals("officeworker", StringComparison.InvariantCultureIgnoreCase); + IsCourier = Role.Equals("courier", StringComparison.InvariantCultureIgnoreCase); + Claims = claims ?? new Dictionary(); + } + + internal static IIdentityContext Empty => new IdentityContext(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs new file mode 100644 index 0000000..cc6d79c --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Commands; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Decorators +{ + internal sealed class OutboxCommandHandlerDecorator : ICommandHandler + where TCommand : class, ICommand + { + private readonly ICommandHandler _handler; + private readonly IMessageOutbox _outbox; + private readonly string _messageId; + private readonly bool _enabled; + + public OutboxCommandHandlerDecorator(ICommandHandler handler, IMessageOutbox outbox, + OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) + { + _handler = handler; + _outbox = outbox; + _enabled = outboxOptions.Enabled; + + var messageProperties = messagePropertiesAccessor.MessageProperties; + _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId) + ? Guid.NewGuid().ToString("N") + : messageProperties.MessageId; + } + + public Task HandleAsync(TCommand command, CancellationToken cancellationToken) + => _enabled + ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(command)) + : _handler.HandleAsync(command); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs new file mode 100644 index 0000000..a0cd56b --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Decorators +{ + internal sealed class OutboxEventHandlerDecorator : IEventHandler + where TEvent : class, IEvent + { + private readonly IEventHandler _handler; + private readonly IMessageOutbox _outbox; + private readonly string _messageId; + private readonly bool _enabled; + + public OutboxEventHandlerDecorator(IEventHandler handler, IMessageOutbox outbox, + OutboxOptions outboxOptions, IMessagePropertiesAccessor messagePropertiesAccessor) + { + _handler = handler; + _outbox = outbox; + _enabled = outboxOptions.Enabled; + + var messageProperties = messagePropertiesAccessor.MessageProperties; + _messageId = string.IsNullOrWhiteSpace(messageProperties?.MessageId) + ? Guid.NewGuid().ToString("N") + : messageProperties.MessageId; + } + + public Task HandleAsync(TEvent @event, CancellationToken cancellationToken) + => _enabled + ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(@event)) + : _handler.HandleAsync(@event); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Exceptions/ExceptionToMessageMapper.cs new file mode 100644 index 0000000..4aeafa2 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.MessageBrokers.RabbitMQ; +using SwiftParcel.Services.Parcels.Application.Commands; +using SwiftParcel.Services.Parcels.Application.Events.Rejected; +using SwiftParcel.Services.Parcels.Application.Exceptions; +using SwiftParcel.Services.Parcels.Core.Exceptions; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Exceptions +{ + public class ExceptionToMessageMapper: IExceptionToMessageMapper + { + public object Map(Exception exception, object message) + => exception switch + { + InvalidAddressElementException ex => message switch + { + AddParcel command => new AddParcelRejected(command.ParcelId, ex.Message, ex.Code), + _ => null, + }, + InvalidParcelDescriptionException ex => message switch + { + AddParcel command => new AddParcelRejected(command.ParcelId, ex.Message, ex.Code), + _ => null, + }, + InvalidParcelDimensionException ex => message switch + { + AddParcel command => new AddParcelRejected(command.ParcelId, ex.Message, ex.Code), + _ => null, + }, + InvalidParcelPriceException ex => message switch + { + AddParcel command => new AddParcelRejected(command.ParcelId, ex.Message, ex.Code), + _ => null, + }, + InvalidParcelWeightException ex => message switch + { + AddParcel command => new AddParcelRejected(command.ParcelId, ex.Message, ex.Code), + _ => null, + }, + ParcelNotFoundException ex => message switch + { + DeleteParcel command => new DeleteParcelRejected(command.ParcelId, ex.Message, ex.Code), + _ => null, + }, + _ => null, + }; + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Exceptions/ExceptionToResponseMapper.cs new file mode 100644 index 0000000..6a1ee70 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Convey; +using Convey.WebApi.Exceptions; +using SwiftParcel.Services.Parcels.Application.Exceptions; +using SwiftParcel.Services.Parcels.Core.Exceptions; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Exceptions +{ + public class ExceptionToResponseMapper: IExceptionToResponseMapper + { + private static readonly ConcurrentDictionary Codes = new ConcurrentDictionary(); + + public ExceptionResponse Map(Exception exception) + => exception switch + { + DomainException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message}, + HttpStatusCode.BadRequest), + AppException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message}, + HttpStatusCode.BadRequest), + _ => new ExceptionResponse(new {code = "error", reason = "There was an error."}, + HttpStatusCode.BadRequest) + }; + + private static string GetCode(Exception exception) + { + var type = exception.GetType(); + if (Codes.TryGetValue(type, out var code)) + { + return code; + } + + var exceptionCode = exception switch + { + DomainException domainException when !string.IsNullOrWhiteSpace(domainException.Code) => domainException + .Code, + AppException appException when !string.IsNullOrWhiteSpace(appException.Code) => appException.Code, + _ => exception.GetType().Name.Underscore().Replace("_exception", string.Empty) + }; + + Codes.TryAdd(type, exceptionCode); + + return exceptionCode; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Extensions.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Extensions.cs new file mode 100644 index 0000000..28d39da --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Extensions.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Convey; +using Convey.CQRS.Commands; +using Convey.CQRS.Events; +using Convey.CQRS.Queries; +using Convey.Discovery.Consul; +using Convey.Docs.Swagger; +using Convey.HTTP; +using Convey.LoadBalancing.Fabio; +using Convey.MessageBrokers; +using Convey.MessageBrokers.CQRS; +using Convey.MessageBrokers.Outbox; +using Convey.MessageBrokers.Outbox.Mongo; +using Convey.MessageBrokers.RabbitMQ; +using Convey.Metrics.AppMetrics; +using Convey.Persistence.MongoDB; +using Convey.Persistence.Redis; +using Convey.Security; +using Convey.Tracing.Jaeger; +using Convey.Tracing.Jaeger.RabbitMQ; +using Convey.WebApi; +using Convey.WebApi.CQRS; +using Convey.WebApi.Swagger; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using SwiftParcel.Services.Parcels.Application; +using SwiftParcel.Services.Parcels.Application.Commands; +using SwiftParcel.Services.Parcels.Application.Events.External; +using SwiftParcel.Services.Parcels.Application.Services; +using SwiftParcel.Services.Parcels.Core.Repositories; +using SwiftParcel.Services.Parcels.Infrastructure.Contexts; +using SwiftParcel.Services.Parcels.Infrastructure.Decorators; +using SwiftParcel.Services.Parcels.Infrastructure.Exceptions; +using SwiftParcel.Services.Parcels.Infrastructure.Logging; +using SwiftParcel.Services.Parcels.Infrastructure.Mongo.Documents; +using SwiftParcel.Services.Parcels.Infrastructure.Mongo.Repositories; +using SwiftParcel.Services.Parcels.Infrastructure.Services; +using SwiftParcel.Services.Parcels.Application.Services.Clients; +using SwiftParcel.Services.Parcels.Infrastructure.Services.Clients; + + +namespace SwiftParcel.Services.Parcels.Infrastructure +{ + public static class Extensions + { + public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + { + builder.Services.AddSingleton(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(ctx => ctx.GetRequiredService().Create()); + builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>)); + builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>)); + + return builder + .AddErrorHandler() + .AddQueryHandlers() + .AddInMemoryQueryDispatcher() + .AddHttpClient() + .AddConsul() + .AddFabio() + .AddRabbitMq(plugins: p => p.AddJaegerRabbitMqPlugin()) + .AddMessageOutbox(o => o.AddMongo()) + .AddExceptionToMessageMapper() + .AddMongo() + .AddRedis() + .AddMetrics() + .AddJaeger() + .AddMongo() + .AddHandlersLogging() + .AddMongoRepository("customers") + .AddMongoRepository("parcels") + .AddWebApiSwaggerDocs() + .AddSecurity(); + } + + public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app) + { + app.UseErrorHandler() + .UseSwaggerDocs() + .UseJaeger() + .UseConvey() + .UsePublicContracts() + .UseMetrics() + .UseRabbitMq() + .SubscribeCommand() + .SubscribeCommand() + .SubscribeEvent(); + + return app; + } + + internal static CorrelationContext GetCorrelationContext(this IHttpContextAccessor accessor) + => accessor.HttpContext?.Request.Headers.TryGetValue("Correlation-Context", out var json) is true + ? JsonConvert.DeserializeObject(json.FirstOrDefault()) + : null; + + internal static IDictionary GetHeadersToForward(this IMessageProperties messageProperties) + { + const string sagaHeader = "Saga"; + if (messageProperties?.Headers is null || !messageProperties.Headers.TryGetValue(sagaHeader, out var saga)) + { + return null; + } + + return saga is null + ? null + : new Dictionary + { + [sagaHeader] = saga + }; + } + + internal static string GetSpanContext(this IMessageProperties messageProperties, string header) + { + if (messageProperties is null) + { + return string.Empty; + } + + if (messageProperties.Headers.TryGetValue(header, out var span) && span is byte[] spanBytes) + { + return Encoding.UTF8.GetString(spanBytes); + } + + return string.Empty; + } + } + +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/IAppContextFactory.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/IAppContextFactory.cs new file mode 100644 index 0000000..96754a4 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/IAppContextFactory.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Parcels.Application; + +namespace SwiftParcel.Services.Parcels.Infrastructure +{ + public interface IAppContextFactory + { + IAppContext Create(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Logging/Extensions.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Logging/Extensions.cs new file mode 100644 index 0000000..f5fcb25 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Logging/Extensions.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey; +using Convey.Logging.CQRS; +using Microsoft.Extensions.DependencyInjection; +using SwiftParcel.Services.Parcels.Application.Commands; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Logging +{ + internal static class Extensions + { + public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + { + var assembly = typeof(AddParcel).Assembly; + + builder.Services.AddSingleton(new MessageToLogTemplateMapper()); + + return builder + .AddCommandHandlersLogging(assembly) + .AddEventHandlersLogging(assembly); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Logging/MessageToLogTemplateMapper.cs new file mode 100644 index 0000000..7f13c3e --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.Logging.CQRS; +using SwiftParcel.Services.Parcels.Application.Commands; +using SwiftParcel.Services.Parcels.Application.Events.External; +using SwiftParcel.Services.Parcels.Application.Exceptions; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Logging +{ + internal sealed class MessageToLogTemplateMapper : IMessageToLogTemplateMapper + { + private static IReadOnlyDictionary MessageTemplates + => new Dictionary + { + { + typeof(AddParcel), + new HandlerLogTemplate + { + After = "Added a parcel with id: {ParcelId}." + } + }, + { + typeof(DeleteParcel), + new HandlerLogTemplate + { + After = "Deleted a parcel with id: {ParcelId}." + } + }, + { + typeof(CustomerCreated), + new HandlerLogTemplate + { + After = "Added a customer with id: {CustomerId}", + OnError = new Dictionary + { + { + typeof(CustomerAlreadyAddedException), + "Customer with id: {CustomerId} was already added." + + } + } + } + }, + }; + + public HandlerLogTemplate Map(TMessage message) where TMessage : class + { + var key = message.GetType(); + return MessageTemplates.TryGetValue(key, out var template) ? template : null; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Documents/CustomerDocument.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Documents/CustomerDocument.cs new file mode 100644 index 0000000..4dcdcdd --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Documents/CustomerDocument.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Convey.Types; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Mongo.Documents +{ + public class CustomerDocument : IIdentifiable + { + public Guid Id { get; set; } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Documents/Extensions.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Documents/Extensions.cs new file mode 100644 index 0000000..cb0ca9a --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Documents/Extensions.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SharpCompress.Common; +using SwiftParcel.Services.Parcels.Application.DTO; +using SwiftParcel.Services.Parcels.Core.Entities; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Mongo.Documents +{ + internal static class Extensions + { + public static Parcel AsEntity(this ParcelDocument document) + => document is null? null : new Parcel( + document.Id, + document.Description, + document.Width, + document.Height, + document.Depth, + document.Weight, + new Address(document.Source.Street, + document.Source.BuildingNumber, + document.Source.ApartmentNumber, + document.Source.City, + document.Source.ZipCode, + document.Source.Country + ), + new Address(document.Destination.Street, + document.Destination.BuildingNumber, + document.Destination.ApartmentNumber, + document.Destination.City, + document.Destination.ZipCode, + document.Destination.Country), + document.Priority, + document.AtWeekend, + document.PickupDate, + document.DeliveryDate, + document.IsCompany, + document.VipPackage, + document.CreatedAt, + document.CalculatedPrice, + document.ValidTo, + document.CustomerId + ); + + public static async Task AsEntityAsync(this Task task) + => (await task).AsEntity(); + + public static ParcelDocument AsDocument(this Parcel entity) + => new ParcelDocument + { + Id = entity.Id, + CustomerId = entity.CustomerId, + Description = entity.Description, + Width = entity.Width, + Height = entity.Height, + Depth = entity.Depth, + Weight = entity.Weight, + Source = new Address() + { + Street = entity.Source.Street, + BuildingNumber = entity.Source.BuildingNumber, + ApartmentNumber = entity.Source.ApartmentNumber, + City = entity.Source.City, + ZipCode = entity.Source.ZipCode, + Country = entity.Source.Country + }, + Destination = new Address() + { + Street = entity.Destination.Street, + BuildingNumber = entity.Destination.BuildingNumber, + ApartmentNumber = entity.Destination.ApartmentNumber, + City = entity.Destination.City, + ZipCode = entity.Destination.ZipCode, + Country = entity.Destination.Country + }, + Priority = entity.Priority, + AtWeekend = entity.AtWeekend, + PickupDate = entity.PickupDate, + DeliveryDate = entity.DeliveryDate, + IsCompany = entity.IsCompany, + VipPackage = entity.VipPackage, + CreatedAt = entity.CreatedAt, + ValidTo = entity.ValidTo, + CalculatedPrice = entity.CalculatedPrice + }; + + public static async Task AsDocumentAsync(this Task task) + => (await task).AsDocument(); + + public static ParcelDto AsDto(this ParcelDocument document) + => new ParcelDto + { + Id = document.Id, + CustomerId = document.CustomerId, + Description = document.Description, + Width = document.Width, + Height = document.Height, + Depth = document.Depth, + Weight = document.Weight, + Source = new AddressDto + { + Street = document.Source.Street, + BuildingNumber = document.Source.BuildingNumber, + ApartmentNumber = document.Source.ApartmentNumber, + City = document.Source.City, + ZipCode = document.Source.ZipCode, + Country = document.Source.Country + }, + Destination = new AddressDto + { + Street = document.Destination.Street, + BuildingNumber = document.Destination.BuildingNumber, + ApartmentNumber = document.Destination.ApartmentNumber, + City = document.Destination.City, + ZipCode = document.Destination.ZipCode, + Country = document.Destination.Country + }, + Priority = document.Priority.ToString().ToLowerInvariant(), + AtWeekend = document.AtWeekend, + PickupDate = document.PickupDate, + DeliveryDate = document.DeliveryDate, + IsCompany = document.IsCompany, + VipPackage = document.VipPackage, + CreatedAt = document.CreatedAt, + ValidTo = document.ValidTo, + CalculatedPrice = document.CalculatedPrice + }; + + public static Customer AsEntity(this CustomerDocument document) + => new Customer(document.Id); + + public static CustomerDocument AsDocument(this Customer entity) + => new CustomerDocument + { + Id = entity.Id + }; + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Documents/ParcelDocument.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Documents/ParcelDocument.cs new file mode 100644 index 0000000..2ec16c8 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Documents/ParcelDocument.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Convey.Types; +using SwiftParcel.Services.Parcels.Core.Entities; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Mongo.Documents +{ + internal class ParcelDocument : IIdentifiable + { + public Guid Id { get; set; } + public Guid? CustomerId { get; set; } + public string Description { get; set; } + public double Width { get; set; } + public double Height { get; set; } + public double Depth { get; set; } + public double Weight { get; set; } + public Address Source { get; set; } + public Address Destination { get; set; } + public Priority Priority { get; set; } + public bool AtWeekend { get; set; } + public DateTime PickupDate { get; set; } + public DateTime DeliveryDate { get; set; } + public bool IsCompany { get; set; } + public bool VipPackage { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime ValidTo { get; set; } + public decimal CalculatedPrice { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Queries/GetParcelExpirationStatusHandler.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Queries/GetParcelExpirationStatusHandler.cs new file mode 100644 index 0000000..3b8bd45 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Queries/GetParcelExpirationStatusHandler.cs @@ -0,0 +1,31 @@ +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Parcels.Application.DTO; +using SwiftParcel.Services.Parcels.Application.Queries; +using SwiftParcel.Services.Parcels.Infrastructure.Mongo.Documents; +using SwiftParcel.Services.Parcels.Application; + + + +namespace SwiftParcel.Services.Parcels.Infrastructure.Mongo.Queries +{ + internal sealed class GetParcelExpirationStatusHandler : IQueryHandler + { + private readonly IMongoRepository _repository; + public GetParcelExpirationStatusHandler(IMongoRepository repository) + { + _repository = repository; + } + + public async Task HandleAsync(GetParcelExpirationStatus query, CancellationToken cancellationToken) + { + var document = await _repository.GetAsync(v => v.Id == query.ParcelId); + return new ExpirationStatusDto + { + ParcelId = document.Id, + TotalPrice = document.CalculatedPrice, + ExpiringAt = document.ValidTo + }; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Queries/GetParcelHandler.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Queries/GetParcelHandler.cs new file mode 100644 index 0000000..6b6434c --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Queries/GetParcelHandler.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Parcels.Application.DTO; +using SwiftParcel.Services.Parcels.Application.Queries; +using SwiftParcel.Services.Parcels.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Mongo.Queries +{ + internal sealed class GetParcelHandler : IQueryHandler + { + private readonly IMongoRepository _repository; + + public GetParcelHandler(IMongoRepository repository) + { + _repository = repository; + } + + public async Task HandleAsync(GetParcel query, CancellationToken cancellationToken) + { + var document = await _repository.GetAsync(v => v.Id == query.ParcelId); + return document?.AsDto(); + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Queries/GetParcelsHandler.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Queries/GetParcelsHandler.cs new file mode 100644 index 0000000..3ef63cd --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Queries/GetParcelsHandler.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using SwiftParcel.Services.Parcels.Application; +using SwiftParcel.Services.Parcels.Application.DTO; +using SwiftParcel.Services.Parcels.Application.Queries; +using SwiftParcel.Services.Parcels.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Mongo.Queries +{ + internal sealed class GetParcelsHandler : IQueryHandler> + { + private readonly IMongoRepository _repository; + private readonly IAppContext _appContext; + + public GetParcelsHandler(IMongoRepository repository, IAppContext appContext) + { + _repository = repository; + _appContext = appContext; + } + + public async Task> HandleAsync(GetParcels query, CancellationToken cancellationToken) + { + var documents = _repository.Collection.AsQueryable(); + + if (query.CustomerId.HasValue) + { + var identity = _appContext.Identity; + if (identity.IsAuthenticated && identity.Id != query.CustomerId && !identity.IsOfficeWorker) + { + return Enumerable.Empty(); + } + + documents = documents.Where(p => p.CustomerId == query.CustomerId); + } + + var orders = await documents.ToListAsync(); + return orders.Select(p => p.AsDto()); + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Queries/GetParcelsOfficeWorkerHandler.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Queries/GetParcelsOfficeWorkerHandler.cs new file mode 100644 index 0000000..d6df250 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Queries/GetParcelsOfficeWorkerHandler.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using Convey.Persistence.MongoDB; +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using SwiftParcel.Services.Parcels.Application; +using SwiftParcel.Services.Parcels.Application.DTO; +using SwiftParcel.Services.Parcels.Application.Queries; +using SwiftParcel.Services.Parcels.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Mongo.Queries +{ + internal sealed class GetParcelsOfficeWorkerHandler : IQueryHandler> + { + private readonly IMongoRepository _repository; + private readonly IAppContext _appContext; + + public GetParcelsOfficeWorkerHandler(IMongoRepository repository, IAppContext appContext) + { + _repository = repository; + _appContext = appContext; + } + + public async Task> HandleAsync(GetParcelsOfficeWorker query, CancellationToken cancellationToken) + { + var documents = _repository.Collection.AsQueryable(); + + var identity = _appContext.Identity; + if (!identity.IsOfficeWorker) + { + return Enumerable.Empty(); + } + + var orders = await documents.ToListAsync(); + return orders.Select(p => p.AsDto()); + } + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Repositories/CustomerMongoRepository.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Repositories/CustomerMongoRepository.cs new file mode 100644 index 0000000..dd79df8 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Repositories/CustomerMongoRepository.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Parcels.Core.Entities; +using SwiftParcel.Services.Parcels.Core.Repositories; +using SwiftParcel.Services.Parcels.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Mongo.Repositories +{ + public class CustomerMongoRepository : ICustomerRepository + { + private readonly IMongoRepository _repository; + + public CustomerMongoRepository(IMongoRepository repository) + => _repository = repository; + + public Task ExistsAsync(Guid id) + => _repository.ExistsAsync(c => c.Id == id); + + public Task AddAsync(Customer customer) + => _repository.AddAsync(customer.AsDocument()); + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Repositories/ParcelMongoRepository.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Repositories/ParcelMongoRepository.cs new file mode 100644 index 0000000..28b0c81 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Mongo/Repositories/ParcelMongoRepository.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MongoDB.Driver; +using Convey.Persistence.MongoDB; +using SwiftParcel.Services.Parcels.Core.Entities; +using SwiftParcel.Services.Parcels.Core.Repositories; +using SwiftParcel.Services.Parcels.Infrastructure.Mongo.Documents; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Mongo.Repositories +{ + internal class ParcelMongoRepository : IParcelRepository + { + private readonly IMongoRepository _repository; + + public ParcelMongoRepository(IMongoRepository repository) + => _repository = repository; + + public Task GetAsync(Guid id) + => _repository + .GetAsync(id) + .AsEntityAsync(); + + public Task AddAsync(Parcel parcel) + => _repository.AddAsync(parcel.AsDocument()); + + public Task DeleteAsync(Guid id) + => _repository.DeleteAsync(id); + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Services/Clients/PricingServiceClient.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Services/Clients/PricingServiceClient.cs new file mode 100644 index 0000000..356f16f --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Services/Clients/PricingServiceClient.cs @@ -0,0 +1,24 @@ +using System; +using System.Threading.Tasks; +using Convey.HTTP; +using SwiftParcel.Services.Parcels.Application.DTO; +using SwiftParcel.Services.Parcels.Application.Services.Clients; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Services.Clients +{ + public class PricingServiceClient : IPricingServiceClient + { + private readonly IHttpClient _httpClient; + private readonly string _url; + + public PricingServiceClient(IHttpClient httpClient, HttpClientOptions options) + { + _httpClient = httpClient; + _url = options.Services["pricing"]; + } + + public Task GetParcelDeliveryPriceAsync(Guid customerId, decimal orderPrice, + double length, double width, double height, double weight, bool highPriority, bool deliverAtWeekend) + => _httpClient.GetAsync($"{_url}/pricing?customerId={customerId}&orderPrice={orderPrice}&length={length}&width={width}&height={height}&weight={weight}&highPriority={highPriority.ToString().ToLowerInvariant()}&deliverAtWeekend={deliverAtWeekend.ToString().ToLowerInvariant()}"); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Services/DateTimeProvider.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Services/DateTimeProvider.cs new file mode 100644 index 0000000..1c21f66 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Services/DateTimeProvider.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SwiftParcel.Services.Parcels.Application.Services; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Services +{ + public class DateTimeProvider : IDateTimeProvider + { + public DateTime Now => DateTime.UtcNow; + } +} diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Services/MessageBroker.cs b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Services/MessageBroker.cs new file mode 100644 index 0000000..5fe356b --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/Services/MessageBroker.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; +using Convey.MessageBrokers.RabbitMQ; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using OpenTracing; +using SwiftParcel.Services.Parcels.Application.Services; + +namespace SwiftParcel.Services.Parcels.Infrastructure.Services +{ + internal sealed class MessageBroker + : IMessageBroker + { + private const string DefaultSpanContextHeader = "span_context"; + private readonly IBusPublisher _busPublisher; + private readonly IMessageOutbox _outbox; + private readonly ICorrelationContextAccessor _contextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IMessagePropertiesAccessor _messagePropertiesAccessor; + private readonly ITracer _tracer; + private readonly ILogger _logger; + private readonly string _spanContextHeader; + + public MessageBroker(IBusPublisher busPublisher, IMessageOutbox outbox, + ICorrelationContextAccessor contextAccessor, IHttpContextAccessor httpContextAccessor, + IMessagePropertiesAccessor messagePropertiesAccessor, RabbitMqOptions options, ITracer tracer, + ILogger logger) + { + _busPublisher = busPublisher; + _outbox = outbox; + _contextAccessor = contextAccessor; + _httpContextAccessor = httpContextAccessor; + _messagePropertiesAccessor = messagePropertiesAccessor; + _tracer = tracer; + _logger = logger; + _spanContextHeader = string.IsNullOrWhiteSpace(options.SpanContextHeader) + ? DefaultSpanContextHeader + : options.SpanContextHeader; + } + + public Task PublishAsync(params IEvent[] events) => PublishAsync(events?.AsEnumerable()); + + public async Task PublishAsync(IEnumerable events) + { + if (events is null) + { + return; + } + + var messageProperties = _messagePropertiesAccessor.MessageProperties; + var originatedMessageId = messageProperties?.MessageId; + var correlationId = messageProperties?.CorrelationId; + var spanContext = messageProperties?.GetSpanContext(_spanContextHeader); + if (string.IsNullOrWhiteSpace(spanContext)) + { + spanContext = _tracer.ActiveSpan is null ? string.Empty : _tracer.ActiveSpan.Context.ToString(); + } + + var headers = messageProperties.GetHeadersToForward(); + var correlationContext = _contextAccessor.CorrelationContext ?? + _httpContextAccessor.GetCorrelationContext(); + + foreach (var @event in events) + { + if (@event is null) + { + continue; + } + + var messageId = Guid.NewGuid().ToString("N"); + _logger.LogTrace($"Publishing integration event: {@event.GetType().Name} [id: '{messageId}']."); + if (_outbox.Enabled) + { + await _outbox.SendAsync(@event, originatedMessageId, messageId, correlationId, spanContext, + correlationContext, headers); + continue; + } + + await _busPublisher.PublishAsync(@event, messageId, correlationId, spanContext, correlationContext, + headers); + } + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure.csproj b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure.csproj new file mode 100644 index 0000000..9f10901 --- /dev/null +++ b/SwiftParcel.Services.Parcels/src/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure/SwiftParcel.Services.Parcels.Infrastructure.csproj @@ -0,0 +1,37 @@ + + + + net6.0 + enable + disable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SwiftParcel.Services.Pricing/scripts/build.sh b/SwiftParcel.Services.Pricing/scripts/build.sh new file mode 100755 index 0000000..3affad0 --- /dev/null +++ b/SwiftParcel.Services.Pricing/scripts/build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +dotnet build -c release \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/scripts/start.sh b/SwiftParcel.Services.Pricing/scripts/start.sh new file mode 100755 index 0000000..5642c4c --- /dev/null +++ b/SwiftParcel.Services.Pricing/scripts/start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=local +cd ../src/SwiftParcel.Services.Pricing.Api +dotnet run \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/scripts/test.sh b/SwiftParcel.Services.Pricing/scripts/test.sh new file mode 100755 index 0000000..6046c35 --- /dev/null +++ b/SwiftParcel.Services.Pricing/scripts/test.sh @@ -0,0 +1,2 @@ +#!/bin/bash +dotnet test \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Core/Entities/Customer.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Core/Entities/Customer.cs new file mode 100644 index 0000000..1a74318 --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Core/Entities/Customer.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Pricing.Api.Core.Entities +{ + public class Customer + { + public Guid Id { get; private set; } + public bool IsVip { get; private set; } + public int CompletedOrdersNumber { get; private set; } + + public Customer(Guid id, bool isVip, int completedOrdersNumber) + { + Id = id; + IsVip = isVip; + CompletedOrdersNumber = completedOrdersNumber; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Core/Entities/Parcel.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Core/Entities/Parcel.cs new file mode 100644 index 0000000..7a2bb62 --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Core/Entities/Parcel.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Pricing.Api.Core.Entities +{ + public class Parcel + { + public double Length { get; private set; } + public double Width { get; private set; } + public double Height { get; private set; } + public double Weight { get; private set; } + public bool HighPriority { get; private set; } + public bool DeliverAtWeekend { get; private set; } + + public Parcel(double length, double width, double height, double weight, bool highPriority, bool deliverAtWeekend) + { + Length = length; + Width = width; + Height = height; + Weight = weight; + HighPriority = highPriority; + DeliverAtWeekend = deliverAtWeekend; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Core/Services/CustomerDiscountsService.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Core/Services/CustomerDiscountsService.cs new file mode 100644 index 0000000..39ca446 --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Core/Services/CustomerDiscountsService.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Pricing.Api.Core.Entities; + +namespace SwiftParcel.Services.Pricing.Api.Core.Services +{ + public class CustomerDiscountsService : ICustomerDiscountsService + { + public decimal CalculateDiscount(Customer customer) + { + var discount = 0.0m; + + if (customer.CompletedOrdersNumber >= 10) + { + discount = 0.1m; + } + else if (customer.CompletedOrdersNumber < 10 && customer.CompletedOrdersNumber > 3) + { + discount = 0.05m; + } + else if(customer.CompletedOrdersNumber <= 3 && customer.CompletedOrdersNumber > 0) + { + discount = 0.02m; + } + + if (customer.IsVip) + { + discount += 0.1m; + } + + return discount; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Core/Services/ICustomerDiscountsService.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Core/Services/ICustomerDiscountsService.cs new file mode 100644 index 0000000..e06aa6f --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Core/Services/ICustomerDiscountsService.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Pricing.Api.Core.Entities; + +namespace SwiftParcel.Services.Pricing.Api.Core.Services +{ + public interface ICustomerDiscountsService + { + decimal CalculateDiscount(Customer customer); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Exceptions/AppException.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Exceptions/AppException.cs new file mode 100644 index 0000000..272b1c9 --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Exceptions/AppException.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Pricing.Api.Exceptions +{ + public abstract class AppException : Exception + { + public virtual string Code { get; } + + protected AppException(string message) : base(message) + { + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Exceptions/CustomerNotFoundException.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Exceptions/CustomerNotFoundException.cs new file mode 100644 index 0000000..cb2d97d --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Exceptions/CustomerNotFoundException.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Pricing.Api.Exceptions +{ + public class CustomerNotFoundException : AppException + { + public override string Code { get; } = "customer_not_found"; + + public CustomerNotFoundException(Guid id) : base($"Customer not found: {id}.") + { + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Exceptions/ExceptionToResponseMapper.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Exceptions/ExceptionToResponseMapper.cs new file mode 100644 index 0000000..5cf9af4 --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Exceptions/ExceptionToResponseMapper.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Convey; +using Convey.WebApi.Exceptions; + +namespace SwiftParcel.Services.Pricing.Api.Exceptions +{ + public class ExceptionToResponseMapper : IExceptionToResponseMapper + { + private static readonly ConcurrentDictionary Codes = new ConcurrentDictionary(); + + public ExceptionResponse Map(Exception exception) + => exception switch + { + AppException ex => new ExceptionResponse(new {code = GetCode(ex), reason = ex.Message}, + HttpStatusCode.BadRequest), + _ => new ExceptionResponse(new {code = "error", reason = "There was an error."}, + HttpStatusCode.BadRequest) + }; + + private static string GetCode(Exception exception) + { + var type = exception.GetType(); + if (Codes.TryGetValue(type, out var code)) + { + return code; + } + + var exceptionCode = exception switch + { + AppException appException when !string.IsNullOrWhiteSpace(appException.Code) => appException.Code, + _ => exception.GetType().Name.Underscore().Replace("_exception", string.Empty) + }; + + Codes.TryAdd(type, exceptionCode); + + return exceptionCode; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Infrastructure/Extensions.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Infrastructure/Extensions.cs new file mode 100644 index 0000000..d6cac69 --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Infrastructure/Extensions.cs @@ -0,0 +1,50 @@ +using Convey; +using Convey.CQRS.Queries; +using Convey.Discovery.Consul; +using Convey.Docs.Swagger; +using Convey.HTTP; +using Convey.LoadBalancing.Fabio; +using Convey.Metrics.AppMetrics; +using Convey.Security; +using Convey.Tracing.Jaeger; +using Convey.WebApi; +using Convey.WebApi.Swagger; +using SwiftParcel.Services.Pricing.Api.Core.Services; +using SwiftParcel.Services.Pricing.Api.Exceptions; +using SwiftParcel.Services.Pricing.Api.Services; + +namespace SwiftParcel.Services.Pricing.Api.Infrastructure +{ + public static class Extensions + { + public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + { + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + + return builder + .AddErrorHandler() + .AddQueryHandlers() + .AddInMemoryQueryDispatcher() + .AddHttpClient() + .AddConsul() + .AddFabio() + .AddMetrics() + .AddJaeger() + .AddWebApiSwaggerDocs() + .AddSecurity(); + } + + public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app) + { + app.UseErrorHandler() + .UseSwaggerDocs() + .UseJaeger() + .UseConvey() + .UseMetrics(); + + return app; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Program.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Program.cs new file mode 100644 index 0000000..f35f0bf --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Program.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Convey; +using Convey.WebApi; +using SwiftParcel.Services.Pricing.Api.Infrastructure; +using Convey.WebApi.CQRS; +using SwiftParcel.Services.Pricing.Api.Queries; +using SwiftParcel.Services.Pricing.Api.dto; +using Convey.Types; +using Convey.Logging; +using Convey.Secrets.Vault; +using Convey.CQRS.Queries; + +namespace SwiftParcel.Services.Pricing.Api +{ + public class Program + { + public static async Task Main(string[] args) + => await WebHost.CreateDefaultBuilder(args) + .ConfigureServices(services => services + .AddConvey() + .AddWebApi() + .AddInfrastructure() + .Build()) + .Configure(app => app + .UseInfrastructure() + .UseDispatcherEndpoints(endpoints => endpoints + .Get("", ctx => ctx.Response.WriteAsync(ctx.RequestServices.GetService().Name)) + .Get("pricing", (query, ctx) => + { + var queryDispatcher = ctx.RequestServices.GetService(); + var getQuery = new GetOrderPricing + { + CustomerId = query.CustomerId, + OrderPrice = query.OrderPrice, + Length = query.Length, + Width = query.Width, + Height = query.Height, + Weight = query.Weight, + HighPriority = query.HighPriority, + DeliverAtWeekend = query.DeliverAtWeekend + }; + return queryDispatcher.QueryAsync(getQuery); + }) + )) + .UseLogging() + .UseVault() + .Build() + .RunAsync(); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Properties/launchSettings.json b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Properties/launchSettings.json new file mode 100644 index 0000000..5b4b5dd --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:57936", + "sslPort": 44380 + } + }, + "profiles": { + "SwiftParcel.Services.Pricing.Api": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5008", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Queries/GetOrderPricing.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Queries/GetOrderPricing.cs new file mode 100644 index 0000000..2e9ac1a --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Queries/GetOrderPricing.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using SwiftParcel.Services.Pricing.Api.dto; + +namespace SwiftParcel.Services.Pricing.Api.Queries +{ + public class GetOrderPricing : IQuery + { + public Guid CustomerId { get; set; } + public decimal OrderPrice { get; set; } + public double Length { get; set; } + public double Width { get; set; } + public double Height { get; set; } + public double Weight { get; set; } + public string HighPriority { get; set; } + public string DeliverAtWeekend { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Queries/Handlers/GetOrderPricingHandler.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Queries/Handlers/GetOrderPricingHandler.cs new file mode 100644 index 0000000..bd9abc8 --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Queries/Handlers/GetOrderPricingHandler.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.CQRS.Queries; +using SwiftParcel.Services.Pricing.Api.Core.Services; +using SwiftParcel.Services.Pricing.Api.dto; +using SwiftParcel.Services.Pricing.Api.Exceptions; +using SwiftParcel.Services.Pricing.Api.Services; + +namespace SwiftParcel.Services.Pricing.Api.Queries.Handlers +{ + internal sealed class GetOrderPricingHandler: IQueryHandler + { + private readonly ICustomersServiceClient _client; + private readonly ICustomerDiscountsService _service; + private readonly IPricingService _pricingService; + private readonly ILogger _logger; + + public GetOrderPricingHandler(ICustomersServiceClient client, ICustomerDiscountsService discountDervice, IPricingService pricingService, + ILogger logger) + { + _client = client; + _service = discountDervice; + _pricingService = pricingService; + _logger = logger; + } + + public async Task HandleAsync(GetOrderPricing query, CancellationToken cancellationToken) + { + CustomerDto customerDto = null; + decimal customerDiscount = 0m; + + if (query.CustomerId != Guid.Empty) + { + customerDto = await _client.GetAsync(query.CustomerId); + if (customerDto != null) + { + var customer = customerDto.AsEntity(); + customerDiscount = _service.CalculateDiscount(customer); // Corrected to use _service + } + } + + var parcelDto = new ParcelDto + { + Length = query.Length, + Width = query.Width, + Height = query.Height, + Weight = query.Weight, + HighPriority = query.HighPriority == "true", + DeliverAtWeekend = query.DeliverAtWeekend == "true" + }; + + var parcel = parcelDto.AsEntity(); + var parcelPrice = _pricingService.CalculateParcelPrice(parcel, customer: null); + + var discountedPrice = parcelPrice - customerDiscount; + var finalPrice = discountedPrice > 0 ? discountedPrice : 0m; // Ensure final price is not negative + + _logger.LogInformation("Calculated pricing for customer ID: {CustomerId}, " + + "parcel price: {ParcelPrice} $, customer discount: {CustomerDiscount} $, " + + "final price: {FinalPrice} $.", + query.CustomerId, parcelPrice, customerDiscount, finalPrice); + + + + return new OrderPricingDto + { + //Parcel = parcelDto, + CustomerDiscount = customerDiscount, + OrderPrice = parcelPrice, + OrderDiscountPrice = discountedPrice, + FinalPrice = finalPrice + }; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Services/CustomersServiceClient.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Services/CustomersServiceClient.cs new file mode 100644 index 0000000..90f81fe --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Services/CustomersServiceClient.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Convey.HTTP; +using SwiftParcel.Services.Pricing.Api.dto; + +namespace SwiftParcel.Services.Pricing.Api.Services +{ + public class CustomersServiceClient : ICustomersServiceClient + { + private readonly IHttpClient _httpClient; + private readonly string _url; + + public CustomersServiceClient(IHttpClient httpClient, HttpClientOptions options) + { + _httpClient = httpClient; + _url = options.Services["customers"]; + } + + public Task GetAsync(Guid id) + => _httpClient.GetAsync($"{_url}/customers/{id}"); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Services/ICustomersServiceClient.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Services/ICustomersServiceClient.cs new file mode 100644 index 0000000..9aa1887 --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Services/ICustomersServiceClient.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Pricing.Api.dto; + +namespace SwiftParcel.Services.Pricing.Api.Services +{ + public interface ICustomersServiceClient + { + Task GetAsync(Guid id); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Services/IPricingService.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Services/IPricingService.cs new file mode 100644 index 0000000..a311f4d --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Services/IPricingService.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Pricing.Api.Core.Entities; + +namespace SwiftParcel.Services.Pricing.Api.Services +{ + public interface IPricingService + { + decimal CalculateParcelPrice(Parcel parcel, Customer customer = null); + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Services/PricingService.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Services/PricingService.cs new file mode 100644 index 0000000..117bae0 --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/Services/PricingService.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using SwiftParcel.Services.Pricing.Api.Core.Entities; +using SwiftParcel.Services.Pricing.Api.Core.Services; + +namespace SwiftParcel.Services.Pricing.Api.Services +{ + public class PricingService : IPricingService + { + private readonly ICustomerDiscountsService _discountsService; + + private const decimal BaseRate = 2.50m; + private const decimal VolumeRate = 0.05m; // Price per cubic unit + private const decimal WeightRate = 0.10m; // Price per weight unit + private const decimal DimensionalWeightDivisor = 5000m; // Divisor for dimensional weight calculation + public PricingService(ICustomerDiscountsService discountsService) + { + _discountsService = discountsService; + } + + public decimal CalculateParcelPrice(Parcel parcel, Customer customer = null) + { + decimal basePrice = CalculateBasePrice(parcel); + decimal discount = customer != null ? _discountsService.CalculateDiscount(customer) : 0m; + decimal priorityCharge = parcel.HighPriority ? 5.00m : 0m; + decimal weekendDeliveryCharge = parcel.DeliverAtWeekend ? 3.00m : 0m; + + return basePrice + priorityCharge + weekendDeliveryCharge - discount; + } + + private decimal CalculateBasePrice(Parcel parcel) + { + decimal length = (decimal)parcel.Length; + decimal width = (decimal)parcel.Width; + decimal height = (decimal)parcel.Height; + decimal weight = (decimal)parcel.Weight; + + decimal volume = length * width * height; + decimal dimensionalWeight = volume / DimensionalWeightDivisor; + decimal weightCharge = Math.Max(weight, dimensionalWeight) * WeightRate; + decimal volumeCharge = volume * VolumeRate; + + return BaseRate + weightCharge + volumeCharge; + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/SwiftParcel.Services.Pricing.Api.csproj b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/SwiftParcel.Services.Pricing.Api.csproj new file mode 100644 index 0000000..ff51b6a --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/SwiftParcel.Services.Pricing.Api.csproj @@ -0,0 +1,26 @@ + + + + net6.0 + disable + enable + + + + + + + + + + + + + + + + + + + + diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/appsettings.Development.json b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/appsettings.Development.json new file mode 100644 index 0000000..ff66ba6 --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/appsettings.json b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/appsettings.json new file mode 100644 index 0000000..ae8efa3 --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/appsettings.json @@ -0,0 +1,159 @@ +{ + "app": { + "name": "SwiftParcel Pricing", + "service": "pricing-service", + "version": "1" + }, + "consul": { + "enabled": true, + "url": "http://localhost:8500", + "service": "pricing-service", + "address": "docker.for.win.localhost", + "port": "5013", + "pingEnabled": true, + "pingEndpoint": "ping", + "pingInterval": 3, + "removeAfterInterval": 3 + }, + "fabio": { + "enabled": true, + "url": "http://localhost:9999", + "service": "pricing-service" + }, + "httpClient": { + "type": "fabio", + "retries": 3, + "services": { + "customers": "customers-service" + }, + "requestMasking": { + "enabled": true, + "maskTemplate": "*****" + } + }, + "AllowedHosts": "*", + "vault": { + "enabled": false, + "url": "http://127.0.0.1:8200", + "authType": "userpass", + "token": "secret", + "username": "user", + "password": "piotr-amadeusz-andrii-2023", + "dbusername": "andrii-courier-db-user", + "kv": { + "enabled": true, + "engineVersion": 2, + "mountPoint": "secret", + "path": "pricing-service/settings" + }, + "pki": { + "enabled": false, + "roleName": "pricing-service", + "commonName": "pricing-service.swiftparcel.com" + }, + "lease": { + "mongo": { + "type": "database", + "roleName": "pricing-service", + "enabled": true, + "autoRenewal": true, + "templates": { + "connectionString": "mongodb+srv://andrii-courier-db-user:piotr-amadeusz-andrii-2023@cluster0.br51nsv.mongodb.net/?retryWrites=true&w=majority" + } + } + } + }, + "logger": { + "level": "information", + "excludePaths": ["/", "/ping", "/metrics"], + "excludeProperties": [ + "api_key", + "access_key", + "ApiKey", + "ApiSecret", + "ClientId", + "ClientSecret", + "ConnectionString", + "Password", + "Email", + "Login", + "Secret", + "Token" + ], + "console": { + "enabled": true + }, + "elk": { + "enabled": false, + "url": "http://localhost:9200" + }, + "file": { + "enabled": true, + "path": "logs/logs.txt", + "interval": "day" + }, + "seq": { + "enabled": true, + "url": "http://localhost:5341", + "apiKey": "secret" + }, + "tags": {} + }, + "swagger": { + "enabled": true, + "reDocEnabled": false, + "name": "v1", + "title": "API", + "version": "v1", + "routePrefix": "docs", + "includeSecurity": true + }, + + "mongo": { + "connectionString": "mongodb+srv://andrii-courier-db-user:piotr-amadeusz-andrii-2023@cluster0.br51nsv.mongodb.net/?retryWrites=true&w=majority", + "database": "pricing-service", + "seed": false + }, + "rabbitMq": { + "connectionName": "pricing-service", + "retries": 3, + "retryInterval": 2, + "conventionsCasing": "snakeCase", + "logger": { + "enabled": true + }, + "username": "guest", + "password": "guest", + "virtualHost": "/", + "port": 5672, + "hostnames": [ + "localhost" + ], + "requestedConnectionTimeout": "00:00:30", + "requestedHeartbeat": "00:01:00", + "socketReadTimeout": "00:00:30", + "socketWriteTimeout": "00:00:30", + "continuationTimeout": "00:00:20", + "handshakeContinuationTimeout": "00:00:10", + "networkRecoveryInterval": "00:00:05", + "exchange": { + "declare": true, + "durable": true, + "autoDelete": false, + "type": "topic", + "name": "identity" + }, + "queue": { + "declare": true, + "durable": true, + "exclusive": false, + "autoDelete": false, + "template": "pricing-service/{{exchange}}.{{message}}" + }, + "context": { + "enabled": true, + "header": "message_context" + }, + "spanContextHeader": "span_context" + } +} diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/dto/CustomerDto.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/dto/CustomerDto.cs new file mode 100644 index 0000000..3d83e35 --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/dto/CustomerDto.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Pricing.Api.dto +{ + public class CustomerDto + { + public Guid Id { get; set; } + public bool IsVip { get; set; } + public IEnumerable CompletedOrders { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/dto/Extensions.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/dto/Extensions.cs new file mode 100644 index 0000000..065a022 --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/dto/Extensions.cs @@ -0,0 +1,15 @@ +using SwiftParcel.Services.Pricing.Api.Core.Entities; + +namespace SwiftParcel.Services.Pricing.Api.dto +{ + internal static class Extensions + { + public static Customer AsEntity(this CustomerDto dto) + => new Customer(dto.Id, dto.IsVip, dto.CompletedOrders.Count()); + + public static Parcel AsEntity(this ParcelDto dto) + { + return new Parcel(dto.Length, dto.Width, dto.Height, dto.Weight, dto.HighPriority, dto.DeliverAtWeekend); + } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/dto/OrderPricingDto.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/dto/OrderPricingDto.cs new file mode 100644 index 0000000..8dc1fa0 --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/dto/OrderPricingDto.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Pricing.Api.dto +{ + public class OrderPricingDto + { + //public ParcelDto Parcel { get; set; } + public decimal OrderPrice { get; set; } // Price before discount + public decimal CustomerDiscount { get; set; } // Discount amount + public decimal OrderDiscountPrice { get; set; } // Price after discount + public decimal FinalPrice { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/dto/ParcelDto.cs b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/dto/ParcelDto.cs new file mode 100644 index 0000000..25eb54d --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.Api/dto/ParcelDto.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Pricing.Api.dto +{ + public class ParcelDto + { + public double Length { get; set; } + public double Width { get; set; } + public double Height { get; set; } + public double Weight { get; set; } + public bool HighPriority { get; set; } + public bool DeliverAtWeekend { get; set; } + } +} \ No newline at end of file diff --git a/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.sln b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.sln new file mode 100644 index 0000000..5a6004f --- /dev/null +++ b/SwiftParcel.Services.Pricing/src/SwiftParcel.Services.Pricing.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Pricing.Api", "SwiftParcel.Services.Pricing.Api\SwiftParcel.Services.Pricing.Api.csproj", "{6C5048FE-1DCC-406B-86CB-479CDC891D5D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6C5048FE-1DCC-406B-86CB-479CDC891D5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6C5048FE-1DCC-406B-86CB-479CDC891D5D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6C5048FE-1DCC-406B-86CB-479CDC891D5D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6C5048FE-1DCC-406B-86CB-479CDC891D5D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/SwiftParcel.Web/Dockerfile b/SwiftParcel.Web/Dockerfile new file mode 100644 index 0000000..d7e7183 --- /dev/null +++ b/SwiftParcel.Web/Dockerfile @@ -0,0 +1,20 @@ +# Use the official Node.js 18 image as a base image +FROM node:18 + +# Set the working directory in the container +WORKDIR /usr/src/app + +# Navigate to the frontend directory and copy package.json files +COPY ./frontend/package*.json ./ + +# Install the project dependencies +RUN npm install --legacy-peer-deps + +# Copy the rest of the frontend directory into the container +COPY ./frontend/ . + +# Make port 3001 available to the world outside this container +EXPOSE 3001 + +# Define the command to run your app using CMD which defines your runtime +CMD ["npm", "start"] diff --git a/SwiftParcel.Web/backend/.env.example b/SwiftParcel.Web/backend/.env.example deleted file mode 100644 index cf39be2..0000000 --- a/SwiftParcel.Web/backend/.env.example +++ /dev/null @@ -1,16 +0,0 @@ -port=8080 - -db_dialect="mysql" -db_host="localhost" -db_user="root" -db_password="" -db_database="parcel-delivery" -db_logging=false - -pool_max=5 -pool_min=0 -pool_acquire=30000 -pool_idle=10000 - -jwt_secret="secret" -jwt_expiration=7d \ No newline at end of file diff --git a/SwiftParcel.Web/backend/.gitignore b/SwiftParcel.Web/backend/.gitignore deleted file mode 100644 index 6a7d6d8..0000000 --- a/SwiftParcel.Web/backend/.gitignore +++ /dev/null @@ -1,130 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp -.cache - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* \ No newline at end of file diff --git a/SwiftParcel.Web/backend/controllers/car.controller.js b/SwiftParcel.Web/backend/controllers/car.controller.js deleted file mode 100644 index 742256b..0000000 --- a/SwiftParcel.Web/backend/controllers/car.controller.js +++ /dev/null @@ -1,171 +0,0 @@ -const { getPagination, getPagingData } = require('./shared'); -const db = require("../models"); -const Cars = db.cars; -const Couriers = db.couriers; - -exports.create = (req, res) => { - if (req.user.role != "Admin") { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - const car = { - make: req.body.make, - model: req.body.model, - licensePlate: req.body.licensePlate - } - - Cars.create(car) - .then(data => { - res.status(201).send(data); - }) - .catch(err => { - res.status(400).send({ - message: err.message || "Some error occurred while creating the Car." - }); - }); -}; - -exports.findAll = (req, res) => { - if (req.user.role != "Admin") { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - const { page, size } = req.query; - if (page != null && isNaN(page) || size != null && isNaN(size)) { - res.status(400).send({ - message: "Page and size must be numbers." - }); - return; - } - - const { limit, offset } = getPagination(page, size); - - Cars.findAndCountAll({ limit, offset }) - .then(data => { - if (data.count == 0) { - res.status(204).send({ - message: "Cars were not found." - }); - } else { - const response = getPagingData(data, page, limit); - if (response.page > response.total_pages) { - res.status(400).send({ - message: `Page ${page} was not found.` - }); - } else { - res.send(response); - } - } - }) - .catch(err => { - res.status(500).send({ - message: err.message || "Some error occurred while retrieving cars." - }); - }); -}; - -exports.findOne = async (req, res) => { - const id = req.params.id; - - const courier = await Couriers.findOne({ where: { userId: req.user.id } }); - if (req.user.role != "Admin" && ((!courier || !courier.carId) || (courier && id != courier.carId))) { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - - Cars.findByPk(id) - .then(data => { - if (data == null) { - res.status(400).send({ - message: `Car with id ${id} was not found.` - }); - } else { - res.send(data); - } - }) - .catch(err => { - res.status(500).send({ - message: err.message || "Some error occurred while retrieving the Car." - }); - }); -}; - -exports.update = async (req, res) => { - if (req.user.role != "Admin") { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - const id = req.params.id; - - const carExists = await Cars.count({ where: { id } }) > 0; - if (!carExists) { - res.status(400).send({ - message: `Car with id ${id} was not found.` - }); - return; - } - - if ("id" in req.body) { - res.status(400).send({ - message: "Id cannot be changed." - }); - return; - } - - Cars.update(req.body, { - where: { id }, - }) - .then(num => { - if (num == 1) { - Cars.findByPk(id) - .then(data => { - res.send(data); - }); - } else { - res.status(400).send({ - message: `Failed to update Car with id ${id}.` - }); - } - }) - .catch(err => { - res.status(400).send({ - message: err.message || "Some error occurred while updating the Car." - }); - }); -}; - -exports.delete = (req, res) => { - if (req.user.role != "Admin") { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - const id = req.params.id; - - Cars.destroy({ - where: { id } - }) - .then(num => { - if (num == 1) { - res.status(204).send(); - } else { - res.status(400).send({ - message: `Car with id ${id} was not found.` - }); - } - }) - .catch(err => { - res.status(500).send({ - message: err.message || "Some error occurred while deleting the Car." - }); - }); -}; \ No newline at end of file diff --git a/SwiftParcel.Web/backend/controllers/courier.controller.js b/SwiftParcel.Web/backend/controllers/courier.controller.js deleted file mode 100644 index b3aff11..0000000 --- a/SwiftParcel.Web/backend/controllers/courier.controller.js +++ /dev/null @@ -1,198 +0,0 @@ -const { getPagination, getPagingData } = require('./shared'); -const db = require("../models"); -const Couriers = db.couriers; -const Cars = db.cars; -const Users = db.users; - -exports.create = (req, res) => { - if (req.user.role != "Admin") { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - const courier = { - firstname: req.body.firstname, - lastname: req.body.lastname, - phone: req.body.phone, - status: req.body.status, - carId: req.body.carId, - userId: req.body.userId - }; - - Couriers.create(courier) - .then(data => { - res.status(201).send(data); - }) - .catch(err => { - res.status(400).send({ - message: err.message || "Some error occurred while creating the Courier." - }); - }); -}; - -exports.findAll = (req, res) => { - if (req.user.role != "Admin") { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - const { page, size } = req.query; - if (page != null && isNaN(page) || size != null && isNaN(size)) { - res.status(400).send({ - message: "Page and size must be numbers." - }); - return; - } - - const { limit, offset } = getPagination(page, size); - - Couriers.findAndCountAll({ - limit, - offset, - include: [{ model: Cars, as: "car", attributes: ['id', 'licensePlate'] }, { model: Users, as: "user", attributes: ['id', 'username', 'email'] }], - attributes: { exclude: ['carId', 'userId'] }, - }) - .then(data => { - if (data.count == 0) { - res.status(204).send({ - message: "Couriers were not found." - }); - } else { - const response = getPagingData(data, page, limit); - if (response.page > response.total_pages) { - res.status(400).send({ - message: `Page ${page} was not found.` - }); - } else { - res.send(response); - } - } - }) - .catch(err => { - res.status(500).send({ - message: err.message || "Some error occurred while retrieving couriers." - }); - }); -}; - -exports.findOne = (req, res) => { - if (req.user.role != "Admin") { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - const id = req.params.id; - - Couriers.findByPk(id, { include: ["car", "user"], attributes: { exclude: ['carId', 'userId'] } }) - .then(data => { - if (data == null) { - res.status(400).send({ - message: `Courier with id ${id} was not found.` - }); - } else { - res.send(data); - } - }) - .catch(err => { - res.status(500).send({ - message: err.message || "Some error occurred while retrieving the Courier." - }); - }); -}; - -exports.update = async (req, res) => { - if (req.user.role != "Admin") { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - const id = req.params.id; - - const courierExists = await Couriers.count({ where: { id } }) > 0; - if (!courierExists) { - res.status(400).send({ - message: `Courier with id ${id} was not found.` - }); - return; - } - - if ("id" in req.body) { - res.status(400).send({ - message: "Id cannot be changed." - }); - return; - } - - if ("carId" in req.body && req.body.carId != null) { - const carExists = await Cars.count({ where: { id: req.body.carId } }) > 0; - if (!carExists) { - res.status(400).send({ - message: `Car with id ${req.body.carId} was not found.` - }); - return; - } - } - - if ("userId" in req.body && req.body.userId != null) { - const userExists = await Users.count({ where: { id: req.body.userId } }) > 0; - if (!userExists) { - res.status(400).send({ - message: `User with id ${req.body.userId} was not found.` - }); - return; - } - } - - Couriers.update(req.body, { - where: { id }, - }) - .then(num => { - if (num == 1) { - Couriers.findByPk(id, { include: ["car", "user"], attributes: { exclude: ['carId', 'userId'] } }) - .then(data => { - res.send(data); - }); - } else { - res.status(400).send({ - message: `Failed to update Courier with id ${id}.` - }); - } - }) - .catch(err => { - res.status(400).send({ - message: err.message || "Some error occurred while updating the Courier." - }); - }); -}; - -exports.delete = (req, res) => { - if (req.user.role != "Admin") { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - const id = req.params.id; - - Couriers.destroy({ - where: { id } - }) - .then(num => { - if (num == 1) { - res.status(204).send(); - } else { - res.status(400).send({ - message: `Courier with id ${id} was not found.` - }); - } - }) - .catch(err => { - res.status(500).send({ - message: err.message || "Some error occurred while deleting the Courier." - }); - }); -}; \ No newline at end of file diff --git a/SwiftParcel.Web/backend/controllers/parcel.controller.js b/SwiftParcel.Web/backend/controllers/parcel.controller.js deleted file mode 100644 index 4542456..0000000 --- a/SwiftParcel.Web/backend/controllers/parcel.controller.js +++ /dev/null @@ -1,363 +0,0 @@ -const { getPagination, getPagingData } = require('./shared'); -const db = require("../models"); -const userModel = require('../models/user.model'); -const Parcels = db.parcels; -const Couriers = db.couriers; - -exports.create = (req, res) => { - if (req.user.role != 'Admin') { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - - const parcel = { - senderName: req.body.senderName, - senderAddress: req.body.senderAddress, - senderPhone: req.body.senderPhone, - receiverName: req.body.receiverName, - receiverAddress: req.body.receiverAddress, - receiverPhone: req.body.receiverPhone, - weight: req.body.weight, - price: req.body.price, - status: req.body.status, - courierId: req.body.courierId - } - - Parcels.create(parcel) - .then(data => { - res.status(201).send(data); - }) - .catch(err => { - res.status(400).send({ - message: err.message || "Some error occurred while creating the Parcel." - }); - }); -}; - -exports.findAll = async (req, res) => { - const { page, size, unassigned } = req.query; - if (page != null && isNaN(page) || size != null && isNaN(size)) { - res.status(400).send({ - message: "Page and size must be numbers." - }); - return; - } - - const { limit, offset } = getPagination(page, size); - - let where = {}; - const courier = await Couriers.findOne({ - where: { userId: req.user.id }, - attributes: ['id'] - }); - - if(unassigned != null && unassigned == 'true') { - where.courierId = null; - } - - if (courier != null) { - where.courierId = courier.id; - } else if (req.user.role != 'Admin') { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - - Parcels.findAndCountAll({ limit, offset, where, include: { - model: Couriers, - as: "courier", - attributes: ['id', 'firstname', 'lastname'], - }, attributes: { - exclude: ['courierId'] - }}) - .then(data => { - if (data.count == 0) { - res.status(204).send({ - message: "Parcels were not found." - }); - } else { - const response = getPagingData(data, page, limit); - if (response.page > response.total_pages) { - res.status(400).send({ - message: `Page ${page} was not found.` - }); - } else { - res.send(response); - } - } - }) - .catch(err => { - res.status(500).send({ - message: err.message || "Some error occurred while retrieving parcels." - }); - }); -}; - -exports.findAllByCourier = async (req, res) => { - const id = req.params.id; - const { page, size } = req.query; - if (page != null && isNaN(page) || size != null && isNaN(size)) { - res.status(400).send({ - message: "Page and size must be numbers." - }); - return; - } - - const { limit, offset } = getPagination(page, size); - - const courier = await Couriers.findOne({ - where: { id: id, userId: req.user.id }, - attributes: ['id'] - }); - - if (req.user.role != 'Admin' && courier == null) { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - - Parcels.findAndCountAll({limit, offset, where: { courierId: id }}) - .then(data => { - if (data.count == 0) { - res.status(400).send({ - message: `Parcels with courier id ${id} were not found.` - }); - } else { - const response = getPagingData(data, page, limit); - if (response.page > response.total_pages) { - res.status(400).send({ - message: `Page ${page} was not found.` - }); - } else { - res.send(response); - } - } - }) - .catch(err => { - res.status(500).send({ - message: err.message || "Some error occurred while retrieving parcels." - }); - }); -}; - -exports.findAllByCars = (req, res) => { - if (req.user.role != 'Admin') { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - - const id = req.params.id; - const { page, size } = req.query; - if (page != null && isNaN(page) || size != null && isNaN(size)) { - res.status(400).send({ - message: "Page and size must be numbers." - }); - return; - } - - const { limit, offset } = getPagination(page, size); - Parcels.findAndCountAll({ - limit, offset, - include: [ - { - model: Couriers, - as: "courier", - where: { carId: id }, - attributes: ['id', 'firstname', 'lastname'] - } - ], - attributes: { - exclude: ['courierId'] - } - }) - .then(data => { - if (data.count == 0) { - res.status(400).send({ - message: `Parcels with car id ${id} were not found.` - }); - } else { - const response = getPagingData(data, page, limit); - if (response.page > response.total_pages) { - res.status(400).send({ - message: `Page ${page} was not found.` - }); - } else { - res.send(response); - } - } - }) - .catch(err => { - res.status(500).send({ - message: err.message || "Some error occurred while retrieving parcels." - }); - }); -}; - -exports.findOne = async (req, res) => { - const id = req.params.id; - - const data = { - where: { "parcelNumber": id }, - } - - if(req.user != null) { - const courier = await Couriers.findOne({ - where: { userId: req.user.id }, - attributes: ['id'] - }); - - if (req.user.role == 'Admin' || courier != null) { - data.include = { - model: Couriers, - as: "courier", - include: ["car", "user"], - attributes: { - exclude: ['carId', 'userId'] - } - } - data.attributes = { - exclude: ['courierId'] - } - } else { - data.attributes = [ 'parcelNumber', 'status', 'senderName', 'receiverName', 'weight', 'createdAt', 'updatedAt' ] - } - } else { - data.attributes = [ 'parcelNumber', 'status', 'senderName', 'receiverName', 'weight', 'createdAt', 'updatedAt' ] - } - - Parcels.findOne(data) - .then(data => { - if (data == null) { - res.status(400).send({ - message: `Parcel with id ${id} was not found.` - }); - } else { - res.send(data); - } - }) - .catch(err => { - res.status(500).send({ - message: err.message || "Some error occurred while retrieving the Parcel." - }); - }); -}; - -exports.update = async (req, res) => { - const id = req.params.id; - - let where = {}; - const courier = await Couriers.findOne({ - where: { userId: req.user.id }, - attributes: ['id'] - }); - - if (courier != null) { - where.courierId = courier.id; - } else if (req.user.role != 'Admin') { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - - const parcelExists = await Parcels.count({ where: { parcelNumber: id, ...where } }) > 0; - if (!parcelExists) { - res.status(400).send({ - message: `Parcel with id ${id} was not found.` - }); - return; - } - - if(req.user.role != 'Admin' && where.hasOwnProperty('courierId') && (!req.body.hasOwnProperty('status') || (req.body.hasOwnProperty('status') && Object.keys(req.body).length > 1))) { - res.status(400).send({ - message: "Page access is restricted." - }); - return; - } - - if ("parcelNumber" in req.body) { - res.status(400).send({ - message: "Parcel number cannot be changed." - }); - return; - } - - if ("courierId" in req.body && req.body.courierId != null) { - const courierExists = await Couriers.count({ where: { id: req.body.courierId } }) > 0;; - if (!courierExists) { - res.status(400).send({ - message: `Courier with id ${req.body.courierId} was not found.` - }); - return; - } - } - - Parcels.update(req.body, { - where: { parcelNumber: id, ...where }, - }) - .then(num => { - if (num == 1) { - Parcels.findByPk(id, { - include: [ - { - model: Couriers, - as: "courier", - include: ["car", "user"], - attributes: { - exclude: ['carId', 'userId'] - } - } - ], - attributes: { - exclude: ['courierId'] - } - }) - .then(data => { - res.send(data); - }); - } else { - res.status(400).send({ - message: `Failed to update Parcel with id ${id}.` - }); - } - }) - .catch(err => { - res.status(400).send({ - message: err.message || "Some error occurred while updating the Parcel." - }); - }); -}; - -exports.delete = (req, res) => { - if (req.user.role != 'Admin') { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - const id = req.params.id; - - Parcels.destroy({ - where: { parcelNumber: id } - }) - .then(num => { - if (num == 1) { - res.status(204).send(); - } else { - res.status(400).send({ - message: `Parcel with id ${id} was not found.` - }); - } - }) - .catch(err => { - res.status(500).send({ - message: err.message || "Some error occurred while deleting the Parcel." - }); - }); -}; \ No newline at end of file diff --git a/SwiftParcel.Web/backend/controllers/shared.js b/SwiftParcel.Web/backend/controllers/shared.js deleted file mode 100644 index dd61311..0000000 --- a/SwiftParcel.Web/backend/controllers/shared.js +++ /dev/null @@ -1,14 +0,0 @@ -exports.getPagination = (page, size) => { - const limit = size ? +size : 3; - const offset = page ? (page - 1) * limit : 0; - - return { limit, offset }; -}; - -exports.getPagingData = (data, page, limit) => { - const { count: total_results, rows: results } = data; - const current_page = page ? +page : 1; - const total_pages = Math.ceil(total_results / limit); - - return { page: current_page, results, total_pages, total_results }; -}; \ No newline at end of file diff --git a/SwiftParcel.Web/backend/controllers/user.controller.js b/SwiftParcel.Web/backend/controllers/user.controller.js deleted file mode 100644 index 2ba6ccb..0000000 --- a/SwiftParcel.Web/backend/controllers/user.controller.js +++ /dev/null @@ -1,122 +0,0 @@ -const { getPagination, getPagingData } = require('./shared'); -const db = require("../models"); -const auth = require("../middlewares/auth.middleware"); -const Users = db.users; -const Couriers = db.couriers; - -const { Op } = require("sequelize"); - -exports.register = (req, res) => { - const user = { - username: req.body.username, - password: req.body.password, - email: req.body.email - }; - - Users.create(user) - .then(user => { - user.dataValues.password = undefined; - user.dataValues.iat = undefined; - const token = auth.generateToken(user); - res.status(201).send({ user, token }); - }) - .catch(err => { - res.status(400).send({ - message: err.message || "Some error occurred while creating the User." - }); - }); -}; - -exports.login = (req, res) => { - const { username, password } = req.body; - Users.scope("withPassword").findOne({ where: { username }, attributes: { exclude: ['iat'] } }) - .then(user => { - if (user == null) { - res.status(400).send({ - message: `Failed to login.` - }); - } else { - if (user.validPassword(password)) { - user.dataValues.password = undefined; - const token = auth.generateToken(user); - res.status(201).send({ user, token }); - } else { - res.status(400).send({ - message: "Failed to login." - }); - } - } - }) - .catch(err => { - res.status(500).send({ - message: err.message || "Some error occurred while retrieving users." - }); - }); -}; - -exports.logout = (req, res) => { - Users.update({ iat: null }, { where: { id: req.user.id } }).then(() => { - res.send({ - message: "User logged out successfully." - }); - }).catch(err => { - res.status(500).send({ - message: err.message || "Some error occurred while logging out." - }); - }); -}; - -exports.profile = async (req, res) => { - const courier = await Couriers.findOne({ where: { userId: req.user.id } }); - res.send({ ...req.user.dataValues, courier }); -}; - -exports.findAll = (req, res) => { - if (req.user.role != "Admin") { - res.status(403).send({ - message: "Page access is restricted." - }); - return; - } - const { page, size, unassigned } = req.query; - if (page != null && isNaN(page) || size != null && isNaN(size)) { - res.status(400).send({ - message: "Page and size must be numbers." - }); - return; - } - - let where = {}; - if(unassigned != null && unassigned == 'true') { - where.id = { [Op.notIn]: db.Sequelize.literal("(SELECT userId FROM couriers WHERE userId IS NOT NULL)") }; - } - - const { limit, offset } = getPagination(page, size); - Users.findAndCountAll({ - limit, - offset, - where, - attributes: { exclude: ['password', 'iat'] }, - }) - .then(data => { - if (data.count == 0) { - res.status(204).send({ - message: "Users were not found." - }); - } else { - const response = getPagingData(data, page, limit); - if (response.page > response.total_pages) { - res.status(400).send({ - message: `Page ${page} was not found.` - }); - } else { - res.send(response); - } - } - }) - .catch(err => { - res.status(500).send({ - message: err.message || "Some error occurred while retrieving users." - }); - }); -}; \ No newline at end of file diff --git a/SwiftParcel.Web/backend/index.js b/SwiftParcel.Web/backend/index.js deleted file mode 100644 index efd5a6e..0000000 --- a/SwiftParcel.Web/backend/index.js +++ /dev/null @@ -1,39 +0,0 @@ -require('dotenv').config(); - -const express = require("express"); -const cors = require("cors"); - -const app = express(); - -var corsOptions = { - origin: "https://nojusgat.github.io" // frontend url -}; - -app.use(cors(corsOptions)); -app.use(express.json()); -app.use(express.urlencoded({ extended: true })); - -const db = require("./models"); - -db.sequelize.sync() - .then(() => { - console.log("Synced db."); - }) - .catch((err) => { - console.log("Failed to sync db: " + err.message); - }); - -app.get("/", (req, res) => { - res.json({ message: "Welcome to the application." }); -}); - -require("./routes/parcel.routes")(app); -require("./routes/courier.routes")(app); -require("./routes/car.routes")(app); -require("./routes/user.routes")(app); - - -const port = process.env.PORT || 8080; -app.listen(port, () => { - console.log(`Backend server is running on port ${process.env.port}.`); -}); \ No newline at end of file diff --git a/SwiftParcel.Web/backend/middlewares/auth.middleware.js b/SwiftParcel.Web/backend/middlewares/auth.middleware.js deleted file mode 100644 index c7f3370..0000000 --- a/SwiftParcel.Web/backend/middlewares/auth.middleware.js +++ /dev/null @@ -1,64 +0,0 @@ -const jwt = require('jsonwebtoken'); -const db = require("../models"); -const Users = db.users; - -exports.generateToken = (user) => { - const token = jwt.sign({ - id: user.id, - email: user.email, - role: user.role - }, process.env.jwt_secret, { - expiresIn: process.env.jwt_expiration - }); - - const decoded = jwt.verify(token, process.env.jwt_secret); - - Users.update({ - iat: decoded.iat - }, { - where: { - id: user.id - } - }); - - return token; -}; - -exports.authorization = async (req, res, next) => { - try { - const token = req.headers.authorization.split(" ")[1]; - const decoded = jwt.verify(token, process.env.jwt_secret); - - const user = await Users.findByPk(decoded.id); - if (!user || user.iat != decoded.iat) - throw new Error(); - - user.dataValues.iat = undefined; - - req.user = user; - next(); - } catch (error) { - return res.status(401).json({ - message: "Authorization failed." - }); - } -}; - -exports.authorization_check = async (req, res, next) => { - try { - const token = req.headers.authorization.split(" ")[1]; - const decoded = jwt.verify(token, process.env.jwt_secret); - - const user = await Users.findByPk(decoded.id); - if (!user || user.iat != decoded.iat) - throw new Error(); - - user.dataValues.iat = undefined; - - req.user = user; - next(); - } catch (error) { - req.user = null; - next(); - } -}; \ No newline at end of file diff --git a/SwiftParcel.Web/backend/models/car.model.js b/SwiftParcel.Web/backend/models/car.model.js deleted file mode 100644 index 714a5aa..0000000 --- a/SwiftParcel.Web/backend/models/car.model.js +++ /dev/null @@ -1,42 +0,0 @@ -module.exports = (sequelize, Sequelize) => { - const Car = sequelize.define("car", { - make: { - type: Sequelize.STRING, - allowNull: false, - validate: { - notEmpty: { - msg: "Car make cannot be empty" - }, - notNull: { - msg: 'Car make is required' - } - } - }, - model: { - type: Sequelize.STRING, - allowNull: false, - validate: { - notEmpty: { - msg: "Car model cannot be empty" - }, - notNull: { - msg: 'Car model is required' - } - } - }, - licensePlate: { - type: Sequelize.STRING, - allowNull: false, - validate: { - notEmpty: { - msg: "License plate cannot be empty" - }, - notNull: { - msg: 'License plate is required' - } - } - } - }, { timestamps: false }); - - return Car; -}; \ No newline at end of file diff --git a/SwiftParcel.Web/backend/models/courier.model.js b/SwiftParcel.Web/backend/models/courier.model.js deleted file mode 100644 index d9c6dec..0000000 --- a/SwiftParcel.Web/backend/models/courier.model.js +++ /dev/null @@ -1,52 +0,0 @@ -module.exports = (sequelize, Sequelize) => { - const Courier = sequelize.define("courier", { - firstname: { - type: Sequelize.STRING, - allowNull: false, - validate: { - notEmpty: { - msg: "First name cannot be empty" - }, - notNull: { - msg: 'First name is required' - } - } - }, - lastname: { - type: Sequelize.STRING, - allowNull: false, - validate: { - notEmpty: { - msg: "Last name cannot be empty" - }, - notNull: { - msg: 'Last name is required' - } - } - }, - phone: { - type: Sequelize.STRING, - allowNull: false, - validate: { - notEmpty: { - msg: "Phone number cannot be empty" - }, - notNull: { - msg: 'Phone number is required' - } - } - }, - status: { - type: Sequelize.ENUM('Available', 'Busy'), - defaultValue: 'Available', - validate: { - isIn: { - args: [['Available', 'Busy']], - msg: "Status must be either 'Available' or 'Busy'" - } - } - } - }); - - return Courier; -}; \ No newline at end of file diff --git a/SwiftParcel.Web/backend/models/index.js b/SwiftParcel.Web/backend/models/index.js deleted file mode 100644 index ef7bf8e..0000000 --- a/SwiftParcel.Web/backend/models/index.js +++ /dev/null @@ -1,50 +0,0 @@ -const Sequelize = require("sequelize"); - -const sequelize = new Sequelize(process.env.db_database, process.env.db_user, process.env.db_password, { - host: process.env.db_host, - dialect: process.env.db_dialect, - operatorsAliases: 0, - pool: { - max: Number(process.env.pool_max) || 5, - min: Number(process.env.pool_min) || 0, - acquire: Number(process.env.pool_acquire) || 30000, - idle: Number(process.env.pool_idle) || 10000 - }, - logging: process.env.db_logging === "true" -}); - -const db = {}; - -db.Sequelize = Sequelize; -db.sequelize = sequelize; - -db.parcels = require("./parcel.model.js")(sequelize, Sequelize); -db.couriers = require("./courier.model.js")(sequelize, Sequelize); -db.cars = require("./car.model.js")(sequelize, Sequelize); -db.users = require("./user.model.js")(sequelize, Sequelize); - -db.couriers.hasMany(db.parcels, { as: "parcel" }); -db.parcels.belongsTo(db.couriers, { - foreignKey: "courierId", - as: "courier", -}); - -db.cars.hasOne(db.couriers, { as: "car" }); -db.couriers.belongsTo(db.cars, { - foreignKey: { - name: "carId", - unique: true - }, - as: "car", -}); - -db.users.hasOne(db.couriers, { as: "user" }); -db.couriers.belongsTo(db.users, { - foreignKey: { - name: "userId", - unique: true - }, - as: "user", -}); - -module.exports = db; \ No newline at end of file diff --git a/SwiftParcel.Web/backend/models/parcel.model.js b/SwiftParcel.Web/backend/models/parcel.model.js deleted file mode 100644 index 3e720bb..0000000 --- a/SwiftParcel.Web/backend/models/parcel.model.js +++ /dev/null @@ -1,139 +0,0 @@ -const { v4: uuidv4 } = require('uuid'); - -module.exports = (sequelize, Sequelize) => { - const Parcel = sequelize.define("parcel", { - parcelNumber: { - type: Sequelize.STRING, - allowNull: false, - unique: true, - primaryKey: true, - defaultValue: function() { - return "LT" + uuidv4().toString().replace(/-/gi, '').toUpperCase().slice(0, 13); - } - }, - senderName: { - type: Sequelize.STRING, - allowNull: false, - validate: { - notEmpty: { - msg: "Sender name cannot be empty" - }, - notNull: { - msg: 'Sender name is required' - } - } - }, - senderAddress: { - type: Sequelize.STRING, - allowNull: false, - validate: { - notEmpty: { - msg: "Sender address cannot be empty" - }, - notNull: { - msg: 'Sender address is required' - } - } - }, - senderPhone: { - type: Sequelize.STRING, - allowNull: true, - validate: { - notEmpty: { - msg: "Sender phone number cannot be empty" - }, - } - }, - receiverName: { - type: Sequelize.STRING, - allowNull: false, - validate: { - notEmpty: { - msg: "Receiver name cannot be empty" - }, - notNull: { - msg: 'Receiver name is required' - } - } - }, - receiverAddress: { - type: Sequelize.STRING, - allowNull: false, - validate: { - notEmpty: { - msg: "Receiver address cannot be empty" - }, - notNull: { - msg: 'Receiver address is required' - } - } - }, - receiverPhone: { - type: Sequelize.STRING, - allowNull: true, - validate: { - notEmpty: { - msg: "Receiver phone number cannot be empty" - } - } - }, - weight: { - type: Sequelize.DOUBLE, - allowNull: false, - validate: { - notEmpty: { - msg: 'Parcel weight cannot be empty' - }, - isNumeric: { - msg: 'Parcel weight must be a number' - }, - min: { - args: 0.1, - msg: 'Parcel weight must be greater than 0kg' - }, - max: { - args: 100, - msg: 'Parcel weight must be less than 100kg' - }, - notNull: { - msg: 'Parcel weight is required' - } - } - }, - price: { - type: Sequelize.DOUBLE, - allowNull: false, - validate: { - notEmpty: { - msg: 'Parcel price cannot be empty' - }, - isNumeric: { - msg: 'Parcel price must be a number' - }, - min: { - args: 0.1, - msg: 'Parcel price must be greater than 0' - }, - max: { - args: 100000, - msg: 'Parcel price must be less than 100000' - }, - notNull: { - msg: 'Parcel price is required' - } - } - }, - status: { - type: Sequelize.ENUM('Pending', 'In progress', 'Delivered'), - defaultValue: 'Pending', - validate: { - isIn: { - args: [['Pending', 'In progress', 'Delivered']], - msg: "Status must be either 'Pending', 'In progress' or 'Delivered'" - } - } - } - }); - - return Parcel; -}; \ No newline at end of file diff --git a/SwiftParcel.Web/backend/models/user.model.js b/SwiftParcel.Web/backend/models/user.model.js deleted file mode 100644 index dca4e66..0000000 --- a/SwiftParcel.Web/backend/models/user.model.js +++ /dev/null @@ -1,76 +0,0 @@ -const bcrypt = require("bcrypt"); - -module.exports = (sequelize, Sequelize) => { - const User = sequelize.define("user", { - username: { - type: Sequelize.STRING, - unique: true, - allowNull: false, - validate: { - notEmpty: { - msg: "Username cannot be empty" - }, - len: { - args: [3, 20], - msg: "Username must be between 3 and 20 characters" - }, - isAlphanumeric: { - msg: "Username must contain only letters and numbers" - }, - notNull: { - msg: 'Username is required' - } - }, - }, - password: { - type: Sequelize.STRING, - allowNull: false - }, - email: { - type: Sequelize.STRING, - unique: true, - allowNull: false, - validate: { - notEmpty: { - msg: "Email cannot be empty" - }, - isEmail: { - msg: "Email must be a valid email address" - }, - notNull: { - msg: 'Email is required' - } - } - }, - role: { - type: Sequelize.ENUM('User', 'Admin'), - defaultValue: 'User' - }, - iat: { - type: Sequelize.INTEGER, - allowNull: true, - default: null - } - }, { - defaultScope: { - attributes: { exclude: ['password'] }, - }, - scopes: { - withPassword: { - attributes: {}, - } - }, - hooks: { - beforeCreate: (user) => { - const salt = bcrypt.genSaltSync(); - user.password = bcrypt.hashSync(user.password, salt); - } - } - }); - - User.prototype.validPassword = function (password) { - return bcrypt.compareSync(password, this.password); - }; - - return User; -}; \ No newline at end of file diff --git a/SwiftParcel.Web/backend/package-lock.json b/SwiftParcel.Web/backend/package-lock.json deleted file mode 100644 index 28ba9ee..0000000 --- a/SwiftParcel.Web/backend/package-lock.json +++ /dev/null @@ -1,2749 +0,0 @@ -{ - "name": "parcel-delivery-backend", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "parcel-delivery-backend", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "bcrypt": "^5.1.0", - "cors": "^2.8.5", - "dotenv": "^16.0.3", - "express": "^4.18.1", - "jsonwebtoken": "^8.5.1", - "mysql2": "^2.3.3", - "sequelize": "^6.24.0", - "uuid": "^9.0.0" - } - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", - "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@types/debug": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", - "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" - }, - "node_modules/@types/node": { - "version": "18.8.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz", - "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==" - }, - "node_modules/@types/validator": { - "version": "13.7.7", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.7.tgz", - "integrity": "sha512-jiEw2kTUJ8Jsh4A1K4b5Pkjj9Xz6FktLLOQ36ZVLRkmxFbpTvAV2VRoKMojz8UlZxNg/2dZqzpigH4JYn1bkQg==" - }, - "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==" - }, - "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==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "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==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/agent-base/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==" - }, - "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==", - "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==" - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, - "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==" - }, - "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==" - }, - "node_modules/bcrypt": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", - "integrity": "sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==", - "hasInstallScript": true, - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.10", - "node-addon-api": "^5.0.0" - }, - "engines": { - "node": ">= 10.0.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==", - "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/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "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==", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "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==", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "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": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, - "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==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "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==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "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": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "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==", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, - "node_modules/denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", - "engines": { - "node": ">=0.10" - } - }, - "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==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/dottie": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz", - "integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg==" - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "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==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", - "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/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "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/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "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==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "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/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/generate-function": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", - "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", - "dependencies": { - "is-property": "^1.0.2" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "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": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, - "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==", - "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/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/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==" - }, - "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==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inflection": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", - "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", - "engines": [ - "node >= 0.4.0" - ] - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "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==" - }, - "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==", - "engines": { - "node": ">= 0.10" - } - }, - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" - }, - "node_modules/jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=4", - "npm": ">=1.4.28" - } - }, - "node_modules/jsonwebtoken/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==" - }, - "node_modules/jsonwebtoken/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" - }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, - "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==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "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==", - "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==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "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==", - "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==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minipass": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", - "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", - "dependencies": { - "yallist": "^4.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==", - "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==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", - "engines": { - "node": "*" - } - }, - "node_modules/moment-timezone": { - "version": "0.5.37", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.37.tgz", - "integrity": "sha512-uEDzDNFhfaywRl+vwXxffjjq1q0Vzr+fcQpQ1bU0kbzorfS7zVtZnCnGc8mhWmF39d4g4YriF6kwA75mJKE/Zg==", - "dependencies": { - "moment": ">= 2.9.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/mysql2": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz", - "integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==", - "dependencies": { - "denque": "^2.0.1", - "generate-function": "^2.3.1", - "iconv-lite": "^0.6.3", - "long": "^4.0.0", - "lru-cache": "^6.0.0", - "named-placeholders": "^1.1.2", - "seq-queue": "^0.0.5", - "sqlstring": "^2.3.2" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/mysql2/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==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/named-placeholders": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz", - "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==", - "dependencies": { - "lru-cache": "^4.1.3" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/named-placeholders/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/named-placeholders/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" - }, - "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==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-addon-api": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", - "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" - }, - "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.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": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "engines": { - "node": ">=0.10.0" - } - }, - "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": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/pg-connection-string": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", - "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" - }, - "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==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" - }, - "node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "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==", - "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/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==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/retry-as-promised": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-6.1.0.tgz", - "integrity": "sha512-Hj/jY+wFC+SB9SDlIIFWiGOHnNG0swYbGYsOj2BJ8u2HKUaobNKab0OIC0zOLYzDy0mb7A4xA5BMo4LMz5YtEA==" - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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==", - "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/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "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==", - "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/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/seq-queue": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", - "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" - }, - "node_modules/sequelize": { - "version": "6.24.0", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.24.0.tgz", - "integrity": "sha512-mPo7Q7gWkrsstjR2aw8ahkrj8RUJCozz3kedAz2B5ZIdLoQEH1z2tvaeJONI8R6RqeuyRQosx9Yn3s9yQyo6lQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/sequelize" - } - ], - "dependencies": { - "@types/debug": "^4.1.7", - "@types/validator": "^13.7.1", - "debug": "^4.3.3", - "dottie": "^2.0.2", - "inflection": "^1.13.2", - "lodash": "^4.17.21", - "moment": "^2.29.1", - "moment-timezone": "^0.5.34", - "pg-connection-string": "^2.5.0", - "retry-as-promised": "^6.1.0", - "semver": "^7.3.5", - "sequelize-pool": "^7.1.0", - "toposort-class": "^1.0.1", - "uuid": "^8.3.2", - "validator": "^13.7.0", - "wkx": "^0.5.0" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependenciesMeta": { - "ibm_db": { - "optional": true - }, - "mariadb": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "oracledb": { - "optional": true - }, - "pg": { - "optional": true - }, - "pg-hstore": { - "optional": true - }, - "snowflake-sdk": { - "optional": true - }, - "sqlite3": { - "optional": true - }, - "tedious": { - "optional": true - } - } - }, - "node_modules/sequelize-pool": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", - "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/sequelize/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/sequelize/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==" - }, - "node_modules/sequelize/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==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "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==", - "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": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "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==", - "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==" - }, - "node_modules/sqlstring": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", - "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "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==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "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==", - "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==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", - "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/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/toposort-class": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", - "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==" - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "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==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/validator": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "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==", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/wkx": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", - "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - }, - "dependencies": { - "@mapbox/node-pre-gyp": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", - "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", - "requires": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - } - }, - "@types/debug": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", - "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", - "requires": { - "@types/ms": "*" - } - }, - "@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" - }, - "@types/node": { - "version": "18.8.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz", - "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==" - }, - "@types/validator": { - "version": "13.7.7", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.7.tgz", - "integrity": "sha512-jiEw2kTUJ8Jsh4A1K4b5Pkjj9Xz6FktLLOQ36ZVLRkmxFbpTvAV2VRoKMojz8UlZxNg/2dZqzpigH4JYn1bkQg==" - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "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==", - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "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==", - "requires": { - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "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==" - }, - "are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "bcrypt": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", - "integrity": "sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==", - "requires": { - "@mapbox/node-pre-gyp": "^1.0.10", - "node-addon-api": "^5.0.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==", - "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" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" - }, - "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==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, - "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==", - "requires": { - "safe-buffer": "5.2.1" - } - }, - "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==" - }, - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, - "denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" - }, - "dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" - }, - "dottie": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz", - "integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg==" - }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "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==" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" - }, - "express": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", - "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", - "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" - } - }, - "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "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" - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" - }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "requires": { - "minipass": "^3.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "requires": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - } - }, - "generate-function": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", - "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", - "requires": { - "is-property": "^1.0.2" - } - }, - "get-intrinsic": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", - "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "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" - } - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, - "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==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "requires": { - "agent-base": "6", - "debug": "4" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "inflection": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", - "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "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==" - }, - "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==" - }, - "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==" - }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" - }, - "jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "requires": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" - }, - "dependencies": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" - }, - "lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" - }, - "lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" - }, - "lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" - }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" - }, - "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, - "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==", - "requires": { - "yallist": "^4.0.0" - } - }, - "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==", - "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==" - } - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "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==", - "requires": { - "mime-db": "1.52.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minipass": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", - "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", - "requires": { - "yallist": "^4.0.0" - } - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "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==" - }, - "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" - }, - "moment-timezone": { - "version": "0.5.37", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.37.tgz", - "integrity": "sha512-uEDzDNFhfaywRl+vwXxffjjq1q0Vzr+fcQpQ1bU0kbzorfS7zVtZnCnGc8mhWmF39d4g4YriF6kwA75mJKE/Zg==", - "requires": { - "moment": ">= 2.9.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "mysql2": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz", - "integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==", - "requires": { - "denque": "^2.0.1", - "generate-function": "^2.3.1", - "iconv-lite": "^0.6.3", - "long": "^4.0.0", - "lru-cache": "^6.0.0", - "named-placeholders": "^1.1.2", - "seq-queue": "^0.0.5", - "sqlstring": "^2.3.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==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, - "named-placeholders": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz", - "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==", - "requires": { - "lru-cache": "^4.1.3" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" - } - } - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" - }, - "node-addon-api": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", - "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" - }, - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "requires": { - "abbrev": "1" - } - }, - "npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "requires": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" - }, - "object-inspect": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" - }, - "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==", - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { - "wrappy": "1" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "pg-connection-string": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", - "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" - }, - "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==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" - }, - "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "requires": { - "side-channel": "^1.0.4" - } - }, - "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==" - }, - "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==", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "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==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "retry-as-promised": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-6.1.0.tgz", - "integrity": "sha512-Hj/jY+wFC+SB9SDlIIFWiGOHnNG0swYbGYsOj2BJ8u2HKUaobNKab0OIC0zOLYzDy0mb7A4xA5BMo4LMz5YtEA==" - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "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==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "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==", - "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": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "seq-queue": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", - "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" - }, - "sequelize": { - "version": "6.24.0", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.24.0.tgz", - "integrity": "sha512-mPo7Q7gWkrsstjR2aw8ahkrj8RUJCozz3kedAz2B5ZIdLoQEH1z2tvaeJONI8R6RqeuyRQosx9Yn3s9yQyo6lQ==", - "requires": { - "@types/debug": "^4.1.7", - "@types/validator": "^13.7.1", - "debug": "^4.3.3", - "dottie": "^2.0.2", - "inflection": "^1.13.2", - "lodash": "^4.17.21", - "moment": "^2.29.1", - "moment-timezone": "^0.5.34", - "pg-connection-string": "^2.5.0", - "retry-as-promised": "^6.1.0", - "semver": "^7.3.5", - "sequelize-pool": "^7.1.0", - "toposort-class": "^1.0.1", - "uuid": "^8.3.2", - "validator": "^13.7.0", - "wkx": "^0.5.0" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - } - } - }, - "sequelize-pool": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", - "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==" - }, - "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==", - "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": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "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==" - }, - "sqlstring": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", - "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==" - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, - "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==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "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==", - "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==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", - "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" - } - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - }, - "toposort-class": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", - "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==" - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" - }, - "uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" - }, - "validator": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.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==", - "requires": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "wkx": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", - "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", - "requires": { - "@types/node": "*" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } -} diff --git a/SwiftParcel.Web/backend/package.json b/SwiftParcel.Web/backend/package.json deleted file mode 100644 index 380b3ce..0000000 --- a/SwiftParcel.Web/backend/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "parcel-delivery-backend", - "version": "1.0.0", - "description": "Parcel delivery system backend", - "main": "index.js", - "scripts": { - "start": "node index.js" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/nojusgat/parcel-delivery-system.git" - }, - "author": "Nojus Gataveckas", - "license": "ISC", - "bugs": { - "url": "https://github.com/nojusgat/parcel-delivery-system/issues" - }, - "homepage": "https://github.com/nojusgat/parcel-delivery-system#readme", - "dependencies": { - "bcrypt": "^5.1.0", - "cors": "^2.8.5", - "dotenv": "^16.0.3", - "express": "^4.18.1", - "jsonwebtoken": "^8.5.1", - "mysql2": "^2.3.3", - "sequelize": "^6.24.0", - "uuid": "^9.0.0" - } -} diff --git a/SwiftParcel.Web/backend/routes/car.routes.js b/SwiftParcel.Web/backend/routes/car.routes.js deleted file mode 100644 index b5aac42..0000000 --- a/SwiftParcel.Web/backend/routes/car.routes.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = app => { - const cars = require("../controllers/car.controller.js"); - const parcels = require("../controllers/parcel.controller.js"); - const auth = require("../middlewares/auth.middleware"); - - var router = require("express").Router(); - - router.post("/", auth.authorization, cars.create); - router.get("/", auth.authorization, cars.findAll); - router.get("/:id", auth.authorization, cars.findOne); - router.put("/:id", auth.authorization, cars.update); - router.delete("/:id", auth.authorization, cars.delete); - router.get("/:id/parcels", auth.authorization, parcels.findAllByCars); - - app.use('/api/cars', router); -}; \ No newline at end of file diff --git a/SwiftParcel.Web/backend/routes/courier.routes.js b/SwiftParcel.Web/backend/routes/courier.routes.js deleted file mode 100644 index 6fc489b..0000000 --- a/SwiftParcel.Web/backend/routes/courier.routes.js +++ /dev/null @@ -1,17 +0,0 @@ -module.exports = app => { - const couriers = require("../controllers/courier.controller.js"); - const parcels = require("../controllers/parcel.controller.js"); - const auth = require("../middlewares/auth.middleware"); - - var router = require("express").Router(); - - router.post("/", auth.authorization, couriers.create); - router.get("/", auth.authorization, couriers.findAll); - router.get("/:id", auth.authorization, couriers.findOne); - router.put("/:id", auth.authorization, couriers.update); - router.delete("/:id", auth.authorization, couriers.delete); - - router.get("/:id/parcels", auth.authorization, parcels.findAllByCourier); - - app.use('/api/couriers', router); -}; \ No newline at end of file diff --git a/SwiftParcel.Web/backend/routes/parcel.routes.js b/SwiftParcel.Web/backend/routes/parcel.routes.js deleted file mode 100644 index 5363ce7..0000000 --- a/SwiftParcel.Web/backend/routes/parcel.routes.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = app => { - const parcels = require("../controllers/parcel.controller.js"); - const auth = require("../middlewares/auth.middleware"); - - var router = require("express").Router(); - - router.post("/", auth.authorization, parcels.create); - router.get("/", auth.authorization, parcels.findAll); - router.get("/:id", auth.authorization_check, parcels.findOne); - router.put("/:id", auth.authorization, parcels.update); - router.delete("/:id", auth.authorization, parcels.delete); - - app.use('/api/parcels', router); -}; \ No newline at end of file diff --git a/SwiftParcel.Web/backend/routes/user.routes.js b/SwiftParcel.Web/backend/routes/user.routes.js deleted file mode 100644 index 2cfc468..0000000 --- a/SwiftParcel.Web/backend/routes/user.routes.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = app => { - const users = require("../controllers/user.controller.js"); - const auth = require("../middlewares/auth.middleware"); - - var router = require("express").Router(); - var userRouter = require("express").Router(); - - router.post("/register", users.register); - router.post("/login", users.login); - router.get("/logout", auth.authorization, users.logout); - router.get("/me", auth.authorization, users.profile); - app.use('/api/auth', router); - - userRouter.get("/", auth.authorization, users.findAll); - app.use('/api/users', userRouter); -}; \ No newline at end of file diff --git a/SwiftParcel.Web/frontend/.eslintignore b/SwiftParcel.Web/frontend/.eslintignore new file mode 100644 index 0000000..a252bc5 --- /dev/null +++ b/SwiftParcel.Web/frontend/.eslintignore @@ -0,0 +1,2 @@ +src/components/* +src/pages/* diff --git a/SwiftParcel.Web/frontend/package-lock.json b/SwiftParcel.Web/frontend/package-lock.json index 52b678c..00028e4 100644 --- a/SwiftParcel.Web/frontend/package-lock.json +++ b/SwiftParcel.Web/frontend/package-lock.json @@ -11,6 +11,7 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "@types/bcrypt": "^5.0.1", "@types/google-map-react": "^2.1.9", "@types/jest": "^27.5.2", "@types/node": "^16.18.3", @@ -18,20 +19,32 @@ "@types/react-dom": "^18.0.9", "@types/react-router-dom": "^5.3.3", "axios": "^1.2.0", + "bcrypt": "^5.1.1", + "bcryptjs": "^2.4.3", + "eslint": "^8.56.0", "flowbite": "^1.5.4", - "flowbite-react": "^0.3.5", "google-map-react": "^2.2.0", + "mongoose": "^8.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^4.6.0", "react-router-dom": "^6.4.3", "react-scripts": "5.0.1", "tailwindcss": "^3.2.4", - "typescript": "^5.2.2", "web-vitals": "^2.1.4" }, "devDependencies": { - "gh-pages": "^4.0.0" + "flowbite-react": "^0.7.2", + "gh-pages": "^4.0.0", + "typescript": "^5.3.3" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "engines": { + "node": ">=0.10.0" } }, "node_modules/@adobe/css-tools": { @@ -1805,11 +1818,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", - "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.6.tgz", + "integrity": "sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==", "dependencies": { - "regenerator-runtime": "^0.13.10" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" @@ -1827,6 +1840,11 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/runtime/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, "node_modules/@babel/template": { "version": "7.18.10", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", @@ -1878,283 +1896,159 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, - "node_modules/@commitlint/config-validator": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.1.0.tgz", - "integrity": "sha512-Q1rRRSU09ngrTgeTXHq6ePJs2KrI+axPTgkNYDWSJIuS1Op4w3J30vUfSXjwn5YEJHklK3fSqWNHmBhmTR7Vdg==", - "optional": true, - "dependencies": { - "@commitlint/types": "^17.0.0", - "ajv": "^8.11.0" - }, - "engines": { - "node": ">=v14" - } - }, - "node_modules/@commitlint/config-validator/node_modules/ajv": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", - "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", - "optional": true, + "node_modules/@contentlayer/cli": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@contentlayer/cli/-/cli-0.3.4.tgz", + "integrity": "sha512-vNDwgLuhYNu+m70NZ3XK9kexKNguuxPXg7Yvzj3B34cEilQjjzSrcTY/i+AIQm9V7uT5GGshx9ukzPf+SmoszQ==", + "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/@commitlint/config-validator/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==", - "optional": true - }, - "node_modules/@commitlint/execute-rule": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.0.0.tgz", - "integrity": "sha512-nVjL/w/zuqjCqSJm8UfpNaw66V9WzuJtQvEnCrK4jDw6qKTmZB+1JQ8m6BQVZbNBcwfYdDNKnhIhqI0Rk7lgpQ==", - "optional": true, - "engines": { - "node": ">=v14" + "@contentlayer/core": "0.3.4", + "@contentlayer/utils": "0.3.4", + "clipanion": "^3.2.1", + "typanion": "^3.12.1" } }, - "node_modules/@commitlint/load": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.3.0.tgz", - "integrity": "sha512-u/pV6rCAJrCUN+HylBHLzZ4qj1Ew3+eN9GBPhNi9otGxtOfA8b+8nJSxaNbcC23Ins/kcpjGf9zPSVW7628Umw==", - "optional": true, + "node_modules/@contentlayer/client": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@contentlayer/client/-/client-0.3.4.tgz", + "integrity": "sha512-QSlLyc3y4PtdC5lFw0L4wTZUH8BQnv2nk37hNCsPAqGf+dRO7TLAzdc+2/mVIRgK+vSH+pSOzjLsQpFxxXRTZA==", + "dev": true, "dependencies": { - "@commitlint/config-validator": "^17.1.0", - "@commitlint/execute-rule": "^17.0.0", - "@commitlint/resolve-extends": "^17.3.0", - "@commitlint/types": "^17.0.0", - "@types/node": "^14.0.0", - "chalk": "^4.1.0", - "cosmiconfig": "^7.0.0", - "cosmiconfig-typescript-loader": "^4.0.0", - "lodash.isplainobject": "^4.0.6", - "lodash.merge": "^4.6.2", - "lodash.uniq": "^4.5.0", - "resolve-from": "^5.0.0", - "ts-node": "^10.8.1", - "typescript": "^4.6.4" - }, - "engines": { - "node": ">=v14" + "@contentlayer/core": "0.3.4" } }, - "node_modules/@commitlint/load/node_modules/@types/node": { - "version": "14.18.33", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.33.tgz", - "integrity": "sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==", - "optional": true - }, - "node_modules/@commitlint/load/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==", - "optional": true, + "node_modules/@contentlayer/core": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@contentlayer/core/-/core-0.3.4.tgz", + "integrity": "sha512-o68oBLwfYZ+2vtgfk1lgHxOl3LoxvRNiUfeQ8IWFWy/L4wnIkKIqLZX01zlRE5IzYM+ZMMN5V0cKQlO7DsyR9g==", + "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "@contentlayer/utils": "0.3.4", + "camel-case": "^4.1.2", + "comment-json": "^4.2.3", + "esbuild": "0.17.x || 0.18.x", + "gray-matter": "^4.0.3", + "mdx-bundler": "^9.2.1", + "rehype-stringify": "^9.0.3", + "remark-frontmatter": "^4.0.1", + "remark-parse": "^10.0.2", + "remark-rehype": "^10.1.0", + "source-map-support": "^0.5.21", + "type-fest": "^3.12.0", + "unified": "^10.1.2" + }, + "peerDependencies": { + "esbuild": "0.17.x || 0.18.x", + "markdown-wasm": "1.x" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependenciesMeta": { + "esbuild": { + "optional": true + }, + "markdown-wasm": { + "optional": true + } } }, - "node_modules/@commitlint/load/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "optional": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "node_modules/@contentlayer/core/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@commitlint/load/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==", - "optional": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@commitlint/load/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==", - "optional": true - }, - "node_modules/@commitlint/load/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==", - "optional": true, - "engines": { - "node": ">=8" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@commitlint/load/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==", - "optional": true, + "node_modules/@contentlayer/source-files": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@contentlayer/source-files/-/source-files-0.3.4.tgz", + "integrity": "sha512-4njyn0OFPu7WY4tAjMxiJgWOKeiHuBOGdQ36EYE03iij/pPPRbiWbL+cmLccYXUFEW58mDwpqROZZm6pnxjRDQ==", + "dev": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@commitlint/load/node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "optional": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" + "@contentlayer/core": "0.3.4", + "@contentlayer/utils": "0.3.4", + "chokidar": "^3.5.3", + "fast-glob": "^3.2.12", + "gray-matter": "^4.0.3", + "imagescript": "^1.2.16", + "micromatch": "^4.0.5", + "ts-pattern": "^4.3.0", + "unified": "^10.1.2", + "yaml": "^2.3.1", + "zod": "^3.21.4" } }, - "node_modules/@commitlint/resolve-extends": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.3.0.tgz", - "integrity": "sha512-Lf3JufJlc5yVEtJWC8o4IAZaB8FQAUaVlhlAHRACd0TTFizV2Lk2VH70et23KgvbQNf7kQzHs/2B4QZalBv6Cg==", - "optional": true, - "dependencies": { - "@commitlint/config-validator": "^17.1.0", - "@commitlint/types": "^17.0.0", - "import-fresh": "^3.0.0", - "lodash.mergewith": "^4.6.2", - "resolve-from": "^5.0.0", - "resolve-global": "^1.0.0" - }, + "node_modules/@contentlayer/source-files/node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true, "engines": { - "node": ">=v14" + "node": ">= 14" } }, - "node_modules/@commitlint/types": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.0.0.tgz", - "integrity": "sha512-hBAw6U+SkAT5h47zDMeOu3HSiD0SODw4Aq7rRNh1ceUmL7GyLKYhPbUvlRWqZ65XjBLPHZhFyQlRaPNz8qvUyQ==", - "optional": true, + "node_modules/@contentlayer/source-remote-files": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@contentlayer/source-remote-files/-/source-remote-files-0.3.4.tgz", + "integrity": "sha512-cyiv4sNUySZvR0uAKlM+kSAELzNd2h2QT1R2e41dRKbwOUVxeLfmGiLugr0aVac6Q3xYcD99dbHyR1xWPV+w9w==", + "dev": true, "dependencies": { - "chalk": "^4.1.0" - }, - "engines": { - "node": ">=v14" + "@contentlayer/core": "0.3.4", + "@contentlayer/source-files": "0.3.4", + "@contentlayer/utils": "0.3.4" } }, - "node_modules/@commitlint/types/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==", - "optional": true, + "node_modules/@contentlayer/utils": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@contentlayer/utils/-/utils-0.3.4.tgz", + "integrity": "sha512-ZWWOhbUWYQ2QHoLIlcUnEo7X4ZbwcyFPuzVQWWMkK43BxCveyQtZwBIzfyx54sqVzi0GUmKP8bHzsLQT0QxaLQ==", + "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "@effect-ts/core": "^0.60.5", + "@effect-ts/otel": "^0.15.1", + "@effect-ts/otel-exporter-trace-otlp-grpc": "^0.15.1", + "@effect-ts/otel-sdk-trace-node": "^0.15.1", + "@js-temporal/polyfill": "^0.4.4", + "@opentelemetry/api": "^1.4.1", + "@opentelemetry/core": "^1.13.0", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.39.1", + "@opentelemetry/resources": "^1.13.0", + "@opentelemetry/sdk-trace-base": "^1.13.0", + "@opentelemetry/sdk-trace-node": "^1.13.0", + "@opentelemetry/semantic-conventions": "^1.13.0", + "chokidar": "^3.5.3", + "hash-wasm": "^4.9.0", + "inflection": "^2.0.1", + "memfs": "^3.5.1", + "oo-ascii-tree": "^1.84.0", + "ts-pattern": "^4.3.0", + "type-fest": "^3.12.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependenciesMeta": { + "@effect-ts/core": { + "optional": true + }, + "@effect-ts/otel": { + "optional": true + }, + "@effect-ts/otel-node": { + "optional": true + } } }, - "node_modules/@commitlint/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "optional": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "node_modules/@contentlayer/utils/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@commitlint/types/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==", - "optional": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@commitlint/types/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==", - "optional": true - }, - "node_modules/@commitlint/types/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==", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@commitlint/types/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==", - "optional": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "optional": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "optional": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@csstools/normalize.css": { @@ -2428,15 +2322,475 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@effect-ts/core": { + "version": "0.60.5", + "resolved": "https://registry.npmjs.org/@effect-ts/core/-/core-0.60.5.tgz", + "integrity": "sha512-qi1WrtJA90XLMnj2hnUszW9Sx4dXP03ZJtCc5DiUBIOhF4Vw7plfb65/bdBySPoC9s7zy995TdUX1XBSxUkl5w==", + "dev": true, + "dependencies": { + "@effect-ts/system": "^0.57.5" + } + }, + "node_modules/@effect-ts/otel": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@effect-ts/otel/-/otel-0.15.1.tgz", + "integrity": "sha512-AmZJHl7t0+Peh7Yb2+hqn6r9+rd9/UfeA4AMV9h0YGTdOyouyFfD3wzWlxnAUzAQ4Lrod4kC7Noruret4EpqpA==", + "dev": true, + "peerDependencies": { + "@effect-ts/core": "^0.60.2", + "@opentelemetry/api": "^1.4.0", + "@opentelemetry/core": "^1.13.0", + "@opentelemetry/sdk-trace-base": "^1.13.0" + } + }, + "node_modules/@effect-ts/otel-exporter-trace-otlp-grpc": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@effect-ts/otel-exporter-trace-otlp-grpc/-/otel-exporter-trace-otlp-grpc-0.15.1.tgz", + "integrity": "sha512-47gAg0O2pW5Jlo86jfzjdkwL5a7Bzb+Kj5WTmdu4CxYRfWn9ytKjuuYIfsNDW8neuhdKzn+P5wCddgEh0glYyQ==", + "dev": true, + "dependencies": { + "@effect-ts/otel": "^0.15.1" + }, + "peerDependencies": { + "@effect-ts/core": "^0.60.2", + "@opentelemetry/api": "^1.4.0", + "@opentelemetry/core": "^1.13.0", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.39.0", + "@opentelemetry/sdk-trace-base": "^1.13.0" + } + }, + "node_modules/@effect-ts/otel-sdk-trace-node": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@effect-ts/otel-sdk-trace-node/-/otel-sdk-trace-node-0.15.1.tgz", + "integrity": "sha512-a2sF0ylmn8xOJs8fNeT/spJ1gUcsksAJCALxo9WOfuTCMtTwMVtVhCKEPEeQoL7wFqU+JgPkVdP91+FJ/Rkeow==", + "dev": true, + "dependencies": { + "@effect-ts/otel": "^0.15.1" + }, + "peerDependencies": { + "@effect-ts/core": "^0.60.2", + "@opentelemetry/api": "^1.4.0", + "@opentelemetry/core": "^1.13.0", + "@opentelemetry/sdk-trace-base": "^1.13.0", + "@opentelemetry/sdk-trace-node": "^1.13.0" + } + }, + "node_modules/@effect-ts/system": { + "version": "0.57.5", + "resolved": "https://registry.npmjs.org/@effect-ts/system/-/system-0.57.5.tgz", + "integrity": "sha512-/crHGujo0xnuHIYNc1VgP0HGJGFSoSqq88JFXe6FmFyXPpWt8Xu39LyLg7rchsxfXFeEdA9CrIZvLV5eswXV5g==", + "dev": true + }, + "node_modules/@esbuild-plugins/node-resolve": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-resolve/-/node-resolve-0.1.4.tgz", + "integrity": "sha512-haFQ0qhxEpqtWWY0kx1Y5oE3sMyO1PcoSiWEPrAw6tm/ZOOLXjSs6Q+v1v9eyuVF0nNt50YEvrcrvENmyoMv5g==", + "dev": true, + "dependencies": { + "@types/resolve": "^1.17.1", + "debug": "^4.3.1", + "escape-string-regexp": "^4.0.0", + "resolve": "^1.19.0" + }, + "peerDependencies": { + "esbuild": "*" + } + }, + "node_modules/@esbuild-plugins/node-resolve/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", + "espree": "^9.6.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -2456,9 +2810,9 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dependencies": { "type-fest": "^0.20.2" }, @@ -2491,44 +2845,73 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@fal-works/esbuild-plugin-global-externals": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz", + "integrity": "sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==", + "dev": true + }, "node_modules/@floating-ui/core": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.0.2.tgz", - "integrity": "sha512-Skfy0YS3NJ5nV9us0uuPN0HDk1Q4edljaOhRBJGDWs9EBa7ZVMYBHRFlhLvvmwEoaIM9BlH6QJFn9/uZg0bACg==" + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.2.tgz", + "integrity": "sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==", + "dev": true, + "dependencies": { + "@floating-ui/utils": "^0.1.3" + } }, "node_modules/@floating-ui/dom": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.0.6.tgz", - "integrity": "sha512-kt/tg1oip9OAH1xjCTcx1OpcUpu9rjDw3GKJ/rEhUqhO7QyJWfrHU0DpLTNsH67+JyFL5Kv9X1utsXwKFVtyEQ==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", + "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", + "dev": true, "dependencies": { - "@floating-ui/core": "^1.0.2" + "@floating-ui/core": "^1.4.2", + "@floating-ui/utils": "^0.1.3" } }, - "node_modules/@floating-ui/react-dom": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-1.0.1.tgz", - "integrity": "sha512-UW0t1Gi8ikbDRr8cQPVcqIDMBwUEENe5V4wlHWdrJ5egFnRQFBV9JirauTBFI6S8sM1qFUC1i+qa3g87E6CLTw==", + "node_modules/@floating-ui/react": { + "version": "0.26.4", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.4.tgz", + "integrity": "sha512-pRiEz+SiPyfTcckAtLkEf3KJ/sUbB4X4fWMcDm27HT2kfAq+dH+hMc2VoOkNaGpDE35a2PKo688ugWeHaToL3g==", + "dev": true, "dependencies": { - "@floating-ui/dom": "^1.0.5" + "@floating-ui/react-dom": "^2.0.3", + "@floating-ui/utils": "^0.1.5", + "tabbable": "^6.0.1" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, - "node_modules/@floating-ui/react-dom-interactions": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom-interactions/-/react-dom-interactions-0.9.3.tgz", - "integrity": "sha512-oHwFLxySRtmhgwg7ZdWswvDDi+ld4mEtxu6ngOd7mRC5L1Rk6adjSfOBOHDxea+ItAWmds8m6A725sn1HQtUyQ==", + "node_modules/@floating-ui/react-dom": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.4.tgz", + "integrity": "sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==", + "dev": true, "dependencies": { - "@floating-ui/react-dom": "^1.0.0", - "aria-hidden": "^1.1.3" + "@floating-ui/dom": "^1.5.1" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, + "node_modules/@floating-ui/utils": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", + "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==", + "dev": true + }, "node_modules/@googlemaps/js-api-loader": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.15.1.tgz", @@ -2537,12 +2920,84 @@ "fast-deep-equal": "^3.1.3" } }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.13", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.13.tgz", + "integrity": "sha512-OEZZu9v9AA+7/tghMDE8o5DAMD5THVnwSqDWuh7PPYO5287rTyqy0xEHT6/e4pbqSrhyLPdQFsam4TwFQVVIIw==", + "dev": true, + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", + "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", + "dev": true, + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.4", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@grpc/proto-loader/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "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.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@grpc/proto-loader/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", - "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" }, @@ -2563,9 +3018,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==" }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -3294,16 +3749,130 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@js-temporal/polyfill": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@js-temporal/polyfill/-/polyfill-0.4.4.tgz", + "integrity": "sha512-2X6bvghJ/JAoZO52lbgyAPFj8uCflhTo2g7nkFzEQdXd/D8rEeD4HtmTEpmtGCva260fcd66YNXBOYdnmHqSOg==", + "dev": true, + "dependencies": { + "jsbi": "^4.3.0", + "tslib": "^2.4.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, "node_modules/@mapbox/point-geometry": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==" }, + "node_modules/@mdx-js/esbuild": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@mdx-js/esbuild/-/esbuild-2.3.0.tgz", + "integrity": "sha512-r/vsqsM0E+U4Wr0DK+0EfmABE/eg+8ITW4DjvYdh3ve/tK2safaqHArNnaqbOk1DjYGrhxtoXoGaM3BY8fGBTA==", + "dev": true, + "dependencies": { + "@mdx-js/mdx": "^2.0.0", + "node-fetch": "^3.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "esbuild": ">=0.11.0" + } + }, + "node_modules/@mdx-js/esbuild/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/@mdx-js/mdx": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-2.3.0.tgz", + "integrity": "sha512-jLuwRlz8DQfQNiUCJR50Y09CGPq3fLtmtUQfVrj79E0JWu3dvsVcxVIcfhR5h0iXu+/z++zDrYeiJqifRynJkA==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/mdx": "^2.0.0", + "estree-util-build-jsx": "^2.0.0", + "estree-util-is-identifier-name": "^2.0.0", + "estree-util-to-js": "^1.1.0", + "estree-walker": "^3.0.0", + "hast-util-to-estree": "^2.0.0", + "markdown-extensions": "^1.0.0", + "periscopic": "^3.0.0", + "remark-mdx": "^2.0.0", + "remark-parse": "^10.0.0", + "remark-rehype": "^10.0.0", + "unified": "^10.0.0", + "unist-util-position-from-estree": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/mdx/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.1.tgz", + "integrity": "sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -3364,6 +3933,495 @@ "node": ">= 8" } }, + "node_modules/@opentelemetry/api": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", + "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.39.1.tgz", + "integrity": "sha512-9BJ8lMcOzEN0lu+Qji801y707oFO4xT3db6cosPvl+k7ItUHKN5ofWqtSbM9gbt1H4JJ/4/2TVrqI9Rq7hNv6Q==", + "dev": true, + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/context-async-hooks": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.19.0.tgz", + "integrity": "sha512-0i1ECOc9daKK3rjUgDDXf0GDD5XfCou5lXnt2DALIc2qKoruPPcesobNKE54laSVUWnC3jX26RzuOa31g0V32A==", + "dev": true, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.8.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.19.0.tgz", + "integrity": "sha512-w42AukJh3TP8R0IZZOVJVM/kMWu8g+lm4LzT70WtuKqhwq7KVhcDzZZuZinWZa6TtQCl7Smt2wolEYzpHabOgw==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.19.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.8.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.39.1.tgz", + "integrity": "sha512-l5RhLKx6U+yuLhMrtgavTDthX50E1mZM3/SSySC7OPZiArFHV/b/9x9jxAzrOgIQUDxyj4N0V9aLKSA2t7Qzxg==", + "dev": true, + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.13.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.39.1", + "@opentelemetry/otlp-transformer": "0.39.1", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/sdk-trace-base": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.13.0.tgz", + "integrity": "sha512-moTiQtc0uPR1hQLt6gLDJH9IIkeBhgRb71OKjNHZPE1VF45fHtD6nBDi5J/DkTHTwYP5X3kBJLa3xN7ub6J4eg==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.39.1.tgz", + "integrity": "sha512-Pv5X8fbi6jD/RJBePyn7MnCSuE6MbPB6dl+7YYBWJ5RcMGYMwvLXjd4h2jWsPV2TSUg38H/RoSP0aXvQ06Y7iw==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.39.1.tgz", + "integrity": "sha512-u3ErFRQqQFKjjIMuwLWxz/tLPYInfmiAmSy//fGSCzCh2ZdJgqQjMOAxBgqFtCF2xFL+OmMhyuC2ThMzceGRWA==", + "dev": true, + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.13.0", + "@opentelemetry/otlp-exporter-base": "0.39.1", + "protobufjs": "^7.2.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.39.1.tgz", + "integrity": "sha512-0hgVnXXz5efI382B/24NxD4b6Zxlh7nxCdJkxkdmQMbn0yRiwoq/ZT+QG8eUL6JNzsBAV1WJlF5aJNsL8skHvw==", + "dev": true, + "dependencies": { + "@opentelemetry/api-logs": "0.39.1", + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/sdk-logs": "0.39.1", + "@opentelemetry/sdk-metrics": "1.13.0", + "@opentelemetry/sdk-trace-base": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.13.0.tgz", + "integrity": "sha512-moTiQtc0uPR1hQLt6gLDJH9IIkeBhgRb71OKjNHZPE1VF45fHtD6nBDi5J/DkTHTwYP5X3kBJLa3xN7ub6J4eg==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/propagator-b3": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.19.0.tgz", + "integrity": "sha512-v7y5IBOKBm0vP3yf0DHzlw4L2gL6tZ0KeeMTaxfO5IuomMffDbrGWcvYFp0Dt4LdZctTSK523rVLBB9FBHBciQ==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.19.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.8.0" + } + }, + "node_modules/@opentelemetry/propagator-jaeger": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.19.0.tgz", + "integrity": "sha512-dedkOoTzKg+nYoLWCMp0Im+wo+XkTRW6aXhi8VQRtMW/9SNJGOllCJSu8llToLxMDF0+6zu7OCrKkevAof2tew==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.19.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.8.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.19.0.tgz", + "integrity": "sha512-RgxvKuuMOf7nctOeOvpDjt2BpZvZGr9Y0vf7eGtY5XYZPkh2p7e2qub1S2IArdBMf9kEbz0SfycqCviOu9isqg==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.19.0", + "@opentelemetry/semantic-conventions": "1.19.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.8.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.39.1.tgz", + "integrity": "sha512-/gmgKfZ1ZVFporKuwsewqIyvaUIGpv76JZ7lBpHQQPb37IMpaXO6pdqFI4ebHAWfNIm3akMyhmdtzivcgF3lgw==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.5.0", + "@opentelemetry/api-logs": ">=0.38.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.13.0.tgz", + "integrity": "sha512-MOjZX6AnSOqLliCcZUrb+DQKjAWXBiGeICGbHAGe5w0BB18PJIeIo995lO5JSaFfHpmUMgJButTPfJJD27W3Vg==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0", + "lodash.merge": "4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dev": true, + "dependencies": { + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.5.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.19.0.tgz", + "integrity": "sha512-+IRvUm+huJn2KqfFW3yW/cjvRwJ8Q7FzYHoUNx5Fr0Lws0LxjMJG1uVB8HDpLwm7mg5XXH2M5MF+0jj5cM8BpQ==", + "dev": true, + "dependencies": { + "@opentelemetry/core": "1.19.0", + "@opentelemetry/resources": "1.19.0", + "@opentelemetry/semantic-conventions": "1.19.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.8.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-node": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.19.0.tgz", + "integrity": "sha512-TCiEq/cUjM15RFqBRwWomTVbOqzndWL4ILa7ZCu0zbjU1/XY6AgHkgrgAc7vGP6TjRqH4Xryuglol8tcIfbBUQ==", + "dev": true, + "dependencies": { + "@opentelemetry/context-async-hooks": "1.19.0", + "@opentelemetry/core": "1.19.0", + "@opentelemetry/propagator-b3": "1.19.0", + "@opentelemetry/propagator-jaeger": "1.19.0", + "@opentelemetry/sdk-trace-base": "1.19.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.8.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.19.0.tgz", + "integrity": "sha512-14jRpC8f5c0gPSwoZ7SbEJni1PqI+AhAE8m1bMz6v+RPM4OlP1PT2UHBJj5Qh/ALLPjhVU/aZUK3YyjTUqqQVg==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.9", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.9.tgz", @@ -3422,6 +4480,70 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "dev": true + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "dev": true + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dev": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "dev": true + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "dev": true + }, "node_modules/@remix-run/router": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.3.tgz", @@ -3976,29 +5098,14 @@ "node": ">=10.13.0" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "optional": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "optional": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "optional": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "optional": true + "node_modules/@types/acorn": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", + "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } }, "node_modules/@types/aria-query": { "version": "4.2.2", @@ -4042,6 +5149,14 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/bcrypt": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.1.tgz", + "integrity": "sha512-dIIrEsLV1/v0AUNI8oHMaRRTSeVjoy5ID8oclJavtPj8CwPJoD1eFoNXEypuu6k091brEzBeOo3LlxeAH9zRZg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -4076,6 +5191,15 @@ "@types/node": "*" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/eslint": { "version": "8.4.10", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", @@ -4099,6 +5223,15 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.3.tgz", + "integrity": "sha512-pvQ+TKeRHeiUGRhvYwRrQ/ISnohKkSJR14fT2yqyZ4e9K5vqc7hrtY2Y1Dw0ZwAzQ6DQsxsaCUuSIIi8v0Cq6w==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/@types/express": { "version": "4.17.14", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", @@ -4136,6 +5269,15 @@ "@types/node": "*" } }, + "node_modules/@types/hast": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.8.tgz", + "integrity": "sha512-aMIqAlFd2wTIDZuvLbhUT+TGvMxrNC8ECUIVtH6xxy0sQLs3iu6NO8Kp/VT5je7i5ufnebXzdV1dNDMnvaH6IQ==", + "dev": true, + "dependencies": { + "@types/unist": "^2" + } + }, "node_modules/@types/history": { "version": "4.7.11", "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", @@ -4194,11 +5336,32 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "dev": true, + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@types/mdx": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.10.tgz", + "integrity": "sha512-Rllzc5KHk0Al5/WANwgSPl1/CwjqCy+AZrGd78zuK+jO9aDM6ffblZ+zIjgPNAaEBmlO0RYDvLNh7wD0zKVgEg==", + "dev": true + }, "node_modules/@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true + }, "node_modules/@types/node": { "version": "16.18.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.3.tgz", @@ -4209,6 +5372,12 @@ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, + "node_modules/@types/parse5": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz", + "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==", + "dev": true + }, "node_modules/@types/prettier": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", @@ -4337,6 +5506,26 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" }, + "node_modules/@types/unist": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", + "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==", + "dev": true + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.2.tgz", + "integrity": "sha512-uNv6b/uGRLlCVmelat2rA8bcVd3k/42mV2EmjhPh6JLkd35T5bgwR/t6xy7a9MWhd9sixIeBUzhBenvk3NO+DQ==" + }, + "node_modules/@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "dependencies": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, "node_modules/@types/ws": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", @@ -4575,6 +5764,11 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -4721,6 +5915,11 @@ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" }, + "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==" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -4734,9 +5933,9 @@ } }, "node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "bin": { "acorn": "bin/acorn" }, @@ -4924,6 +6123,18 @@ "ansi-html": "bin/ansi-html" } }, + "node_modules/ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha512-ewaIr5y+9CUTGFwZfpECUbFlGcC0GCw1oqR9RI6h1gQCd9Aj2GxSckCnPsVJnmfMZbwFYE+leZGASgkWl06Jow==", + "dev": true, + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -4943,6 +6154,15 @@ "node": ">=4" } }, + "node_modules/ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -4955,6 +6175,23 @@ "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==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -4968,26 +6205,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/aria-hidden": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.2.tgz", - "integrity": "sha512-6y/ogyDTk/7YAe91T3E2PR1ALVKyM2QbTio5HwM+N1Q6CMlCKhvClyIjkckBswa0f2xJhjsfzIGa1yVSe1UMVA==", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", - "react": "^16.9.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/aria-query": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", @@ -5023,6 +6240,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -5114,6 +6337,15 @@ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" }, + "node_modules/astring": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz", + "integrity": "sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==", + "dev": true, + "bin": { + "astring": "bin/astring" + } + }, "node_modules/async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", @@ -5132,6 +6364,15 @@ "node": ">= 4.0.0" } }, + "node_modules/autolinker": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.28.1.tgz", + "integrity": "sha512-zQAFO1Dlsn69eXaO6+7YZc+v84aquQKbwpzCE3L0stj56ERn9hutFxPopViLjo9G+rWwjozRhgS5KJ25Xy19cQ==", + "dev": true, + "dependencies": { + "gulp-header": "^1.7.1" + } + }, "node_modules/autoprefixer": { "version": "10.4.13", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", @@ -5211,6 +6452,12 @@ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==" }, + "node_modules/b4a": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", + "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==", + "dev": true + }, "node_modules/babel-jest": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", @@ -5491,6 +6738,16 @@ "babel-plugin-transform-react-remove-prop-types": "^0.4.24" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -5500,6 +6757,7 @@ "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", @@ -5520,6 +6778,24 @@ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + }, "node_modules/bfj": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", @@ -5554,6 +6830,7 @@ "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", @@ -5696,10 +6973,19 @@ "node-int64": "^0.4.0" } }, + "node_modules/bson": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.2.0.tgz", + "integrity": "sha512-ID1cI+7bazPDyL9wYy9GaQ8gEEohWvcUl/Yf0dIdutJxnmInEEyCsb4awy/OiBfall7zBA179Pahi3vCdFze3Q==", + "engines": { + "node": ">=16.20.1" + } + }, "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", @@ -5743,14 +7029,6 @@ "node": ">= 0.8" } }, - "node_modules/cachedir": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", - "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", - "engines": { - "node": ">=6" - } - }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -5833,6 +7111,16 @@ "node": ">=4" } }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -5854,10 +7142,45 @@ "node": ">=10" } }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, "node_modules/check-types": { "version": "11.2.2", @@ -5901,6 +7224,14 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "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", @@ -5925,7 +7256,8 @@ "node_modules/classnames": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", - "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==", + "dev": true }, "node_modules/clean-css": { "version": "5.3.1", @@ -5946,34 +7278,16 @@ "node": ">=0.10.0" } }, - "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==", + "node_modules/clipanion": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/clipanion/-/clipanion-3.2.1.tgz", + "integrity": "sha512-dYFdjLb7y1ajfxQopN05mylEpK9ZX0sO1/RfMXdfmwjlIsPkbh4p7A682x++zFPLDCo1x3p82dtljHf5cW2LKA==", + "dev": true, "dependencies": { - "restore-cursor": "^3.1.0" + "typanion": "^3.8.0" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", - "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==", - "engines": { - "node": ">= 10" + "peerDependencies": { + "typanion": "*" } }, "node_modules/cliui": { @@ -5986,14 +7300,6 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "engines": { - "node": ">=0.8" - } - }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -6016,11 +7322,38 @@ "node": ">= 4.0" } }, + "node_modules/coffee-script": { + "version": "1.12.7", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", + "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==", + "deprecated": "CoffeeScript on NPM has moved to \"coffeescript\" (no hyphen)", + "dev": true, + "bin": { + "cake": "bin/cake", + "coffee": "bin/coffee" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/collect-v8-coverage": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -6034,6 +7367,42 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "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==", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/color/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/color/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/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -6055,6 +7424,16 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", @@ -6063,54 +7442,22 @@ "node": ">= 12" } }, - "node_modules/commitizen": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-4.2.5.tgz", - "integrity": "sha512-9sXju8Qrz1B4Tw7kC5KhnvwYQN88qs2zbiB8oyMsnXZyJ24PPGiNM3nHr73d32dnE3i8VJEXddBFIbOgYSEXtQ==", - "dependencies": { - "cachedir": "2.3.0", - "cz-conventional-changelog": "3.3.0", - "dedent": "0.7.0", - "detect-indent": "6.1.0", - "find-node-modules": "^2.1.2", - "find-root": "1.1.0", - "fs-extra": "9.1.0", - "glob": "7.2.3", - "inquirer": "8.2.4", - "is-utf8": "^0.2.1", - "lodash": "4.17.21", - "minimist": "1.2.6", - "strip-bom": "4.0.0", - "strip-json-comments": "3.1.1" - }, - "bin": { - "commitizen": "bin/commitizen", - "cz": "bin/git-cz", - "git-cz": "bin/git-cz" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/commitizen/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "node_modules/comment-json": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.3.tgz", + "integrity": "sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==", + "dev": true, "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", + "esprima": "^4.0.1", + "has-own-prop": "^2.0.0", + "repeat-string": "^1.6.1" }, "engines": { - "node": ">=10" + "node": ">= 6" } }, - "node_modules/commitizen/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/common-path-prefix": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", @@ -6180,6 +7527,69 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "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/concat-stream/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/concat-stream/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/concat-with-sourcemaps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", + "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", + "dev": true, + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/concat-with-sourcemaps/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/confusing-browser-globals": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", @@ -6193,6 +7603,11 @@ "node": ">=0.8" } }, + "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": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -6212,10 +7627,26 @@ "node": ">= 0.6" } }, - "node_modules/conventional-commit-types": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/conventional-commit-types/-/conventional-commit-types-3.0.0.tgz", - "integrity": "sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg==" + "node_modules/contentlayer": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/contentlayer/-/contentlayer-0.3.4.tgz", + "integrity": "sha512-FYDdTUFaN4yqep0waswrhcXjmMJnPD5iXDTtxcUCGdklfuIrXM2xLx51xl748cHmGA6IsC+27YZFxU6Ym13QIA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@contentlayer/cli": "0.3.4", + "@contentlayer/client": "0.3.4", + "@contentlayer/core": "0.3.4", + "@contentlayer/source-files": "0.3.4", + "@contentlayer/source-remote-files": "0.3.4", + "@contentlayer/utils": "0.3.4" + }, + "bin": { + "contentlayer": "bin/cli.cjs" + }, + "engines": { + "node": ">=14.18" + } }, "node_modules/convert-source-map": { "version": "1.9.0", @@ -6287,28 +7718,6 @@ "node": ">=10" } }, - "node_modules/cosmiconfig-typescript-loader": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.2.0.tgz", - "integrity": "sha512-NkANeMnaHrlaSSlpKGyvn2R4rqUDeE/9E5YHx+b4nwo0R8dZyAqcih8/gxpCZvqWP9Vf6xuLpMSzSgdVEIM78g==", - "optional": true, - "engines": { - "node": ">=12", - "npm": ">=6" - }, - "peerDependencies": { - "@types/node": "*", - "cosmiconfig": ">=7", - "ts-node": ">=10", - "typescript": ">=3" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "optional": true - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -6719,30 +8128,20 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, - "node_modules/cz-conventional-changelog": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz", - "integrity": "sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==", - "dependencies": { - "chalk": "^2.4.1", - "commitizen": "^4.0.3", - "conventional-commit-types": "^3.0.0", - "lodash.map": "^4.5.1", - "longest": "^2.0.1", - "word-wrap": "^1.0.3" - }, - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@commitlint/load": ">6.1.1" - } - }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, "node_modules/data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -6759,7 +8158,8 @@ "node_modules/debounce": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", - "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "dev": true }, "node_modules/debug": { "version": "4.3.4", @@ -6782,6 +8182,34 @@ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.2.tgz", "integrity": "sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA==" }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dev": true, + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -6817,6 +8245,15 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -6841,17 +8278,6 @@ "node": ">= 10" } }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -6891,6 +8317,11 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -6899,6 +8330,15 @@ "node": ">= 0.8" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -6908,18 +8348,10 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", "engines": { "node": ">=8" } @@ -6982,16 +8414,38 @@ "node": ">=0.8.0" } }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diacritics-map": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz", + "integrity": "sha512-3omnDTYrGigU0i4cJjvaKwD52B8aoqyX/NEIkukFFkogBemsIbhSa1O414fpTp5nuszJG6lvQ5vBvDVNCbSsaQ==", + "dev": true, + "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/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "optional": true, + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true, "engines": { "node": ">=0.3.1" } @@ -7160,7 +8614,8 @@ "node_modules/easy-bem": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/easy-bem/-/easy-bem-1.1.1.tgz", - "integrity": "sha512-GJRqdiy2h+EXy6a8E6R+ubmqUM08BK0FWNq41k24fup6045biQ8NXxoXimiwegMQvFFV3t1emADdGNL1TlS61A==" + "integrity": "sha512-GJRqdiy2h+EXy6a8E6R+ubmqUM08BK0FWNq41k24fup6045biQ8NXxoXimiwegMQvFFV3t1emADdGNL1TlS61A==", + "dev": true }, "node_modules/ee-first": { "version": "1.1.1", @@ -7224,6 +8679,15 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.11.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.11.0.tgz", @@ -7354,6 +8818,43 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -7453,48 +8954,47 @@ } }, "node_modules/eslint": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz", - "integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==", - "dependencies": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.15.0", - "grapheme-splitter": "^1.0.4", + "globals": "^13.19.0", + "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -7789,15 +9289,18 @@ } }, "node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-utils": { @@ -7826,11 +9329,14 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-webpack-plugin": { @@ -8002,9 +9508,9 @@ } }, "node_modules/eslint/node_modules/globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dependencies": { "type-fest": "^0.20.2" }, @@ -8057,13 +9563,13 @@ } }, "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -8085,9 +9591,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dependencies": { "estraverse": "^5.1.0" }, @@ -8114,6 +9620,94 @@ "node": ">=4.0" } }, + "node_modules/estree-util-attach-comments": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-2.1.1.tgz", + "integrity": "sha512-+5Ba/xGGS6mnwFbXIuQiDPTbuTxuMCooq3arVv7gPZtYpjp+VXH/NkHAP35OOefPhNG/UGqU3vt/LTABwcHX0w==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-2.2.2.tgz", + "integrity": "sha512-m56vOXcOBuaF+Igpb9OPAy7f9w9OIkb5yhjsZuaPm7HoGi4oTOQi0h2+yZ+AtKklYFZ+rPC4n0wYCJCEU1ONqg==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "estree-util-is-identifier-name": "^2.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-2.1.0.tgz", + "integrity": "sha512-bEN9VHRyXAUOjkKVQVvArFym08BTWB0aJPppZZr0UNyAqWsLaVfAqP7hbaTJjzHifmB5ebnR8Wm7r7yGN/HonQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-1.2.0.tgz", + "integrity": "sha512-IzU74r1PK5IMMGZXUVZbmiu4A1uhiPgW5hm1GjcOfr4ZzHaMPpLNJjR7HjXiIOzi25nZDrgFTobHTkV5Q6ITjA==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-value-to-estree": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-1.3.0.tgz", + "integrity": "sha512-Y+ughcF9jSUJvncXwqRageavjrNPAI+1M/L3BI3PyLp1nmgYTGUXU6t5z1Y7OWuThoDdhPME07bQU+d5LxdJqw==", + "dev": true, + "dependencies": { + "is-plain-obj": "^3.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/estree-util-visit": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-1.2.1.tgz", + "integrity": "sha512-xbgqcrkIVbIG+lI/gzbvd9SGTJL4zqJKBFttUl5pP27KhAjtMKbX/mQXJ7qgyXpMgVy/zvpm0xoQQaGL8OloOw==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/estree-walker": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", @@ -8178,17 +9772,85 @@ "node": ">= 0.8.0" } }, - "node_modules/expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "node_modules/expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha512-AFASGfIlnIbkKPQwX1yHaDjFvh/1gyKJODme52V6IORh69uEYgZp0o9C+qsIGNVEiuuhQU0CSSl++Rlegg1qvA==", + "dev": true, + "dependencies": { + "fill-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-range/node_modules/fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, + "dependencies": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-range/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/expand-range/node_modules/is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-range/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-range/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, "dependencies": { - "homedir-polyfill": "^1.0.1" + "is-buffer": "^1.1.5" }, "engines": { "node": ">=0.10.0" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/expect": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", @@ -8262,25 +9924,19 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "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==", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } + "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/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==", + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "is-extendable": "^0.1.0" }, "engines": { "node": ">=0.10.0" @@ -8291,6 +9947,12 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", @@ -8335,6 +9997,19 @@ "reusify": "^1.0.4" } }, + "node_modules/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "dev": true, + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/faye-websocket": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", @@ -8354,18 +10029,27 @@ "bser": "2.1.1" } }, - "node_modules/figures": { + "node_modules/fetch-blob": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], "dependencies": { - "escape-string-regexp": "^1.0.5" + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^12.20 || >= 14.13" } }, "node_modules/file-entry-cache": { @@ -8516,20 +10200,6 @@ "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, - "node_modules/find-node-modules": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.1.3.tgz", - "integrity": "sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg==", - "dependencies": { - "findup-sync": "^4.0.0", - "merge": "^2.1.1" - } - }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -8545,20 +10215,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/findup-sync": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", - "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", - "dependencies": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^4.0.2", - "resolve-dir": "^1.0.1" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -8586,24 +10242,38 @@ } }, "node_modules/flowbite-react": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/flowbite-react/-/flowbite-react-0.3.5.tgz", - "integrity": "sha512-X2unmmXmS7DZbGTk+C7jf0qrtpPu4i/yo8OeHHd1XO5V5TAUjhAyYIcy3RwKjKS+WC+Fj/rMHe5QKBF7E0ryoA==", - "dependencies": { - "@floating-ui/react-dom": "^1.0.0", - "@floating-ui/react-dom-interactions": "^0.9.1", - "classnames": "^2.3.2", - "commitizen": "^4.2.5", - "react-icons": "^4.6.0", - "react-indiana-drag-scroll": "^2.2.0" + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/flowbite-react/-/flowbite-react-0.7.2.tgz", + "integrity": "sha512-Qq+yKW3955ZWjABzNq02NAS91rE86AHjCVRTXHXZ3dkcoGghqViIpsDCxOBo0NO7xnKkshYU3DocmczcP4pYTA==", + "dev": true, + "dependencies": { + "@floating-ui/react": "^0.26.2", + "contentlayer": "^0.3.4", + "flowbite": "^2.0.0", + "markdown-toc": "^1.2.0", + "next-contentlayer": "^0.3.4", + "react-icons": "^4.11.0", + "react-indiana-drag-scroll": "^2.2.0", + "react-markdown": "^9.0.0", + "sharp": "^0.32.6", + "tailwind-merge": "^2.0.0" }, "peerDependencies": { - "flowbite": "^1", "react": "^18", "react-dom": "^18", "tailwindcss": "^3" } }, + "node_modules/flowbite-react/node_modules/flowbite": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/flowbite/-/flowbite-2.2.1.tgz", + "integrity": "sha512-iiZyBTtriEDRHrqXZgpKHaxl4B2J8HZUP8Yn1RXozUDKszWHDVj4GxQqMMB9AJHRWOgXV/4E/LJZ/zqQgBUhWA==", + "dev": true, + "dependencies": { + "@popperjs/core": "^2.9.3", + "mini-svg-data-uri": "^1.4.3" + } + }, "node_modules/follow-redirects": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", @@ -8631,6 +10301,15 @@ "is-callable": "^1.1.3" } }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fork-ts-checker-webpack-plugin": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", @@ -8800,6 +10479,27 @@ "node": ">= 6" } }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -8828,6 +10528,12 @@ "node": ">= 0.6" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, "node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -8841,10 +10547,32 @@ "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==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.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==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", + "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==" }, "node_modules/fs.realpath": { "version": "1.0.0", @@ -8894,6 +10622,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -9059,6 +10806,12 @@ "node": ">= 4.0.0" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -9094,18 +10847,6 @@ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" }, - "node_modules/global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", - "optional": true, - "dependencies": { - "ini": "^1.3.4" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/global-modules": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", @@ -9202,10 +10943,37 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "dev": true, + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/gulp-header": { + "version": "1.8.12", + "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-1.8.12.tgz", + "integrity": "sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==", + "deprecated": "Removed event-stream from gulp-header", + "dev": true, + "dependencies": { + "concat-with-sourcemaps": "*", + "lodash.template": "^4.4.0", + "through2": "^2.0.0" + } }, "node_modules/gzip-size": { "version": "6.0.0", @@ -9258,6 +11026,15 @@ "node": ">=4" } }, + "node_modules/has-own-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", + "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/has-property-descriptors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", @@ -9294,309 +11071,1195 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "bin": { - "he": "bin/he" - } - }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dependencies": { - "parse-passwd": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, - "node_modules/hoopy": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", - "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", - "engines": { - "node": ">= 6.0.0" - } + "node_modules/hash-wasm": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/hash-wasm/-/hash-wasm-4.11.0.tgz", + "integrity": "sha512-HVusNXlVqHe0fzIzdQOGolnFN6mX/fqcrSAOcTBXdvzrXVHwTz11vXeKRmkR5gTuwVpvHZEIyKoePDvuAR+XwQ==", + "dev": true }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "node_modules/hast-util-from-parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz", + "integrity": "sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw==", + "dev": true, "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" + "@types/hast": "^2.0.0", + "@types/unist": "^2.0.0", + "hastscript": "^7.0.0", + "property-information": "^6.0.0", + "vfile": "^5.0.0", + "vfile-location": "^4.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==", + "node_modules/hast-util-parse-selector": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz", + "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==", + "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" + "@types/hast": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/hpack.js/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==" - }, - "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==", + "node_modules/hast-util-raw": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-7.2.3.tgz", + "integrity": "sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg==", + "dev": true, "dependencies": { - "safe-buffer": "~5.1.0" + "@types/hast": "^2.0.0", + "@types/parse5": "^6.0.0", + "hast-util-from-parse5": "^7.0.0", + "hast-util-to-parse5": "^7.0.0", + "html-void-elements": "^2.0.0", + "parse5": "^6.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "node_modules/hast-util-to-estree": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-2.3.3.tgz", + "integrity": "sha512-ihhPIUPxN0v0w6M5+IiAZZrn0LH2uZomeWwhn7uP7avZC6TE7lIiEh2yBMPr5+zi1aUCXq6VoYRgs2Bw9xmycQ==", + "dev": true, "dependencies": { - "whatwg-encoding": "^1.0.5" + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^2.0.0", + "@types/unist": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "estree-util-attach-comments": "^2.0.0", + "estree-util-is-identifier-name": "^2.0.0", + "hast-util-whitespace": "^2.0.0", + "mdast-util-mdx-expression": "^1.0.0", + "mdast-util-mdxjs-esm": "^1.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.1", + "unist-util-position": "^4.0.0", + "zwitch": "^2.0.0" }, - "engines": { - "node": ">=10" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/html-entities": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", - "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" - }, - "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==" - }, - "node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "node_modules/hast-util-to-html": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-8.0.4.tgz", + "integrity": "sha512-4tpQTUOr9BMjtYyNlt0P50mH7xj0Ks2xpo8M943Vykljf99HW6EzulIoJP1N3eKOSScEHzyzi9dm7/cn0RfGwA==", + "dev": true, "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" + "@types/hast": "^2.0.0", + "@types/unist": "^2.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-raw": "^7.0.0", + "hast-util-whitespace": "^2.0.0", + "html-void-elements": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": ">=12" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/html-webpack-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", - "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz", + "integrity": "sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==", + "dev": true, "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "webpack": "^5.20.0" + "url": "https://opencollective.com/unified" } }, - "node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], + "node_modules/hast-util-to-jsx-runtime/node_modules/@types/hast": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.3.tgz", + "integrity": "sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ==", + "dev": true, "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" + "@types/unist": "*" } }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" - }, - "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==", + "node_modules/hast-util-to-jsx-runtime/node_modules/@types/mdast": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", + "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "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" + "@types/unist": "*" } }, - "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + "node_modules/hast-util-to-jsx-runtime/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "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==", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" + "node_modules/hast-util-to-jsx-runtime/node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==", + "node_modules/hast-util-to-jsx-runtime/node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" + "@types/hast": "^3.0.0" }, - "engines": { - "node": ">= 6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==", + "node_modules/hast-util-to-jsx-runtime/node_modules/inline-style-parser": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.2.tgz", + "integrity": "sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ==", + "dev": true + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/mdast-util-from-markdown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", + "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", + "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" + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "node_modules/hast-util-to-jsx-runtime/node_modules/mdast-util-mdx-expression": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz", + "integrity": "sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==", + "dev": true, "dependencies": { - "agent-base": "6", - "debug": "4" + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, - "engines": { - "node": ">= 6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==", - "engines": { - "node": ">=10.17.0" + "node_modules/hast-util-to-jsx-runtime/node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==", + "node_modules/hast-util-to-jsx-runtime/node_modules/mdast-util-phrasing": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.0.0.tgz", + "integrity": "sha512-xadSsJayQIucJ9n053dfQwVu1kuXg7jCTdYsMK8rqzKZh52nLfSH/k0sAxE0u+pj/zKZX+o5wB+ML5mRayOxFA==", + "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==", - "engines": { - "node": "^10 || ^12 || >= 14" + "node_modules/hast-util-to-jsx-runtime/node_modules/mdast-util-to-markdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", + "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" }, - "peerDependencies": { - "postcss": "^8.1.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/idb": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", - "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" - }, - "node_modules/identity-obj-proxy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", - "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "node_modules/hast-util-to-jsx-runtime/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dev": true, "dependencies": { - "harmony-reflect": "^1.4.6" + "@types/mdast": "^4.0.0" }, - "engines": { - "node": ">=4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "dev": true, "funding": [ { - "type": "github", - "url": "https://github.com/sponsors/feross" + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" }, { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-core-commonmark": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", + "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" }, { - "type": "consulting", - "url": "https://feross.org/support" + "type": "OpenCollective", + "url": "https://opencollective.com/unified" } - ] + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-factory-destination": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } }, - "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "engines": { - "node": ">= 4" + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-factory-label": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-factory-title": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-factory-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-util-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.0.1.tgz", + "integrity": "sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-util-chunked": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-util-classify-character": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-util-combine-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-util-decode-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-util-html-tag-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-util-resolve-all": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-util-subtokenize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", + "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/style-to-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.5.tgz", + "integrity": "sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ==", + "dev": true, + "dependencies": { + "inline-style-parser": "0.2.2" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-7.1.0.tgz", + "integrity": "sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw==", + "dev": true, + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", + "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", + "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", + "dev": true, + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^3.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "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==", + "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/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==" + }, + "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==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" + }, + "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==" + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-url-attributes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.0.tgz", + "integrity": "sha512-/sXbVCWayk6GDVg3ctOX6nxaVj7So40FcFAnWlWGNAB1LpYKcV5Cd10APjPjW80O7zYW2MsjBV4zZ7IZO5fVow==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz", + "integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "webpack": "^5.20.0" + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" + }, + "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==", + "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-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "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==", + "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==", + "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==", + "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.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "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==", + "engines": { + "node": ">=10.17.0" + } + }, + "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==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "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==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, + "node_modules/identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "dependencies": { + "harmony-reflect": "^1.4.6" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imagescript": { + "version": "1.2.17", + "resolved": "https://registry.npmjs.org/imagescript/-/imagescript-1.2.17.tgz", + "integrity": "sha512-gUibUVTqbd2AeakephgVshjTL9zqh7k4Ea9yk9z8O9jjn11qU3vQWbMRDWGVzQl1t/cLeHYjclefjx8HblXo9Q==", + "dev": true, + "engines": { + "node": ">=14.0.0" } }, "node_modules/immer": { @@ -9604,112 +12267,856 @@ "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz", "integrity": "sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ==", "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "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==", + "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==", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflection": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-2.0.1.tgz", + "integrity": "sha512-wzkZHqpb4eGrOKBl34xy3umnYHx8Si5R1U4fwmdxLo5gdH6mEK8gclckTj/qWqy4Je0bsDYe/qazZYuO7xe3XQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "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==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dev": true, + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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==", + "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": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "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-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "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" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "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==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "engines": { + "node": ">=6" + } + }, + "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-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "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==", + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "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==", + "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-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "node_modules/is-reference": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", + "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, + "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==", + "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-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", "engines": { "node": ">=6" + } + }, + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "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==", + "engines": { + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-fresh/node_modules/resolve-from": { + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "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": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "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==", + "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==", + "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/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "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==", "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" + "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==", + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" }, "bin": { - "import-local-fixture": "fixtures/cli.js" + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/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==", + "dependencies": { + "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": ">=0.8.19" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/indent-string": { + "node_modules/jake/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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/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/jake/node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { "node": ">=8" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "node_modules/jake/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==", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "node_modules/jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "dependencies": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + "node_modules/jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "dependencies": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } }, - "node_modules/inquirer": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", - "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "node_modules/jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", "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.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" }, "engines": { - "node": ">=12.0.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/inquirer/node_modules/ansi-styles": { + "node_modules/jest-circus/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==", @@ -9723,7 +13130,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/inquirer/node_modules/chalk": { + "node_modules/jest-circus/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -9738,7 +13145,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/inquirer/node_modules/color-convert": { + "node_modules/jest-circus/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==", @@ -9749,12 +13156,12 @@ "node": ">=7.0.0" } }, - "node_modules/inquirer/node_modules/color-name": { + "node_modules/jest-circus/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/inquirer/node_modules/has-flag": { + "node_modules/jest-circus/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==", @@ -9762,7 +13169,7 @@ "node": ">=8" } }, - "node_modules/inquirer/node_modules/supports-color": { + "node_modules/jest-circus/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==", @@ -9773,488 +13180,359 @@ "node": ">=8" } }, - "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "node_modules/jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" }, - "engines": { - "node": ">= 0.4" - } - }, - "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==", - "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==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "bin": { + "jest": "bin/jest.js" }, "engines": { - "node": ">= 0.4" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" }, - "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": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dependencies": { - "has-bigints": "^1.0.1" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "node-notifier": { + "optional": 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==", + "node_modules/jest-cli/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==", "dependencies": { - "binary-extensions": "^2.0.0" + "color-convert": "^2.0.1" }, "engines": { "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "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-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dependencies": { - "has": "^1.0.3" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "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==", + "node_modules/jest-cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "has-tostringtag": "^1.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "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==", - "bin": { - "is-docker": "cli.js" + "node_modules/jest-cli/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==", + "dependencies": { + "color-name": "~1.1.4" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=7.0.0" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } + "node_modules/jest-cli/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/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==", + "node_modules/jest-cli/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==", "engines": { "node": ">=8" } }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "engines": { - "node": ">=6" - } - }, - "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==", + "node_modules/jest-cli/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==", "dependencies": { - "is-extglob": "^2.1.1" + "has-flag": "^4.0.0" }, - "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==", "engines": { "node": ">=8" } }, - "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "engines": { - "node": ">= 0.4" + "node_modules/jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "dependencies": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } } }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "node_modules/jest-config/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==", "dependencies": { - "has-tostringtag": "^1.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "engines": { - "node": ">=8" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "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==", + "node_modules/jest-config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" - }, - "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==", + "node_modules/jest-config/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==", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=7.0.0" } }, - "node_modules/is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", - "engines": { - "node": ">=0.10.0" - } + "node_modules/jest-config/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/is-root": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", - "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "node_modules/jest-config/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==", "engines": { - "node": ">=6" - } - }, - "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "node_modules/jest-config/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==", "dependencies": { - "call-bind": "^1.0.2" + "has-flag": "^4.0.0" }, - "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==", "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", "dependencies": { - "has-tostringtag": "^1.0.0" + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/jest-diff/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==", "dependencies": { - "has-symbols": "^1.0.2" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "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==", - "engines": { - "node": ">=10" + "node_modules/jest-diff/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==", + "dependencies": { + "color-name": "~1.1.4" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=7.0.0" } }, - "node_modules/is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==" + "node_modules/jest-diff/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/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/jest-diff/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==", + "engines": { + "node": ">=8" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/jest-diff/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==", "dependencies": { - "call-bind": "^1.0.2" + "has-flag": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "node_modules/jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "detect-newline": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "engines": { - "node": ">=0.10.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "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==", + "node_modules/jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", "dependencies": { - "is-docker": "^2.0.0" + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" }, "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "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==", + "node_modules/jest-each/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==", + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "node_modules/jest-each/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "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" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", - "bin": { - "semver": "bin/semver.js" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "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==", + "node_modules/jest-each/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==", "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">=8" + "node": ">=7.0.0" } }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { + "node_modules/jest-each/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/jest-each/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==", @@ -10262,7 +13540,7 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { + "node_modules/jest-each/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==", @@ -10273,57 +13551,100 @@ "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==", + "node_modules/jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" }, "engines": { - "node": ">=10" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "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==", + "node_modules/jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, "engines": { - "node": ">=0.10.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" }, "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" } }, - "node_modules/jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "node_modules/jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "bin": { - "jake": "bin/cli.js" + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" }, "engines": { - "node": ">=10" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jake/node_modules/ansi-styles": { + "node_modules/jest-jasmine2/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==", @@ -10337,7 +13658,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jake/node_modules/chalk": { + "node_modules/jest-jasmine2/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -10352,7 +13673,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jake/node_modules/color-convert": { + "node_modules/jest-jasmine2/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==", @@ -10363,12 +13684,12 @@ "node": ">=7.0.0" } }, - "node_modules/jake/node_modules/color-name": { + "node_modules/jest-jasmine2/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/jake/node_modules/has-flag": { + "node_modules/jest-jasmine2/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==", @@ -10376,7 +13697,7 @@ "node": ">=8" } }, - "node_modules/jake/node_modules/supports-color": { + "node_modules/jest-jasmine2/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==", @@ -10387,73 +13708,116 @@ "node": ">=8" } }, - "node_modules/jest": { + "node_modules/jest-leak-detector": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", - "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", "dependencies": { - "@jest/core": "^27.5.1", - "import-local": "^3.0.2", - "jest-cli": "^27.5.1" + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" }, - "bin": { - "jest": "bin/jest.js" + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" }, "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils/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==", + "dependencies": { + "color-convert": "^2.0.1" }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "engines": { + "node": ">=8" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-changed-files": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", - "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "@jest/types": "^27.5.1", - "execa": "^5.0.0", - "throat": "^6.0.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-circus": { + "node_modules/jest-matcher-utils/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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-matcher-utils/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/jest-matcher-utils/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-matcher-utils/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==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", - "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", + "@babel/code-frame": "^7.12.13", "@jest/types": "^27.5.1", - "@types/node": "*", + "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", "pretty-format": "^27.5.1", "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" + "stack-utils": "^2.0.3" }, "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-circus/node_modules/ansi-styles": { + "node_modules/jest-message-util/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==", @@ -10467,7 +13831,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-circus/node_modules/chalk": { + "node_modules/jest-message-util/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -10482,7 +13846,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-circus/node_modules/color-convert": { + "node_modules/jest-message-util/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==", @@ -10493,12 +13857,12 @@ "node": ">=7.0.0" } }, - "node_modules/jest-circus/node_modules/color-name": { + "node_modules/jest-message-util/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/jest-circus/node_modules/has-flag": { + "node_modules/jest-message-util/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==", @@ -10506,7 +13870,7 @@ "node": ">=8" } }, - "node_modules/jest-circus/node_modules/supports-color": { + "node_modules/jest-message-util/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==", @@ -10517,40 +13881,76 @@ "node": ">=8" } }, - "node_modules/jest-cli": { + "node_modules/jest-mock": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", - "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", "dependencies": { - "@jest/core": "^27.5.1", - "@jest/test-result": "^27.5.1", "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "prompts": "^2.0.1", - "yargs": "^16.2.0" - }, - "bin": { - "jest": "bin/jest.js" + "@types/node": "*" }, "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "engines": { + "node": ">=6" }, "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "jest-resolve": "*" }, "peerDependenciesMeta": { - "node-notifier": { + "jest-resolve": { "optional": true } } }, - "node_modules/jest-cli/node_modules/ansi-styles": { + "node_modules/jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve/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==", @@ -10564,7 +13964,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-cli/node_modules/chalk": { + "node_modules/jest-resolve/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -10579,7 +13979,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-cli/node_modules/color-convert": { + "node_modules/jest-resolve/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==", @@ -10590,12 +13990,12 @@ "node": ">=7.0.0" } }, - "node_modules/jest-cli/node_modules/color-name": { + "node_modules/jest-resolve/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/jest-cli/node_modules/has-flag": { + "node_modules/jest-resolve/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==", @@ -10603,7 +14003,7 @@ "node": ">=8" } }, - "node_modules/jest-cli/node_modules/supports-color": { + "node_modules/jest-resolve/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==", @@ -10614,49 +14014,38 @@ "node": ">=8" } }, - "node_modules/jest-config": { + "node_modules/jest-runner": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", - "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", "dependencies": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.5.1", + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", "@jest/types": "^27.5.1", - "babel-jest": "^27.5.1", + "@types/node": "*", "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", + "emittery": "^0.8.1", "graceful-fs": "^4.2.9", - "jest-circus": "^27.5.1", + "jest-docblock": "^27.5.1", "jest-environment-jsdom": "^27.5.1", "jest-environment-node": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-jasmine2": "^27.5.1", - "jest-regex-util": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", "jest-resolve": "^27.5.1", - "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" }, "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - } } }, - "node_modules/jest-config/node_modules/ansi-styles": { + "node_modules/jest-runner/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==", @@ -10670,7 +14059,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-config/node_modules/chalk": { + "node_modules/jest-runner/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -10685,7 +14074,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-config/node_modules/color-convert": { + "node_modules/jest-runner/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==", @@ -10696,12 +14085,12 @@ "node": ">=7.0.0" } }, - "node_modules/jest-config/node_modules/color-name": { + "node_modules/jest-runner/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/jest-config/node_modules/has-flag": { + "node_modules/jest-runner/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==", @@ -10709,7 +14098,7 @@ "node": ">=8" } }, - "node_modules/jest-config/node_modules/supports-color": { + "node_modules/jest-runner/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==", @@ -10720,21 +14109,39 @@ "node": ">=8" } }, - "node_modules/jest-diff": { + "node_modules/jest-runtime": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" }, "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-diff/node_modules/ansi-styles": { + "node_modules/jest-runtime/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==", @@ -10748,7 +14155,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-diff/node_modules/chalk": { + "node_modules/jest-runtime/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -10763,7 +14170,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-diff/node_modules/color-convert": { + "node_modules/jest-runtime/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==", @@ -10774,12 +14181,12 @@ "node": ">=7.0.0" } }, - "node_modules/jest-diff/node_modules/color-name": { + "node_modules/jest-runtime/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/jest-diff/node_modules/has-flag": { + "node_modules/jest-runtime/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==", @@ -10787,7 +14194,7 @@ "node": ">=8" } }, - "node_modules/jest-diff/node_modules/supports-color": { + "node_modules/jest-runtime/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==", @@ -10798,33 +14205,51 @@ "node": ">=8" } }, - "node_modules/jest-docblock": { + "node_modules/jest-serializer": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", - "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", "dependencies": { - "detect-newline": "^3.0.0" + "@types/node": "*", + "graceful-fs": "^4.2.9" }, "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-each": { + "node_modules/jest-snapshot": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", - "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", "dependencies": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", "jest-util": "^27.5.1", - "pretty-format": "^27.5.1" + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" }, "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-each/node_modules/ansi-styles": { + "node_modules/jest-snapshot/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==", @@ -10838,7 +14263,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-each/node_modules/chalk": { + "node_modules/jest-snapshot/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -10853,7 +14278,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-each/node_modules/color-convert": { + "node_modules/jest-snapshot/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==", @@ -10864,12 +14289,12 @@ "node": ">=7.0.0" } }, - "node_modules/jest-each/node_modules/color-name": { + "node_modules/jest-snapshot/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/jest-each/node_modules/has-flag": { + "node_modules/jest-snapshot/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==", @@ -10877,7 +14302,7 @@ "node": ">=8" } }, - "node_modules/jest-each/node_modules/supports-color": { + "node_modules/jest-snapshot/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==", @@ -10888,100 +14313,23 @@ "node": ">=8" } }, - "node_modules/jest-environment-jsdom": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", - "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1", - "jsdom": "^16.6.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", - "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", - "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^27.5.1", - "jest-serializer": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "micromatch": "^4.0.4", - "walker": "^1.0.7" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-jasmine2": { + "node_modules/jest-util": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", - "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", "@jest/types": "^27.5.1", "@types/node": "*", "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "throat": "^6.0.1" + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-jasmine2/node_modules/ansi-styles": { + "node_modules/jest-util/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==", @@ -10995,7 +14343,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-jasmine2/node_modules/chalk": { + "node_modules/jest-util/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -11010,7 +14358,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-jasmine2/node_modules/color-convert": { + "node_modules/jest-util/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==", @@ -11021,12 +14369,12 @@ "node": ">=7.0.0" } }, - "node_modules/jest-jasmine2/node_modules/color-name": { + "node_modules/jest-util/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/jest-jasmine2/node_modules/has-flag": { + "node_modules/jest-util/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==", @@ -11034,7 +14382,7 @@ "node": ">=8" } }, - "node_modules/jest-jasmine2/node_modules/supports-color": { + "node_modules/jest-util/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==", @@ -11045,33 +14393,23 @@ "node": ">=8" } }, - "node_modules/jest-leak-detector": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", - "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", - "dependencies": { - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-matcher-utils": { + "node_modules/jest-validate": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", "dependencies": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-diff": "^27.5.1", "jest-get-type": "^27.5.1", + "leven": "^3.1.0", "pretty-format": "^27.5.1" }, "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "node_modules/jest-validate/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==", @@ -11085,7 +14423,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-matcher-utils/node_modules/chalk": { + "node_modules/jest-validate/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -11100,7 +14438,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-matcher-utils/node_modules/color-convert": { + "node_modules/jest-validate/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==", @@ -11111,12 +14449,12 @@ "node": ">=7.0.0" } }, - "node_modules/jest-matcher-utils/node_modules/color-name": { + "node_modules/jest-validate/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/jest-matcher-utils/node_modules/has-flag": { + "node_modules/jest-validate/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==", @@ -11124,7 +14462,7 @@ "node": ">=8" } }, - "node_modules/jest-matcher-utils/node_modules/supports-color": { + "node_modules/jest-validate/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==", @@ -11135,26 +14473,89 @@ "node": ">=8" } }, - "node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "node_modules/jest-watch-typeahead": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", + "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", + "ansi-escapes": "^4.3.1", "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "jest-regex-util": "^28.0.0", + "jest-watcher": "^28.0.0", + "slash": "^4.0.0", + "string-length": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "jest": "^27.0.0 || ^28.0.0" } }, - "node_modules/jest-message-util/node_modules/ansi-styles": { + "node_modules/jest-watch-typeahead/node_modules/@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/console/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "dependencies": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/@types/yargs": { + "version": "17.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz", + "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-watch-typeahead/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==", @@ -11168,7 +14569,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-message-util/node_modules/chalk": { + "node_modules/jest-watch-typeahead/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -11183,7 +14584,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-message-util/node_modules/color-convert": { + "node_modules/jest-watch-typeahead/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==", @@ -11194,12 +14595,23 @@ "node": ">=7.0.0" } }, - "node_modules/jest-message-util/node_modules/color-name": { + "node_modules/jest-watch-typeahead/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/jest-message-util/node_modules/has-flag": { + "node_modules/jest-watch-typeahead/node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/jest-watch-typeahead/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==", @@ -11207,140 +14619,188 @@ "node": ">=8" } }, - "node_modules/jest-message-util/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==", + "node_modules/jest-watch-typeahead/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", "dependencies": { - "has-flag": "^4.0.0" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*" - }, + "node_modules/jest-watch-typeahead/node_modules/jest-message-util/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=8" } }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "node_modules/jest-watch-typeahead/node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-regex-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", - "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "node_modules/jest-watch-typeahead/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-resolve": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", - "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "node_modules/jest-watch-typeahead/node_modules/jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", "dependencies": { - "@jest/types": "^27.5.1", + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "node_modules/jest-resolve-dependencies": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", - "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dependencies": { - "@jest/types": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-snapshot": "^27.5.1" + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=10" } }, - "node_modules/jest-resolve/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==", + "node_modules/jest-watch-typeahead/node_modules/jest-watcher/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==", "dependencies": { - "color-convert": "^2.0.1" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" + } + }, + "node_modules/jest-watch-typeahead/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watch-typeahead/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-resolve/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-watch-typeahead/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "node_modules/jest-watch-typeahead/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watch-typeahead/node_modules/string-length": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", + "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "char-regex": "^2.0.0", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=10" + "node": ">=12.20" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-resolve/node_modules/color-convert": { + "node_modules/jest-watch-typeahead/node_modules/string-length/node_modules/char-regex": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", + "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/jest-watch-typeahead/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==", "dependencies": { - "color-name": "~1.1.4" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/jest-resolve/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/jest-resolve/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==", + "node_modules/jest-watch-typeahead/node_modules/strip-ansi/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==", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/jest-resolve/node_modules/supports-color": { + "node_modules/jest-watch-typeahead/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==", @@ -11351,38 +14811,24 @@ "node": ">=8" } }, - "node_modules/jest-runner": { + "node_modules/jest-watcher": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", - "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", "dependencies": { - "@jest/console": "^27.5.1", - "@jest/environment": "^27.5.1", "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", "@jest/types": "^27.5.1", "@types/node": "*", + "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "emittery": "^0.8.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-leak-detector": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runtime": "^27.5.1", "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" + "string-length": "^4.0.1" }, "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/jest-runner/node_modules/ansi-styles": { + "node_modules/jest-watcher/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==", @@ -11396,7 +14842,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-runner/node_modules/chalk": { + "node_modules/jest-watcher/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -11411,7 +14857,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-runner/node_modules/color-convert": { + "node_modules/jest-watcher/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==", @@ -11422,12 +14868,12 @@ "node": ">=7.0.0" } }, - "node_modules/jest-runner/node_modules/color-name": { + "node_modules/jest-watcher/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/jest-runner/node_modules/has-flag": { + "node_modules/jest-watcher/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==", @@ -11435,7 +14881,7 @@ "node": ">=8" } }, - "node_modules/jest-runner/node_modules/supports-color": { + "node_modules/jest-watcher/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==", @@ -11446,1344 +14892,2333 @@ "node": ">=8" } }, - "node_modules/jest-runtime": { + "node_modules/jest-worker": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", - "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/globals": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 10.13.0" } }, - "node_modules/jest-runtime/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==", + "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==", + "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==", "dependencies": { - "color-convert": "^2.0.1" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/jest-runtime/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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==" + }, + "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==", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.3.0.tgz", + "integrity": "sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==", + "dev": true + }, + "node_modules/jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" }, "engines": { "node": ">=10" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, - "node_modules/jest-runtime/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==", + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "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==" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "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==" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dependencies": { - "color-name": "~1.1.4" + "universalify": "^2.0.0" }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", "engines": { - "node": ">=7.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-runtime/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/jsx-ast-utils": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", + "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", + "dependencies": { + "array-includes": "^3.1.5", + "object.assign": "^4.1.3" + }, + "engines": { + "node": ">=4.0" + } }, - "node_modules/jest-runtime/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==", + "node_modules/kareem": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", + "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", "engines": { - "node": ">=8" + "node": ">=12.0.0" } }, - "node_modules/jest-runtime/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==", + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" + }, + "node_modules/language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", "dependencies": { - "has-flag": "^4.0.0" + "language-subtag-registry": "~0.3.2" + } + }, + "node_modules/lazy-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", + "integrity": "sha512-7vp2Acd2+Kz4XkzxGxaB1FWOi8KjWIWsgdfD5MCb86DWvlLqhRPM+d6Pro3iNEL5VT9mstz5hKAlcd+QR6H3aA==", + "dev": true, + "dependencies": { + "set-getter": "^0.1.0" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/jest-serializer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", - "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dependencies": { - "@types/node": "*", - "graceful-fs": "^4.2.9" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">= 0.8.0" } }, - "node_modules/jest-snapshot": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", - "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "node_modules/lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "engines": { + "node": ">=10" + } + }, + "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==" + }, + "node_modules/list-item": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/list-item/-/list-item-1.1.1.tgz", + "integrity": "sha512-S3D0WZ4J6hyM8o5SNKWaMYB1ALSacPZ2nHGEuCjmHZ+dc03gFeNZoNDcqfcnO4vDhTZmNrqrpYZCdXsRh22bzw==", + "dev": true, "dependencies": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^27.5.1", - "semver": "^7.3.2" + "expand-range": "^1.8.1", + "extend-shallow": "^2.0.1", + "is-number": "^2.1.0", + "repeat-string": "^1.5.2" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { + "node_modules/list-item/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/list-item/node_modules/is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/list-item/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-runner": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" + }, + "node_modules/lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "dev": true, + "dependencies": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "node_modules/lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dev": true, + "dependencies": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "dev": true + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "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==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "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==", "dependencies": { - "color-convert": "^2.0.1" + "semver": "^6.0.0" }, "engines": { "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-snapshot/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/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==", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/jest-snapshot/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==", + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "tmpl": "1.0.5" } }, - "node_modules/jest-snapshot/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/jest-snapshot/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==", + "node_modules/markdown-extensions": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz", + "integrity": "sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/jest-snapshot/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, + "node_modules/markdown-link": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/markdown-link/-/markdown-link-0.1.1.tgz", + "integrity": "sha512-TurLymbyLyo+kAUUAV9ggR9EPcDjP/ctlv9QAFiqUH7c+t6FlsbivPo9OKTU8xdOx9oNd2drW/Fi5RRElQbUqA==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "node_modules/markdown-toc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/markdown-toc/-/markdown-toc-1.2.0.tgz", + "integrity": "sha512-eOsq7EGd3asV0oBfmyqngeEIhrbkc7XVP63OwcJBIhH2EpG2PzFcbZdhy1jutXSlRBBVMNXHvMtSr5LAxSUvUg==", + "dev": true, "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" + "concat-stream": "^1.5.2", + "diacritics-map": "^0.1.0", + "gray-matter": "^2.1.0", + "lazy-cache": "^2.0.2", + "list-item": "^1.1.1", + "markdown-link": "^0.1.1", + "minimist": "^1.2.0", + "mixin-deep": "^1.1.3", + "object.pick": "^1.2.0", + "remarkable": "^1.7.1", + "repeat-string": "^1.6.1", + "strip-color": "^0.1.0" + }, + "bin": { + "markdown-toc": "cli.js" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-util/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==", + "node_modules/markdown-toc/node_modules/gray-matter": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-2.1.1.tgz", + "integrity": "sha512-vbmvP1Fe/fxuT2QuLVcqb2BfK7upGhhbLIt9/owWEvPYrZZEkelLcq2HqzxosV+PQ67dUFLaAeNpH7C4hhICAA==", + "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "ansi-red": "^0.1.1", + "coffee-script": "^1.12.4", + "extend-shallow": "^2.0.1", + "js-yaml": "^3.8.1", + "toml": "^2.3.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/markdown-toc/node_modules/toml": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/toml/-/toml-2.3.6.tgz", + "integrity": "sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ==", + "dev": true + }, + "node_modules/math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "dev": true + }, + "node_modules/mdast-util-definitions": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", + "integrity": "sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==", + "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-util/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==", + "node_modules/mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-util/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/jest-util/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==", - "engines": { - "node": ">=8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-util/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==", + "node_modules/mdast-util-frontmatter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-1.0.1.tgz", + "integrity": "sha512-JjA2OjxRqAa8wEG8hloD0uTU0kdn8kbtOWpPP94NBkfAlbxn4S8gCGf/9DwFtEeGPXrDcNXdiDjVaRdUFqYokw==", + "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0", + "micromark-extension-frontmatter": "^1.0.0" }, - "engines": { - "node": ">=8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-validate": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", - "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "node_modules/mdast-util-mdx": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-2.0.1.tgz", + "integrity": "sha512-38w5y+r8nyKlGvNjSEqWrhG0w5PmnRA+wnBvm+ulYCct7nsGYhFVb0lljS9bQav4psDAS1eGkP2LMVcZBi/aqw==", + "dev": true, "dependencies": { - "@jest/types": "^27.5.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "leven": "^3.1.0", - "pretty-format": "^27.5.1" + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-mdx-expression": "^1.0.0", + "mdast-util-mdx-jsx": "^2.0.0", + "mdast-util-mdxjs-esm": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-validate/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==", + "node_modules/mdast-util-mdx-expression": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-1.3.2.tgz", + "integrity": "sha512-xIPmR5ReJDu/DHH1OoIT1HkuybIfRGYRywC+gJtI7qHjCJp/M9jrmBEJW22O8lskDWm562BX2W8TiAwRTb0rKA==", + "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-validate/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/mdast-util-mdx-jsx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.0.0.tgz", + "integrity": "sha512-XZuPPzQNBPAlaqsTTgRrcJnyFbSOBovSadFgbFu8SnuNgm+6Bdx1K+IWoitsmj6Lq6MNtI+ytOqwN70n//NaBA==", + "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-remove-position": "^5.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-validate/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==", + "node_modules/mdast-util-mdx-jsx/node_modules/@types/hast": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.3.tgz", + "integrity": "sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "@types/unist": "*" } }, - "node_modules/jest-validate/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/jest-validate/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==", - "engines": { - "node": ">=8" + "node_modules/mdast-util-mdx-jsx/node_modules/@types/mdast": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", + "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "dev": true, + "dependencies": { + "@types/unist": "*" } }, - "node_modules/jest-validate/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==", + "node_modules/mdast-util-mdx-jsx/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dev": true + }, + "node_modules/mdast-util-mdx-jsx/node_modules/mdast-util-from-markdown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", + "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", + "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" }, - "engines": { - "node": ">=8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-watch-typeahead": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", - "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", + "node_modules/mdast-util-mdx-jsx/node_modules/mdast-util-phrasing": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.0.0.tgz", + "integrity": "sha512-xadSsJayQIucJ9n053dfQwVu1kuXg7jCTdYsMK8rqzKZh52nLfSH/k0sAxE0u+pj/zKZX+o5wB+ML5mRayOxFA==", + "dev": true, "dependencies": { - "ansi-escapes": "^4.3.1", - "chalk": "^4.0.0", - "jest-regex-util": "^28.0.0", - "jest-watcher": "^28.0.0", - "slash": "^4.0.0", - "string-length": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" }, - "peerDependencies": { - "jest": "^27.0.0 || ^28.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-watch-typeahead/node_modules/@jest/console": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", - "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "node_modules/mdast-util-mdx-jsx/node_modules/mdast-util-to-markdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", + "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", + "dev": true, "dependencies": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0" + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/console/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-watch-typeahead/node_modules/@jest/test-result": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", - "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "node_modules/mdast-util-mdx-jsx/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dev": true, "dependencies": { - "@jest/console": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" + "@types/mdast": "^4.0.0" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-watch-typeahead/node_modules/@jest/types": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", - "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "node_modules/mdast-util-mdx-jsx/node_modules/micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "@jest/schemas": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-core-commonmark": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", + "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-factory-destination": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/jest-watch-typeahead/node_modules/@types/yargs": { - "version": "17.0.13", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz", - "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==", + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-factory-label": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "@types/yargs-parser": "*" + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/jest-watch-typeahead/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==", + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/jest-watch-typeahead/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-factory-title": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/jest-watch-typeahead/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==", + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-factory-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/jest-watch-typeahead/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/jest-watch-typeahead/node_modules/emittery": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", - "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.0.1.tgz", + "integrity": "sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/jest-watch-typeahead/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==", - "engines": { - "node": ">=8" + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-chunked": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/jest-watch-typeahead/node_modules/jest-message-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", - "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-classify-character": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^28.1.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/jest-watch-typeahead/node_modules/jest-message-util/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-combine-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/jest-watch-typeahead/node_modules/jest-regex-util": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", - "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/jest-watch-typeahead/node_modules/jest-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", - "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-decode-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/jest-watch-typeahead/node_modules/jest-watcher": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", - "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-html-tag-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "jest-util": "^28.1.3", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-resolve-all": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" + "micromark-util-types": "^2.0.0" } }, - "node_modules/jest-watch-typeahead/node_modules/jest-watcher/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==", + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/jest-watch-typeahead/node_modules/pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-subtokenize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", + "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/jest-watch-typeahead/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "engines": { - "node": ">=10" + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mdast-util-mdx-jsx/node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/mdast-util-mdx-jsx/node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-watch-typeahead/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, - "node_modules/jest-watch-typeahead/node_modules/slash": { + "node_modules/mdast-util-mdx-jsx/node_modules/unist-util-stringify-position": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "engines": { - "node": ">=12" + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-watch-typeahead/node_modules/string-length": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", - "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", + "node_modules/mdast-util-mdx-jsx/node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, "dependencies": { - "char-regex": "^2.0.0", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12.20" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watch-typeahead/node_modules/string-length/node_modules/char-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", - "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==", - "engines": { - "node": ">=12.20" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-watch-typeahead/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==", + "node_modules/mdast-util-mdx-jsx/node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-watch-typeahead/node_modules/strip-ansi/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==", - "engines": { - "node": ">=12" + "node_modules/mdast-util-mdx/node_modules/mdast-util-mdx-jsx": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-2.1.4.tgz", + "integrity": "sha512-DtMn9CmVhVzZx3f+optVDF8yFgQVt7FghCRNdlIaS3X5Bnym3hZwPbg/XW86vdpKjlc1PVj26SpnLGeJBXD3JA==", + "dev": true, + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "ccount": "^2.0.0", + "mdast-util-from-markdown": "^1.1.0", + "mdast-util-to-markdown": "^1.3.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-remove-position": "^4.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-watch-typeahead/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==", + "node_modules/mdast-util-mdx/node_modules/unist-util-remove-position": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-4.0.2.tgz", + "integrity": "sha512-TkBb0HABNmxzAcfLf4qsIbFbaPDvMO6wa3b3j4VcEzFVaw1LBKwnW4/sRJ/atSLSzoIg41JWEdnE7N6DIhGDGQ==", + "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" }, - "engines": { - "node": ">=8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-watcher": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", - "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "node_modules/mdast-util-mdx/node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dev": true, "dependencies": { - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^27.5.1", - "string-length": "^4.0.1" + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-watcher/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==", + "node_modules/mdast-util-mdxjs-esm": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-1.3.1.tgz", + "integrity": "sha512-SXqglS0HrEvSdUEfoXFtcg7DRl7S2cwOXc7jkuusG472Mmjag34DUDeOJUZtl+BVnyeO1frIgVpHlNRWc2gk/w==", + "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-watcher/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/mdast-util-phrasing": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz", + "integrity": "sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==", + "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" + "@types/mdast": "^3.0.0", + "unist-util-is": "^5.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-watcher/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==", + "node_modules/mdast-util-to-hast": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.0.2.tgz", + "integrity": "sha512-U5I+500EOOw9e3ZrclN3Is3fRpw8c19SMyNZlZ2IS+7vLsNzb2Om11VpIVOR+/0137GhZsFEF6YiKD5+0Hr2Og==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0" }, - "engines": { - "node": ">=7.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jest-watcher/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/jest-watcher/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==", - "engines": { - "node": ">=8" + "node_modules/mdast-util-to-hast/node_modules/@types/hast": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.3.tgz", + "integrity": "sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ==", + "dev": true, + "dependencies": { + "@types/unist": "*" } }, - "node_modules/jest-watcher/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==", + "node_modules/mdast-util-to-hast/node_modules/@types/mdast": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", + "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "dev": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "@types/unist": "*" } }, - "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==", + "node_modules/mdast-util-to-hast/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dev": true + }, + "node_modules/mdast-util-to-hast/node_modules/micromark-util-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.0.1.tgz", + "integrity": "sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.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==", - "engines": { - "node": ">=8" - } + "node_modules/mdast-util-to-hast/node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] }, - "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==", + "node_modules/mdast-util-to-hast/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } + "node_modules/mdast-util-to-hast/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] }, - "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==" + "node_modules/mdast-util-to-hast/node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] }, - "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==", + "node_modules/mdast-util-to-hast/node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "@types/unist": "^3.0.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "node_modules/mdast-util-to-hast/node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, "dependencies": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "canvas": "^2.5.0" + "@types/unist": "^3.0.0" }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "bin": { - "jsesc": "bin/jsesc" + "node_modules/mdast-util-to-hast/node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" }, - "engines": { - "node": ">=4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==" - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, - "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==" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" - }, - "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", - "bin": { - "json5": "lib/cli.js" + "node_modules/mdast-util-to-hast/node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" }, - "engines": { - "node": ">=6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "node_modules/mdast-util-to-markdown": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.5.0.tgz", + "integrity": "sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==", + "dev": true, "dependencies": { - "universalify": "^2.0.0" + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^3.0.0", + "mdast-util-to-string": "^3.0.0", + "micromark-util-decode-string": "^1.0.0", + "unist-util-visit": "^4.0.0", + "zwitch": "^2.0.0" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonpointer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", - "engines": { - "node": ">=0.10.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", + "node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "dev": true, "dependencies": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" + "@types/mdast": "^3.0.0" }, - "engines": { - "node": ">=4.0" - } - }, - "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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "engines": { - "node": ">=6" - } - }, - "node_modules/klona": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", - "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", - "engines": { - "node": ">= 8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" }, - "node_modules/language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "node_modules/mdx-bundler": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/mdx-bundler/-/mdx-bundler-9.2.1.tgz", + "integrity": "sha512-hWEEip1KU9MCNqeH2rqwzAZ1pdqPPbfkx9OTJjADqGPQz4t9BO85fhI7AP9gVYrpmfArf9/xJZUN0yBErg/G/Q==", + "dev": true, "dependencies": { - "language-subtag-registry": "~0.3.2" + "@babel/runtime": "^7.16.3", + "@esbuild-plugins/node-resolve": "^0.1.4", + "@fal-works/esbuild-plugin-global-externals": "^2.1.2", + "@mdx-js/esbuild": "^2.0.0", + "gray-matter": "^4.0.3", + "remark-frontmatter": "^4.0.1", + "remark-mdx-frontmatter": "^1.1.1", + "uuid": "^8.3.2", + "vfile": "^5.3.2" + }, + "engines": { + "node": ">=14", + "npm": ">=6" + }, + "peerDependencies": { + "esbuild": "0.*" } }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "engines": { - "node": ">=6" + "node": ">= 0.6" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "fs-monkey": "^1.0.4" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 4.0.0" } }, - "node_modules/lilconfig": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", - "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", - "engines": { - "node": ">=10" - } + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" }, - "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==" + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, - "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==", + "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==" + }, + "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": ">=6.11.5" + "node": ">= 8" } }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "engines": { - "node": ">=8.9.0" + "node": ">= 0.6" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-extension-frontmatter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-1.1.1.tgz", + "integrity": "sha512-m2UH9a7n3W8VAH9JO9y01APpPKmNNNs71P0RbknEmYSaZU5Ghogv38BYO94AI5Xw6OYfxZRdHZZ2nYjs/Z+SZQ==", + "dev": true, + "dependencies": { + "fault": "^2.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "optional": true - }, - "node_modules/lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "node_modules/lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "optional": true - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + "node_modules/micromark-extension-mdx-expression": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-1.0.8.tgz", + "integrity": "sha512-zZpeQtc5wfWKdzDsHRBY003H2Smg+PUi2REhqgIhdzAa5xonhP03FcXxqFSerFiNUr5AWmHpaNPQTBVOS4lrXw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/estree": "^1.0.0", + "micromark-factory-mdx-expression": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-events-to-acorn": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } }, - "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==", + "node_modules/micromark-extension-mdx-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-1.0.5.tgz", + "integrity": "sha512-gPH+9ZdmDflbu19Xkb8+gheqEDqkSpdCEubQyxuz/Hn8DOXiXvrXeikOoBA71+e8Pfi0/UYmU3wW3H58kr7akA==", + "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "estree-util-is-identifier-name": "^2.0.0", + "micromark-factory-mdx-expression": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0", + "vfile-message": "^3.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==", + "node_modules/micromark-extension-mdx-jsx/node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==", + "node_modules/micromark-extension-mdx-md": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-1.0.1.tgz", + "integrity": "sha512-7MSuj2S7xjOQXAjjkbjBsHkMtb+mDGVW6uI2dBL9snOBCbZmoNgDAeZ0nSn9j3T42UE/g2xVNMn18PJxZvkBEA==", + "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "micromark-util-types": "^1.0.0" }, - "engines": { - "node": ">=10" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-1.0.1.tgz", + "integrity": "sha512-7YA7hF6i5eKOfFUzZ+0z6avRG52GpWR8DL+kN47y3f2KhxbBZMhmxe7auOeaTBrW2DenbbZTf1ea9tA2hDpC2Q==", + "dev": true, + "dependencies": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^1.0.0", + "micromark-extension-mdx-jsx": "^1.0.0", + "micromark-extension-mdx-md": "^1.0.0", + "micromark-extension-mdxjs-esm": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-types": "^1.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==", + "node_modules/micromark-extension-mdxjs-esm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-1.0.5.tgz", + "integrity": "sha512-xNRBw4aoURcyz/S69B19WnZAkWJMxHMT5hE36GtDAyhoyn/8TuAeqjFJQlwk+MKQsUD7b3l7kFX+vlfVWgcX1w==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@types/estree": "^1.0.0", + "micromark-core-commonmark": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-events-to-acorn": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-position-from-estree": "^1.1.0", + "uvu": "^0.5.0", + "vfile-message": "^3.0.0" }, - "engines": { - "node": ">=7.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==" + "node_modules/micromark-extension-mdxjs-esm/node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } }, - "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==", - "engines": { - "node": ">=8" + "node_modules/micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" } }, - "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==", + "node_modules/micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" } }, - "node_modules/longest": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-2.0.1.tgz", - "integrity": "sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==", - "engines": { - "node": ">=0.10.0" + "node_modules/micromark-factory-mdx-expression": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-1.0.9.tgz", + "integrity": "sha512-jGIWzSmNfdnkJq05c7b0+Wv0Kfz3NJ3N4cBjnbO4zjXIlxJr+f8lk+5ZmwFvqdAbUy2q6B5rCY//g0QAAaXDWA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/estree": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-events-to-acorn": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-position-from-estree": "^1.0.0", + "uvu": "^0.5.0", + "vfile-message": "^3.0.0" } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "node_modules/micromark-factory-mdx-expression/node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dev": true, "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" }, - "bin": { - "loose-envify": "cli.js" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "tslib": "^2.0.3" + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" } }, - "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==", + "node_modules/micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" } }, - "node_modules/lz-string": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", - "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==", - "bin": { - "lz-string": "bin/bin.js" + "node_modules/micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" } }, - "node_modules/magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "sourcemap-codec": "^1.4.8" + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" } }, - "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==", + "node_modules/micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "micromark-util-symbol": "^1.0.0" } }, - "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==", - "bin": { - "semver": "bin/semver.js" + "node_modules/micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "optional": true + "node_modules/micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "tmpl": "1.0.5" + "micromark-util-symbol": "^1.0.0" } }, - "node_modules/mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + "node_modules/micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" + "node_modules/micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-events-to-acorn": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-1.2.3.tgz", + "integrity": "sha512-ij4X7Wuc4fED6UoLWkmo0xJQhsktfNh1J0m8g4PbIMPlx+ek/4YdW5mvbye8z/aZvAPUoxgXHrwVlXAPKMRp1w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "@types/unist": "^2.0.0", + "estree-util-visit": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0", + "vfile-message": "^3.0.0" } }, - "node_modules/memfs": { - "version": "3.4.12", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.12.tgz", - "integrity": "sha512-BcjuQn6vfqP+k100e0E9m61Hyqa//Brp+I3f0OBmN0ATHlFA8vx3Lt8z57R3u2bPqe3WGDBC+nF72fTH7isyEw==", + "node_modules/micromark-util-events-to-acorn/node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dev": true, "dependencies": { - "fs-monkey": "^1.0.3" + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" }, - "engines": { - "node": ">= 4.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/merge": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz", - "integrity": "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==" + "node_modules/micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "node_modules/micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } }, - "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==" + "node_modules/micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^1.0.0" + } }, - "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/micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" + "node_modules/micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" } }, + "node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -12834,6 +17269,18 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -12941,6 +17388,62 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "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==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -12952,6 +17455,151 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "node_modules/mongodb": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.2.0.tgz", + "integrity": "sha512-d7OSuGjGWDZ5usZPqfvb36laQ9CPhnWkAGHT61x5P95p/8nMVeH8asloMwW6GcYFeB0Vj4CB/1wOTDG2RA9BFA==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.0", + "bson": "^6.2.0", + "mongodb-connection-string-url": "^2.6.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "dependencies": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongoose": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.0.0.tgz", + "integrity": "sha512-PzwkLgm1Jhj0NQdgGfnFsu0QP9V1sBFgbavEgh/IPAUzKAagzvEhuaBuAQOQGjczVWnpIU9tBqyd02cOTgsPlA==", + "dependencies": { + "bson": "^6.2.0", + "kareem": "2.5.1", + "mongodb": "6.2.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "16.0.1" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose/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==" + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -12969,15 +17617,16 @@ "multicast-dns": "cli.js" } }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -12985,6 +17634,12 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -13008,6 +17663,22 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "node_modules/next-contentlayer": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/next-contentlayer/-/next-contentlayer-0.3.4.tgz", + "integrity": "sha512-UtUCwgAl159KwfhNaOwyiI7Lg6sdioyKMeh+E7jxx0CJ29JuXGxBEYmCI6+72NxFGIFZKx8lvttbbQhbnYWYSw==", + "dev": true, + "dependencies": { + "@contentlayer/core": "0.3.4", + "@contentlayer/utils": "0.3.4" + }, + "peerDependencies": { + "contentlayer": "0.3.4", + "next": "^12 || ^13", + "react": "*", + "react-dom": "*" + } + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -13017,6 +17688,80 @@ "tslib": "^2.0.3" } }, + "node_modules/node-abi": { + "version": "3.52.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.52.0.tgz", + "integrity": "sha512-JJ98b02z16ILv7859irtXn4oUaFWADtvkzy2c0IAatNVX2Mc9Yoh8z6hZInn3QwvMEYhHuQloYi+TTQy67SIdQ==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -13035,6 +17780,20 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "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", @@ -13073,6 +17832,17 @@ "node": ">=8" } }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -13211,6 +17981,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object.values": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", @@ -13273,6 +18055,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/oo-ascii-tree": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/oo-ascii-tree/-/oo-ascii-tree-1.93.0.tgz", + "integrity": "sha512-zbmrGCL/UsvxV2WlnsSrqdkdxEggxH7eA1HOk+hmimLQu+eLO4Y3VGqwt0VK04Nfe6iG6GnzRL5/XjH0j1v8bQ==", + "dev": true, + "engines": { + "node": ">= 14.17.0" + } + }, "node_modules/open": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", @@ -13290,115 +18081,21 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" } }, - "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==", - "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==", - "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==", - "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==", - "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==" - }, - "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==", - "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==", - "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": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -13467,6 +18164,26 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", + "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -13484,14 +18201,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -13561,6 +18270,26 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, + "node_modules/periscopic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", + "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + } + }, + "node_modules/periscopic/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -13741,9 +18470,9 @@ } }, "node_modules/postcss": { - "version": "8.4.19", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz", - "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { "type": "opencollective", @@ -13752,10 +18481,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -14904,6 +19637,66 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prebuild-install/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "node_modules/prebuild-install/node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/prebuild-install/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -14996,6 +19789,40 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/property-information": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.0.tgz", + "integrity": "sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/protobufjs": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -15026,6 +19853,16 @@ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -15081,6 +19918,12 @@ } ] }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true + }, "node_modules/quick-lru": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", @@ -15100,6 +19943,29 @@ "performance-now": "^2.1.0" } }, + "node_modules/randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "dev": true, + "dependencies": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/randomatic/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -15149,6 +20015,30 @@ "node": ">=0.10.0" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -15311,9 +20201,9 @@ "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, "node_modules/react-icons": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.6.0.tgz", - "integrity": "sha512-rR/L9m9340yO8yv1QT1QurxWQvWpbNHqVX0fzMln2HEb9TEIrQRGsqiNFQfiv9/JEUbyHmHPlNTB2LWm2Ttz0g==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz", + "integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==", "peerDependencies": { "react": "*" } @@ -15322,6 +20212,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/react-indiana-drag-scroll/-/react-indiana-drag-scroll-2.2.0.tgz", "integrity": "sha512-+W/3B2OQV0FrbdnsoIo4dww/xpH0MUQJz6ziQb7H+oBko3OCbXuzDFYnho6v6yhGrYDNWYPuFUewb89IONEl/A==", + "dev": true, "dependencies": { "classnames": "^2.2.6", "debounce": "^1.2.0", @@ -15341,6 +20232,669 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-markdown": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", + "integrity": "sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, + "node_modules/react-markdown/node_modules/@types/hast": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.3.tgz", + "integrity": "sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/react-markdown/node_modules/@types/mdast": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", + "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/react-markdown/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dev": true + }, + "node_modules/react-markdown/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-markdown/node_modules/mdast-util-from-markdown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", + "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/react-markdown/node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/react-markdown/node_modules/micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-core-commonmark": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", + "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-factory-destination": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-factory-label": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-factory-title": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-factory-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-util-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.0.1.tgz", + "integrity": "sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-util-chunked": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-util-classify-character": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-util-combine-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-util-decode-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/react-markdown/node_modules/micromark-util-html-tag-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/react-markdown/node_modules/micromark-util-normalize-identifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-util-resolve-all": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-util-subtokenize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", + "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/react-markdown/node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/react-markdown/node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/react-markdown/node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "dev": true, + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/react-markdown/node_modules/remark-rehype": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.0.0.tgz", + "integrity": "sha512-vx8x2MDMcxuE4lBmQ46zYUDfcFMmvg80WYX+UNLeG6ixjdCCLcw1lrgAukwBTuOFsS78eoAedHGn9sNM0w7TPw==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/react-markdown/node_modules/unified": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz", + "integrity": "sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/react-markdown/node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/react-markdown/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/react-markdown/node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/react-markdown/node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/react-markdown/node_modules/vfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", + "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -15607,6 +21161,21 @@ "jsesc": "bin/jsesc" } }, + "node_modules/rehype-stringify": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-9.0.4.tgz", + "integrity": "sha512-Uk5xu1YKdqobe5XpSskwPvo1XeHUUucWEQSl8hTrXt5selvca1e8K1EZ37E6YoZ4BT8BCqCdVfQW7OfHfthtVQ==", + "dev": true, + "dependencies": { + "@types/hast": "^2.0.0", + "hast-util-to-html": "^8.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", @@ -15615,6 +21184,146 @@ "node": ">= 0.10" } }, + "node_modules/remark-frontmatter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-4.0.1.tgz", + "integrity": "sha512-38fJrB0KnmD3E33a5jZC/5+gGAC2WKNiPw1/fdXJvijBlhA7RCsvJklrYJakS0HedninvaCYW8lQGf9C918GfA==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-frontmatter": "^1.0.0", + "micromark-extension-frontmatter": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-2.3.0.tgz", + "integrity": "sha512-g53hMkpM0I98MU266IzDFMrTD980gNF3BJnkyFcmN+dD873mQeD5rdMO3Y2X+x8umQfbSE0PcoEDl7ledSA+2g==", + "dev": true, + "dependencies": { + "mdast-util-mdx": "^2.0.0", + "micromark-extension-mdxjs": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx-frontmatter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/remark-mdx-frontmatter/-/remark-mdx-frontmatter-1.1.1.tgz", + "integrity": "sha512-7teX9DW4tI2WZkXS4DBxneYSY7NHiXl4AKdWDO9LXVweULlCT8OPWsOjLEnMIXViN1j+QcY8mfbq3k0EK6x3uA==", + "dev": true, + "dependencies": { + "estree-util-is-identifier-name": "^1.0.0", + "estree-util-value-to-estree": "^1.0.0", + "js-yaml": "^4.0.0", + "toml": "^3.0.0" + }, + "engines": { + "node": ">=12.2.0" + } + }, + "node_modules/remark-mdx-frontmatter/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/remark-mdx-frontmatter/node_modules/estree-util-is-identifier-name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-1.1.0.tgz", + "integrity": "sha512-OVJZ3fGGt9By77Ix9NhaRbzfbDV/2rx9EP7YIDJTmsZSEc5kYn2vWcNccYyahJL2uAQZK2a5Or2i0wtIKTPoRQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/remark-mdx-frontmatter/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/remark-parse": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", + "integrity": "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-10.1.0.tgz", + "integrity": "sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==", + "dev": true, + "dependencies": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-to-hast": "^12.1.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype/node_modules/mdast-util-to-hast": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", + "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", + "dev": true, + "dependencies": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-definitions": "^5.0.0", + "micromark-util-sanitize-uri": "^1.1.0", + "trim-lines": "^3.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remarkable": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.7.4.tgz", + "integrity": "sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==", + "dev": true, + "dependencies": { + "argparse": "^1.0.10", + "autolinker": "~0.28.0" + }, + "bin": { + "remarkable": "bin/remarkable.js" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", @@ -15627,6 +21336,24 @@ "strip-ansi": "^6.0.1" } }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -15675,57 +21402,6 @@ "node": ">=8" } }, - "node_modules/resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", - "dependencies": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-dir/node_modules/global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dependencies": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-dir/node_modules/global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", - "dependencies": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-dir/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -15734,18 +21410,6 @@ "node": ">=8" } }, - "node_modules/resolve-global": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", - "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", - "optional": true, - "dependencies": { - "global-dirs": "^0.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/resolve-url-loader": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", @@ -15810,18 +21474,6 @@ "node": ">=10" } }, - "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==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -15921,14 +21573,6 @@ "node": ">=8" } }, - "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==", - "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", @@ -15951,12 +21595,16 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, "dependencies": { - "tslib": "^2.1.0" + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" } }, "node_modules/safe-buffer": { @@ -16079,6 +21727,19 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -16096,9 +21757,9 @@ } }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -16242,11 +21903,57 @@ "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": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "node_modules/set-getter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.1.tgz", + "integrity": "sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==", + "dev": true, + "dependencies": { + "to-object-path": "^0.3.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sharp": { + "version": "0.32.6", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", + "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.2", + "node-addon-api": "^6.1.0", + "prebuild-install": "^7.1.1", + "semver": "^7.5.4", + "simple-get": "^4.0.1", + "tar-fs": "^3.0.4", + "tunnel-agent": "^0.6.0" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/sharp/node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -16287,11 +21994,76 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", + "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" + }, "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==" }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "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/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "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": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -16378,6 +22150,24 @@ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/spdy": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", @@ -16449,6 +22239,16 @@ "node": ">= 0.8" } }, + "node_modules/streamx": { + "version": "2.15.6", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.6.tgz", + "integrity": "sha512-q+vQL4AAz+FdfT137VF69Cc/APqUbxy+MDOImRrMvchJpigHj9GksgDU2LYbO9rx7RX6osWgxJB2WxhYv4SZAw==", + "dev": true, + "dependencies": { + "fast-fifo": "^1.1.0", + "queue-tick": "^1.0.1" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -16536,6 +22336,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-entities": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz", + "integrity": "sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==", + "dev": true, + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/stringify-object": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", @@ -16568,6 +22382,24 @@ "node": ">=8" } }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-color": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/strip-color/-/strip-color-0.1.0.tgz", + "integrity": "sha512-p9LsUieSjWNNAxVCXLeilaDlmuUOrDS5/dF9znM1nZc7EGX5+zEFC0bEevsNIaldjlks+2jns5Siz6F9iK6jwA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strip-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", @@ -16633,6 +22465,15 @@ "webpack": "^5.0.0" } }, + "node_modules/style-to-object": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", + "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", + "dev": true, + "dependencies": { + "inline-style-parser": "0.1.1" + } + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -16791,6 +22632,25 @@ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "dev": true + }, + "node_modules/tailwind-merge": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.1.0.tgz", + "integrity": "sha512-l11VvI4nSwW7MtLSLYT4ldidDEUwQAMWuSHk7l4zcXZDgnCRa0V3OdCwFfM7DCzakVXMNRwAeje9maFFXT71dQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.23.5" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz", @@ -16844,6 +22704,55 @@ "node": ">=6" } }, + "node_modules/tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "dependencies": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, + "node_modules/tar-stream": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz", + "integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", @@ -16973,27 +22882,51 @@ "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==" }, - "node_modules/through": { + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2/node_modules/readable-stream": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "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/through2/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/through2/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/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -17007,6 +22940,36 @@ "node": ">=4" } }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -17026,6 +22989,12 @@ "node": ">=0.6" } }, + "node_modules/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", + "dev": true + }, "node_modules/tough-cookie": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", @@ -17059,6 +23028,16 @@ "node": ">=8" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/trim-repeated": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", @@ -17071,68 +23050,26 @@ "node": ">=0.10.0" } }, + "node_modules/trough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "optional": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node/node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "optional": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ts-node/node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "optional": true + "node_modules/ts-pattern": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-4.3.0.tgz", + "integrity": "sha512-pefrkcd4lmIVR0LA49Imjf9DYLK8vtWhqBPA3Ya1ir8xCW0O2yjL9dsCVvI7pCodLC5q7smNpEtDR2yVulQxOg==", + "dev": true }, "node_modules/tsconfig-paths": { "version": "3.14.1", @@ -17188,6 +23125,24 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/typanion": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/typanion/-/typanion-3.14.0.tgz", + "integrity": "sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug==", + "dev": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -17230,6 +23185,12 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -17239,9 +23200,10 @@ } }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -17300,6 +23262,37 @@ "node": ">=4" } }, + "node_modules/unified": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "bail": "^2.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -17311,6 +23304,159 @@ "node": ">=8" } }, + "node_modules/unist-util-generated": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", + "integrity": "sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", + "integrity": "sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position-from-estree": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-1.1.2.tgz", + "integrity": "sha512-poZa0eXpS+/XpoQwGwl79UUdea4ol2ZuCYguVaJS4qzIOMDzbqz8a3erUCOmubSZkaOuGamb3tX790iwOIROww==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dev": true + }, + "node_modules/unist-util-remove-position/node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position/node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position/node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -17423,11 +23569,32 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "optional": true + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "dev": true, + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uvu/node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "engines": { + "node": ">=6" + } }, "node_modules/v8-to-istanbul": { "version": "8.1.1", @@ -17450,6 +23617,83 @@ "node": ">= 0.8" } }, + "node_modules/vfile": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", + "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-4.1.0.tgz", + "integrity": "sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message/node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dev": true + }, + "node_modules/vfile-message/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile/node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -17498,12 +23742,23 @@ "minimalistic-assert": "^1.0.0" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dependencies": { - "defaults": "^1.0.3" + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "dev": true, + "engines": { + "node": ">= 8" } }, "node_modules/web-vitals": { @@ -17952,6 +24207,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -18400,15 +24663,6 @@ "node": ">=10" } }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "optional": true, - "engines": { - "node": ">=6" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -18419,9 +24673,33 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } }, "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==" + }, "@adobe/css-tools": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", @@ -19605,11 +25883,18 @@ } }, "@babel/runtime": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", - "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.6.tgz", + "integrity": "sha512-zHd0eUrf5GZoOWVCXp6koAKQTfZV07eit6bGPmJgnZdnSAvvZee6zniW2XMF7Cmc4ISOOnPy3QaSiIJGJkVEDQ==", "requires": { - "regenerator-runtime": "^0.13.10" + "regenerator-runtime": "^0.14.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + } } }, "@babel/runtime-corejs3": { @@ -19663,219 +25948,126 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, - "@commitlint/config-validator": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.1.0.tgz", - "integrity": "sha512-Q1rRRSU09ngrTgeTXHq6ePJs2KrI+axPTgkNYDWSJIuS1Op4w3J30vUfSXjwn5YEJHklK3fSqWNHmBhmTR7Vdg==", - "optional": true, + "@contentlayer/cli": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@contentlayer/cli/-/cli-0.3.4.tgz", + "integrity": "sha512-vNDwgLuhYNu+m70NZ3XK9kexKNguuxPXg7Yvzj3B34cEilQjjzSrcTY/i+AIQm9V7uT5GGshx9ukzPf+SmoszQ==", + "dev": true, "requires": { - "@commitlint/types": "^17.0.0", - "ajv": "^8.11.0" - }, - "dependencies": { - "ajv": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", - "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", - "optional": 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" - } - }, - "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==", - "optional": true - } + "@contentlayer/core": "0.3.4", + "@contentlayer/utils": "0.3.4", + "clipanion": "^3.2.1", + "typanion": "^3.12.1" } }, - "@commitlint/execute-rule": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.0.0.tgz", - "integrity": "sha512-nVjL/w/zuqjCqSJm8UfpNaw66V9WzuJtQvEnCrK4jDw6qKTmZB+1JQ8m6BQVZbNBcwfYdDNKnhIhqI0Rk7lgpQ==", - "optional": true + "@contentlayer/client": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@contentlayer/client/-/client-0.3.4.tgz", + "integrity": "sha512-QSlLyc3y4PtdC5lFw0L4wTZUH8BQnv2nk37hNCsPAqGf+dRO7TLAzdc+2/mVIRgK+vSH+pSOzjLsQpFxxXRTZA==", + "dev": true, + "requires": { + "@contentlayer/core": "0.3.4" + } }, - "@commitlint/load": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.3.0.tgz", - "integrity": "sha512-u/pV6rCAJrCUN+HylBHLzZ4qj1Ew3+eN9GBPhNi9otGxtOfA8b+8nJSxaNbcC23Ins/kcpjGf9zPSVW7628Umw==", - "optional": true, + "@contentlayer/core": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@contentlayer/core/-/core-0.3.4.tgz", + "integrity": "sha512-o68oBLwfYZ+2vtgfk1lgHxOl3LoxvRNiUfeQ8IWFWy/L4wnIkKIqLZX01zlRE5IzYM+ZMMN5V0cKQlO7DsyR9g==", + "dev": true, "requires": { - "@commitlint/config-validator": "^17.1.0", - "@commitlint/execute-rule": "^17.0.0", - "@commitlint/resolve-extends": "^17.3.0", - "@commitlint/types": "^17.0.0", - "@types/node": "^14.0.0", - "chalk": "^4.1.0", - "cosmiconfig": "^7.0.0", - "cosmiconfig-typescript-loader": "^4.0.0", - "lodash.isplainobject": "^4.0.6", - "lodash.merge": "^4.6.2", - "lodash.uniq": "^4.5.0", - "resolve-from": "^5.0.0", - "ts-node": "^10.8.1", - "typescript": "^4.6.4" + "@contentlayer/utils": "0.3.4", + "camel-case": "^4.1.2", + "comment-json": "^4.2.3", + "esbuild": "0.17.x || 0.18.x", + "gray-matter": "^4.0.3", + "mdx-bundler": "^9.2.1", + "rehype-stringify": "^9.0.3", + "remark-frontmatter": "^4.0.1", + "remark-parse": "^10.0.2", + "remark-rehype": "^10.1.0", + "source-map-support": "^0.5.21", + "type-fest": "^3.12.0", + "unified": "^10.1.2" }, "dependencies": { - "@types/node": { - "version": "14.18.33", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.33.tgz", - "integrity": "sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==", - "optional": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "optional": 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==", - "optional": 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==", - "optional": 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==", - "optional": 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==", - "optional": 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==", - "optional": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "optional": true + "type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true } } }, - "@commitlint/resolve-extends": { - "version": "17.3.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.3.0.tgz", - "integrity": "sha512-Lf3JufJlc5yVEtJWC8o4IAZaB8FQAUaVlhlAHRACd0TTFizV2Lk2VH70et23KgvbQNf7kQzHs/2B4QZalBv6Cg==", - "optional": true, - "requires": { - "@commitlint/config-validator": "^17.1.0", - "@commitlint/types": "^17.0.0", - "import-fresh": "^3.0.0", - "lodash.mergewith": "^4.6.2", - "resolve-from": "^5.0.0", - "resolve-global": "^1.0.0" - } - }, - "@commitlint/types": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.0.0.tgz", - "integrity": "sha512-hBAw6U+SkAT5h47zDMeOu3HSiD0SODw4Aq7rRNh1ceUmL7GyLKYhPbUvlRWqZ65XjBLPHZhFyQlRaPNz8qvUyQ==", - "optional": true, + "@contentlayer/source-files": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@contentlayer/source-files/-/source-files-0.3.4.tgz", + "integrity": "sha512-4njyn0OFPu7WY4tAjMxiJgWOKeiHuBOGdQ36EYE03iij/pPPRbiWbL+cmLccYXUFEW58mDwpqROZZm6pnxjRDQ==", + "dev": true, "requires": { - "chalk": "^4.1.0" + "@contentlayer/core": "0.3.4", + "@contentlayer/utils": "0.3.4", + "chokidar": "^3.5.3", + "fast-glob": "^3.2.12", + "gray-matter": "^4.0.3", + "imagescript": "^1.2.16", + "micromatch": "^4.0.5", + "ts-pattern": "^4.3.0", + "unified": "^10.1.2", + "yaml": "^2.3.1", + "zod": "^3.21.4" }, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "optional": 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==", - "optional": 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==", - "optional": 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==", - "optional": 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==", - "optional": 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==", - "optional": true, - "requires": { - "has-flag": "^4.0.0" - } + "yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true } } }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "optional": true, + "@contentlayer/source-remote-files": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@contentlayer/source-remote-files/-/source-remote-files-0.3.4.tgz", + "integrity": "sha512-cyiv4sNUySZvR0uAKlM+kSAELzNd2h2QT1R2e41dRKbwOUVxeLfmGiLugr0aVac6Q3xYcD99dbHyR1xWPV+w9w==", + "dev": true, "requires": { - "@jridgewell/trace-mapping": "0.3.9" + "@contentlayer/core": "0.3.4", + "@contentlayer/source-files": "0.3.4", + "@contentlayer/utils": "0.3.4" + } + }, + "@contentlayer/utils": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@contentlayer/utils/-/utils-0.3.4.tgz", + "integrity": "sha512-ZWWOhbUWYQ2QHoLIlcUnEo7X4ZbwcyFPuzVQWWMkK43BxCveyQtZwBIzfyx54sqVzi0GUmKP8bHzsLQT0QxaLQ==", + "dev": true, + "requires": { + "@effect-ts/core": "^0.60.5", + "@effect-ts/otel": "^0.15.1", + "@effect-ts/otel-exporter-trace-otlp-grpc": "^0.15.1", + "@effect-ts/otel-sdk-trace-node": "^0.15.1", + "@js-temporal/polyfill": "^0.4.4", + "@opentelemetry/api": "^1.4.1", + "@opentelemetry/core": "^1.13.0", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.39.1", + "@opentelemetry/resources": "^1.13.0", + "@opentelemetry/sdk-trace-base": "^1.13.0", + "@opentelemetry/sdk-trace-node": "^1.13.0", + "@opentelemetry/semantic-conventions": "^1.13.0", + "chokidar": "^3.5.3", + "hash-wasm": "^4.9.0", + "inflection": "^2.0.1", + "memfs": "^3.5.1", + "oo-ascii-tree": "^1.84.0", + "ts-pattern": "^4.3.0", + "type-fest": "^3.12.0" }, "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "optional": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } + "type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true } } }, @@ -19996,24 +26188,248 @@ "@csstools/postcss-unset-value": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", - "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", - "requires": {} + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==" }, "@csstools/selector-specificity": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", - "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", - "requires": {} + "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==" + }, + "@effect-ts/core": { + "version": "0.60.5", + "resolved": "https://registry.npmjs.org/@effect-ts/core/-/core-0.60.5.tgz", + "integrity": "sha512-qi1WrtJA90XLMnj2hnUszW9Sx4dXP03ZJtCc5DiUBIOhF4Vw7plfb65/bdBySPoC9s7zy995TdUX1XBSxUkl5w==", + "dev": true, + "requires": { + "@effect-ts/system": "^0.57.5" + } + }, + "@effect-ts/otel": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@effect-ts/otel/-/otel-0.15.1.tgz", + "integrity": "sha512-AmZJHl7t0+Peh7Yb2+hqn6r9+rd9/UfeA4AMV9h0YGTdOyouyFfD3wzWlxnAUzAQ4Lrod4kC7Noruret4EpqpA==", + "dev": true + }, + "@effect-ts/otel-exporter-trace-otlp-grpc": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@effect-ts/otel-exporter-trace-otlp-grpc/-/otel-exporter-trace-otlp-grpc-0.15.1.tgz", + "integrity": "sha512-47gAg0O2pW5Jlo86jfzjdkwL5a7Bzb+Kj5WTmdu4CxYRfWn9ytKjuuYIfsNDW8neuhdKzn+P5wCddgEh0glYyQ==", + "dev": true, + "requires": { + "@effect-ts/otel": "^0.15.1" + } + }, + "@effect-ts/otel-sdk-trace-node": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@effect-ts/otel-sdk-trace-node/-/otel-sdk-trace-node-0.15.1.tgz", + "integrity": "sha512-a2sF0ylmn8xOJs8fNeT/spJ1gUcsksAJCALxo9WOfuTCMtTwMVtVhCKEPEeQoL7wFqU+JgPkVdP91+FJ/Rkeow==", + "dev": true, + "requires": { + "@effect-ts/otel": "^0.15.1" + } + }, + "@effect-ts/system": { + "version": "0.57.5", + "resolved": "https://registry.npmjs.org/@effect-ts/system/-/system-0.57.5.tgz", + "integrity": "sha512-/crHGujo0xnuHIYNc1VgP0HGJGFSoSqq88JFXe6FmFyXPpWt8Xu39LyLg7rchsxfXFeEdA9CrIZvLV5eswXV5g==", + "dev": true + }, + "@esbuild-plugins/node-resolve": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-resolve/-/node-resolve-0.1.4.tgz", + "integrity": "sha512-haFQ0qhxEpqtWWY0kx1Y5oE3sMyO1PcoSiWEPrAw6tm/ZOOLXjSs6Q+v1v9eyuVF0nNt50YEvrcrvENmyoMv5g==", + "dev": true, + "requires": { + "@types/resolve": "^1.17.1", + "debug": "^4.3.1", + "escape-string-regexp": "^4.0.0", + "resolve": "^1.19.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + } + } + }, + "@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "dev": true, + "optional": true + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==" }, "@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", + "espree": "^9.6.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -20027,9 +26443,9 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "requires": { "type-fest": "^0.20.2" } @@ -20049,36 +26465,62 @@ } } }, + "@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==" + }, + "@fal-works/esbuild-plugin-global-externals": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz", + "integrity": "sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==", + "dev": true + }, "@floating-ui/core": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.0.2.tgz", - "integrity": "sha512-Skfy0YS3NJ5nV9us0uuPN0HDk1Q4edljaOhRBJGDWs9EBa7ZVMYBHRFlhLvvmwEoaIM9BlH6QJFn9/uZg0bACg==" + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.2.tgz", + "integrity": "sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==", + "dev": true, + "requires": { + "@floating-ui/utils": "^0.1.3" + } }, "@floating-ui/dom": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.0.6.tgz", - "integrity": "sha512-kt/tg1oip9OAH1xjCTcx1OpcUpu9rjDw3GKJ/rEhUqhO7QyJWfrHU0DpLTNsH67+JyFL5Kv9X1utsXwKFVtyEQ==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", + "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", + "dev": true, "requires": { - "@floating-ui/core": "^1.0.2" + "@floating-ui/core": "^1.4.2", + "@floating-ui/utils": "^0.1.3" } }, - "@floating-ui/react-dom": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-1.0.1.tgz", - "integrity": "sha512-UW0t1Gi8ikbDRr8cQPVcqIDMBwUEENe5V4wlHWdrJ5egFnRQFBV9JirauTBFI6S8sM1qFUC1i+qa3g87E6CLTw==", + "@floating-ui/react": { + "version": "0.26.4", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.4.tgz", + "integrity": "sha512-pRiEz+SiPyfTcckAtLkEf3KJ/sUbB4X4fWMcDm27HT2kfAq+dH+hMc2VoOkNaGpDE35a2PKo688ugWeHaToL3g==", + "dev": true, "requires": { - "@floating-ui/dom": "^1.0.5" + "@floating-ui/react-dom": "^2.0.3", + "@floating-ui/utils": "^0.1.5", + "tabbable": "^6.0.1" } }, - "@floating-ui/react-dom-interactions": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom-interactions/-/react-dom-interactions-0.9.3.tgz", - "integrity": "sha512-oHwFLxySRtmhgwg7ZdWswvDDi+ld4mEtxu6ngOd7mRC5L1Rk6adjSfOBOHDxea+ItAWmds8m6A725sn1HQtUyQ==", + "@floating-ui/react-dom": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.4.tgz", + "integrity": "sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==", + "dev": true, "requires": { - "@floating-ui/react-dom": "^1.0.0", - "aria-hidden": "^1.1.3" + "@floating-ui/dom": "^1.5.1" } }, + "@floating-ui/utils": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", + "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==", + "dev": true + }, "@googlemaps/js-api-loader": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.15.1.tgz", @@ -20087,12 +26529,68 @@ "fast-deep-equal": "^3.1.3" } }, + "@grpc/grpc-js": { + "version": "1.9.13", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.13.tgz", + "integrity": "sha512-OEZZu9v9AA+7/tghMDE8o5DAMD5THVnwSqDWuh7PPYO5287rTyqy0xEHT6/e4pbqSrhyLPdQFsam4TwFQVVIIw==", + "dev": true, + "requires": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + } + }, + "@grpc/proto-loader": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", + "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", + "dev": true, + "requires": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.4", + "yargs": "^17.7.2" + }, + "dependencies": { + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "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.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + } + } + }, "@humanwhocodes/config-array": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", - "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "requires": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" } @@ -20103,9 +26601,9 @@ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==" }, "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==" }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -20648,16 +27146,110 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "@js-temporal/polyfill": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@js-temporal/polyfill/-/polyfill-0.4.4.tgz", + "integrity": "sha512-2X6bvghJ/JAoZO52lbgyAPFj8uCflhTo2g7nkFzEQdXd/D8rEeD4HtmTEpmtGCva260fcd66YNXBOYdnmHqSOg==", + "dev": true, + "requires": { + "jsbi": "^4.3.0", + "tslib": "^2.4.1" + } + }, "@leichtgewicht/ip-codec": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "requires": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + } + }, "@mapbox/point-geometry": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==" }, + "@mdx-js/esbuild": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@mdx-js/esbuild/-/esbuild-2.3.0.tgz", + "integrity": "sha512-r/vsqsM0E+U4Wr0DK+0EfmABE/eg+8ITW4DjvYdh3ve/tK2safaqHArNnaqbOk1DjYGrhxtoXoGaM3BY8fGBTA==", + "dev": true, + "requires": { + "@mdx-js/mdx": "^2.0.0", + "node-fetch": "^3.0.0", + "vfile": "^5.0.0" + }, + "dependencies": { + "node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + } + } + }, + "@mdx-js/mdx": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-2.3.0.tgz", + "integrity": "sha512-jLuwRlz8DQfQNiUCJR50Y09CGPq3fLtmtUQfVrj79E0JWu3dvsVcxVIcfhR5h0iXu+/z++zDrYeiJqifRynJkA==", + "dev": true, + "requires": { + "@types/estree-jsx": "^1.0.0", + "@types/mdx": "^2.0.0", + "estree-util-build-jsx": "^2.0.0", + "estree-util-is-identifier-name": "^2.0.0", + "estree-util-to-js": "^1.1.0", + "estree-walker": "^3.0.0", + "hast-util-to-estree": "^2.0.0", + "markdown-extensions": "^1.0.0", + "periscopic": "^3.0.0", + "remark-mdx": "^2.0.0", + "remark-parse": "^10.0.0", + "remark-rehype": "^10.0.0", + "unified": "^10.0.0", + "unist-util-position-from-estree": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0" + }, + "dependencies": { + "estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0" + } + } + } + }, + "@mongodb-js/saslprep": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.1.tgz", + "integrity": "sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ==", + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, "@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -20705,6 +27297,329 @@ "fastq": "^1.6.0" } }, + "@opentelemetry/api": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz", + "integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==", + "dev": true + }, + "@opentelemetry/api-logs": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.39.1.tgz", + "integrity": "sha512-9BJ8lMcOzEN0lu+Qji801y707oFO4xT3db6cosPvl+k7ItUHKN5ofWqtSbM9gbt1H4JJ/4/2TVrqI9Rq7hNv6Q==", + "dev": true, + "requires": { + "@opentelemetry/api": "^1.0.0" + } + }, + "@opentelemetry/context-async-hooks": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.19.0.tgz", + "integrity": "sha512-0i1ECOc9daKK3rjUgDDXf0GDD5XfCou5lXnt2DALIc2qKoruPPcesobNKE54laSVUWnC3jX26RzuOa31g0V32A==", + "dev": true + }, + "@opentelemetry/core": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.19.0.tgz", + "integrity": "sha512-w42AukJh3TP8R0IZZOVJVM/kMWu8g+lm4LzT70WtuKqhwq7KVhcDzZZuZinWZa6TtQCl7Smt2wolEYzpHabOgw==", + "dev": true, + "requires": { + "@opentelemetry/semantic-conventions": "1.19.0" + } + }, + "@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.39.1.tgz", + "integrity": "sha512-l5RhLKx6U+yuLhMrtgavTDthX50E1mZM3/SSySC7OPZiArFHV/b/9x9jxAzrOgIQUDxyj4N0V9aLKSA2t7Qzxg==", + "dev": true, + "requires": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.13.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.39.1", + "@opentelemetry/otlp-transformer": "0.39.1", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/sdk-trace-base": "1.13.0" + }, + "dependencies": { + "@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dev": true, + "requires": { + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/sdk-trace-base": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.13.0.tgz", + "integrity": "sha512-moTiQtc0uPR1hQLt6gLDJH9IIkeBhgRb71OKjNHZPE1VF45fHtD6nBDi5J/DkTHTwYP5X3kBJLa3xN7ub6J4eg==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "dev": true + } + } + }, + "@opentelemetry/otlp-exporter-base": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.39.1.tgz", + "integrity": "sha512-Pv5X8fbi6jD/RJBePyn7MnCSuE6MbPB6dl+7YYBWJ5RcMGYMwvLXjd4h2jWsPV2TSUg38H/RoSP0aXvQ06Y7iw==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.13.0" + }, + "dependencies": { + "@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dev": true, + "requires": { + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "dev": true + } + } + }, + "@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.39.1.tgz", + "integrity": "sha512-u3ErFRQqQFKjjIMuwLWxz/tLPYInfmiAmSy//fGSCzCh2ZdJgqQjMOAxBgqFtCF2xFL+OmMhyuC2ThMzceGRWA==", + "dev": true, + "requires": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.13.0", + "@opentelemetry/otlp-exporter-base": "0.39.1", + "protobufjs": "^7.2.2" + }, + "dependencies": { + "@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dev": true, + "requires": { + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "dev": true + } + } + }, + "@opentelemetry/otlp-transformer": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.39.1.tgz", + "integrity": "sha512-0hgVnXXz5efI382B/24NxD4b6Zxlh7nxCdJkxkdmQMbn0yRiwoq/ZT+QG8eUL6JNzsBAV1WJlF5aJNsL8skHvw==", + "dev": true, + "requires": { + "@opentelemetry/api-logs": "0.39.1", + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/sdk-logs": "0.39.1", + "@opentelemetry/sdk-metrics": "1.13.0", + "@opentelemetry/sdk-trace-base": "1.13.0" + }, + "dependencies": { + "@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dev": true, + "requires": { + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/sdk-trace-base": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.13.0.tgz", + "integrity": "sha512-moTiQtc0uPR1hQLt6gLDJH9IIkeBhgRb71OKjNHZPE1VF45fHtD6nBDi5J/DkTHTwYP5X3kBJLa3xN7ub6J4eg==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "dev": true + } + } + }, + "@opentelemetry/propagator-b3": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.19.0.tgz", + "integrity": "sha512-v7y5IBOKBm0vP3yf0DHzlw4L2gL6tZ0KeeMTaxfO5IuomMffDbrGWcvYFp0Dt4LdZctTSK523rVLBB9FBHBciQ==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.19.0" + } + }, + "@opentelemetry/propagator-jaeger": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.19.0.tgz", + "integrity": "sha512-dedkOoTzKg+nYoLWCMp0Im+wo+XkTRW6aXhi8VQRtMW/9SNJGOllCJSu8llToLxMDF0+6zu7OCrKkevAof2tew==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.19.0" + } + }, + "@opentelemetry/resources": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.19.0.tgz", + "integrity": "sha512-RgxvKuuMOf7nctOeOvpDjt2BpZvZGr9Y0vf7eGtY5XYZPkh2p7e2qub1S2IArdBMf9kEbz0SfycqCviOu9isqg==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.19.0", + "@opentelemetry/semantic-conventions": "1.19.0" + } + }, + "@opentelemetry/sdk-logs": { + "version": "0.39.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.39.1.tgz", + "integrity": "sha512-/gmgKfZ1ZVFporKuwsewqIyvaUIGpv76JZ7lBpHQQPb37IMpaXO6pdqFI4ebHAWfNIm3akMyhmdtzivcgF3lgw==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0" + }, + "dependencies": { + "@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dev": true, + "requires": { + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "dev": true + } + } + }, + "@opentelemetry/sdk-metrics": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.13.0.tgz", + "integrity": "sha512-MOjZX6AnSOqLliCcZUrb+DQKjAWXBiGeICGbHAGe5w0BB18PJIeIo995lO5JSaFfHpmUMgJButTPfJJD27W3Vg==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/resources": "1.13.0", + "lodash.merge": "4.6.2" + }, + "dependencies": { + "@opentelemetry/core": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.13.0.tgz", + "integrity": "sha512-2dBX3Sj99H96uwJKvc2w9NOiNgbvAO6mOFJFramNkKfS9O4Um+VWgpnlAazoYjT6kUJ1MP70KQ5ngD4ed+4NUw==", + "dev": true, + "requires": { + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/resources": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.13.0.tgz", + "integrity": "sha512-euqjOkiN6xhjE//0vQYGvbStxoD/WWQRhDiO0OTLlnLBO9Yw2Gd/VoSx2H+svsebjzYk5OxLuREBmcdw6rbUNg==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.13.0", + "@opentelemetry/semantic-conventions": "1.13.0" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.13.0.tgz", + "integrity": "sha512-LMGqfSZkaMQXqewO0o1wvWr/2fQdCh4a3Sqlxka/UsJCe0cfLulh6x2aqnKLnsrSGiCq5rSCwvINd152i0nCqw==", + "dev": true + } + } + }, + "@opentelemetry/sdk-trace-base": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.19.0.tgz", + "integrity": "sha512-+IRvUm+huJn2KqfFW3yW/cjvRwJ8Q7FzYHoUNx5Fr0Lws0LxjMJG1uVB8HDpLwm7mg5XXH2M5MF+0jj5cM8BpQ==", + "dev": true, + "requires": { + "@opentelemetry/core": "1.19.0", + "@opentelemetry/resources": "1.19.0", + "@opentelemetry/semantic-conventions": "1.19.0" + } + }, + "@opentelemetry/sdk-trace-node": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.19.0.tgz", + "integrity": "sha512-TCiEq/cUjM15RFqBRwWomTVbOqzndWL4ILa7ZCu0zbjU1/XY6AgHkgrgAc7vGP6TjRqH4Xryuglol8tcIfbBUQ==", + "dev": true, + "requires": { + "@opentelemetry/context-async-hooks": "1.19.0", + "@opentelemetry/core": "1.19.0", + "@opentelemetry/propagator-b3": "1.19.0", + "@opentelemetry/propagator-jaeger": "1.19.0", + "@opentelemetry/sdk-trace-base": "1.19.0", + "semver": "^7.5.2" + } + }, + "@opentelemetry/semantic-conventions": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.19.0.tgz", + "integrity": "sha512-14jRpC8f5c0gPSwoZ7SbEJni1PqI+AhAE8m1bMz6v+RPM4OlP1PT2UHBJj5Qh/ALLPjhVU/aZUK3YyjTUqqQVg==", + "dev": true + }, "@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.9", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.9.tgz", @@ -20726,6 +27641,70 @@ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==" }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "dev": true + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "dev": true + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dev": true, + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "dev": true + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "dev": true + }, "@remix-run/router": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.3.tgz", @@ -21090,29 +28069,14 @@ "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "optional": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "optional": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "optional": true - }, - "@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "optional": true + "@types/acorn": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", + "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", + "dev": true, + "requires": { + "@types/estree": "*" + } }, "@types/aria-query": { "version": "4.2.2", @@ -21156,6 +28120,14 @@ "@babel/types": "^7.3.0" } }, + "@types/bcrypt": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.1.tgz", + "integrity": "sha512-dIIrEsLV1/v0AUNI8oHMaRRTSeVjoy5ID8oclJavtPj8CwPJoD1eFoNXEypuu6k091brEzBeOo3LlxeAH9zRZg==", + "requires": { + "@types/node": "*" + } + }, "@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -21190,6 +28162,15 @@ "@types/node": "*" } }, + "@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "requires": { + "@types/ms": "*" + } + }, "@types/eslint": { "version": "8.4.10", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", @@ -21213,6 +28194,15 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==" }, + "@types/estree-jsx": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.3.tgz", + "integrity": "sha512-pvQ+TKeRHeiUGRhvYwRrQ/ISnohKkSJR14fT2yqyZ4e9K5vqc7hrtY2Y1Dw0ZwAzQ6DQsxsaCUuSIIi8v0Cq6w==", + "dev": true, + "requires": { + "@types/estree": "*" + } + }, "@types/express": { "version": "4.17.14", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", @@ -21250,6 +28240,15 @@ "@types/node": "*" } }, + "@types/hast": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.8.tgz", + "integrity": "sha512-aMIqAlFd2wTIDZuvLbhUT+TGvMxrNC8ECUIVtH6xxy0sQLs3iu6NO8Kp/VT5je7i5ufnebXzdV1dNDMnvaH6IQ==", + "dev": true, + "requires": { + "@types/unist": "^2" + } + }, "@types/history": { "version": "4.7.11", "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", @@ -21308,11 +28307,32 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "dev": true, + "requires": { + "@types/unist": "^2" + } + }, + "@types/mdx": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.10.tgz", + "integrity": "sha512-Rllzc5KHk0Al5/WANwgSPl1/CwjqCy+AZrGd78zuK+jO9aDM6ffblZ+zIjgPNAaEBmlO0RYDvLNh7wD0zKVgEg==", + "dev": true + }, "@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" }, + "@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true + }, "@types/node": { "version": "16.18.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.3.tgz", @@ -21323,6 +28343,12 @@ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, + "@types/parse5": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz", + "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==", + "dev": true + }, "@types/prettier": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", @@ -21451,6 +28477,26 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" }, + "@types/unist": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", + "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==", + "dev": true + }, + "@types/webidl-conversions": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.2.tgz", + "integrity": "sha512-uNv6b/uGRLlCVmelat2rA8bcVd3k/42mV2EmjhPh6JLkd35T5bgwR/t6xy7a9MWhd9sixIeBUzhBenvk3NO+DQ==" + }, + "@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "requires": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, "@types/ws": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", @@ -21586,6 +28632,11 @@ "eslint-visitor-keys": "^3.3.0" } }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + }, "@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -21732,6 +28783,11 @@ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -21742,9 +28798,9 @@ } }, "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==" + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==" }, "acorn-globals": { "version": "6.0.0", @@ -21765,14 +28821,12 @@ "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==", - "requires": {} + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==" }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "requires": {} + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" }, "acorn-node": { "version": "1.8.2", @@ -21858,8 +28912,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "ansi-escapes": { "version": "4.3.2", @@ -21874,6 +28927,15 @@ "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==" }, + "ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha512-ewaIr5y+9CUTGFwZfpECUbFlGcC0GCw1oqR9RI6h1gQCd9Aj2GxSckCnPsVJnmfMZbwFYE+leZGASgkWl06Jow==", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -21887,6 +28949,12 @@ "color-convert": "^1.9.0" } }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", + "dev": true + }, "anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -21896,6 +28964,20 @@ "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==" + }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, "arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -21909,14 +28991,6 @@ "sprintf-js": "~1.0.2" } }, - "aria-hidden": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.2.tgz", - "integrity": "sha512-6y/ogyDTk/7YAe91T3E2PR1ALVKyM2QbTio5HwM+N1Q6CMlCKhvClyIjkckBswa0f2xJhjsfzIGa1yVSe1UMVA==", - "requires": { - "tslib": "^2.0.0" - } - }, "aria-query": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", @@ -21943,6 +29017,12 @@ "is-string": "^1.0.7" } }, + "array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true + }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -22010,6 +29090,12 @@ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" }, + "astring": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz", + "integrity": "sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==", + "dev": true + }, "async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", @@ -22025,6 +29111,15 @@ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" }, + "autolinker": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.28.1.tgz", + "integrity": "sha512-zQAFO1Dlsn69eXaO6+7YZc+v84aquQKbwpzCE3L0stj56ERn9hutFxPopViLjo9G+rWwjozRhgS5KJ25Xy19cQ==", + "dev": true, + "requires": { + "gulp-header": "^1.7.1" + } + }, "autoprefixer": { "version": "10.4.13", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", @@ -22075,6 +29170,12 @@ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==" }, + "b4a": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", + "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==", + "dev": true + }, "babel-jest": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", @@ -22194,8 +29295,7 @@ "babel-plugin-named-asset-import": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", - "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", - "requires": {} + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==" }, "babel-plugin-polyfill-corejs2": { "version": "0.3.3", @@ -22287,6 +29387,12 @@ "babel-plugin-transform-react-remove-prop-types": "^0.4.24" } }, + "bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "dev": true + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -22295,13 +29401,28 @@ "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "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==" }, + "bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "requires": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + } + }, + "bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + }, "bfj": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", @@ -22327,6 +29448,7 @@ "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", @@ -22442,10 +29564,16 @@ "node-int64": "^0.4.0" } }, + "bson": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.2.0.tgz", + "integrity": "sha512-ID1cI+7bazPDyL9wYy9GaQ8gEEohWvcUl/Yf0dIdutJxnmInEEyCsb4awy/OiBfall7zBA179Pahi3vCdFze3Q==" + }, "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" @@ -22466,11 +29594,6 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" }, - "cachedir": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", - "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==" - }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -22525,6 +29648,12 @@ "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==" }, + "ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -22540,10 +29669,29 @@ "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==" }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + "character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true + }, + "character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true + }, + "character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true + }, + "character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "dev": true }, "check-types": { "version": "11.2.2", @@ -22575,6 +29723,11 @@ } } }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, "chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -22593,7 +29746,8 @@ "classnames": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", - "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==", + "dev": true }, "clean-css": { "version": "5.3.1", @@ -22610,24 +29764,15 @@ } } }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "clipanion": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/clipanion/-/clipanion-3.2.1.tgz", + "integrity": "sha512-dYFdjLb7y1ajfxQopN05mylEpK9ZX0sO1/RfMXdfmwjlIsPkbh4p7A682x++zFPLDCo1x3p82dtljHf5cW2LKA==", + "dev": true, "requires": { - "restore-cursor": "^3.1.0" + "typanion": "^3.8.0" } }, - "cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==" - }, - "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==" - }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -22638,11 +29783,6 @@ "wrap-ansi": "^7.0.0" } }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==" - }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -22658,11 +29798,44 @@ "q": "^1.1.2" } }, + "coffee-script": { + "version": "1.12.7", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", + "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==", + "dev": true + }, "collect-v8-coverage": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" }, + "color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "requires": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "dependencies": { + "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 + } + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -22676,6 +29849,21 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "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==" + }, "colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -22694,48 +29882,28 @@ "delayed-stream": "~1.0.0" } }, + "comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true + }, "commander": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" }, - "commitizen": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-4.2.5.tgz", - "integrity": "sha512-9sXju8Qrz1B4Tw7kC5KhnvwYQN88qs2zbiB8oyMsnXZyJ24PPGiNM3nHr73d32dnE3i8VJEXddBFIbOgYSEXtQ==", - "requires": { - "cachedir": "2.3.0", - "cz-conventional-changelog": "3.3.0", - "dedent": "0.7.0", - "detect-indent": "6.1.0", - "find-node-modules": "^2.1.2", - "find-root": "1.1.0", - "fs-extra": "9.1.0", - "glob": "7.2.3", - "inquirer": "8.2.4", - "is-utf8": "^0.2.1", - "lodash": "4.17.21", - "minimist": "1.2.6", - "strip-bom": "4.0.0", - "strip-json-comments": "3.1.1" - }, - "dependencies": { - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - } + "comment-json": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.3.tgz", + "integrity": "sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==", + "dev": true, + "requires": { + "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", + "esprima": "^4.0.1", + "has-own-prop": "^2.0.0", + "repeat-string": "^1.6.1" } }, "common-path-prefix": { @@ -22800,6 +29968,67 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "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" + } + }, + "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 + }, + "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" + } + } + } + }, + "concat-with-sourcemaps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", + "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", + "dev": true, + "requires": { + "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 + } + } + }, "confusing-browser-globals": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", @@ -22810,6 +30039,11 @@ "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==" }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -22823,10 +30057,19 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, - "conventional-commit-types": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/conventional-commit-types/-/conventional-commit-types-3.0.0.tgz", - "integrity": "sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg==" + "contentlayer": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/contentlayer/-/contentlayer-0.3.4.tgz", + "integrity": "sha512-FYDdTUFaN4yqep0waswrhcXjmMJnPD5iXDTtxcUCGdklfuIrXM2xLx51xl748cHmGA6IsC+27YZFxU6Ym13QIA==", + "dev": true, + "requires": { + "@contentlayer/cli": "0.3.4", + "@contentlayer/client": "0.3.4", + "@contentlayer/core": "0.3.4", + "@contentlayer/source-files": "0.3.4", + "@contentlayer/source-remote-files": "0.3.4", + "@contentlayer/utils": "0.3.4" + } }, "convert-source-map": { "version": "1.9.0", @@ -22878,19 +30121,6 @@ "yaml": "^1.10.0" } }, - "cosmiconfig-typescript-loader": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.2.0.tgz", - "integrity": "sha512-NkANeMnaHrlaSSlpKGyvn2R4rqUDeE/9E5YHx+b4nwo0R8dZyAqcih8/gxpCZvqWP9Vf6xuLpMSzSgdVEIM78g==", - "optional": true, - "requires": {} - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "optional": true - }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -22917,8 +30147,7 @@ "css-declaration-sorter": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", - "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", - "requires": {} + "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==" }, "css-has-pseudo": { "version": "3.0.4", @@ -23001,8 +30230,7 @@ "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==", - "requires": {} + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==" }, "css-select": { "version": "4.3.0", @@ -23106,8 +30334,7 @@ "cssnano-utils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", - "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", - "requires": {} + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==" }, "csso": { "version": "4.2.0", @@ -23163,25 +30390,17 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, - "cz-conventional-changelog": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz", - "integrity": "sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==", - "requires": { - "@commitlint/load": ">6.1.1", - "chalk": "^2.4.1", - "commitizen": "^4.0.3", - "conventional-commit-types": "^3.0.0", - "lodash.map": "^4.5.1", - "longest": "^2.0.1", - "word-wrap": "^1.0.3" - } - }, "damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" }, + "data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true + }, "data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -23195,7 +30414,8 @@ "debounce": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", - "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "dev": true }, "debug": { "version": "4.3.4", @@ -23210,6 +30430,24 @@ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.2.tgz", "integrity": "sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA==" }, + "decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dev": true, + "requires": { + "character-entities": "^2.0.0" + } + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "requires": { + "mimic-response": "^3.1.0" + } + }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -23244,6 +30482,12 @@ } } }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, "deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -23262,14 +30506,6 @@ "execa": "^5.0.0" } }, - "defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "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", @@ -23294,25 +30530,31 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, + "dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true + }, "destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==" - }, - "detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==" + "detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==" }, "detect-newline": { "version": "3.1.0", @@ -23358,16 +30600,31 @@ "minimist": "^1.2.6" } }, + "devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "requires": { + "dequal": "^2.0.0" + } + }, + "diacritics-map": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz", + "integrity": "sha512-3omnDTYrGigU0i4cJjvaKwD52B8aoqyX/NEIkukFFkogBemsIbhSa1O414fpTp5nuszJG6lvQ5vBvDVNCbSsaQ==", + "dev": true + }, "didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "optional": true + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true }, "diff-sequences": { "version": "27.5.1", @@ -23496,7 +30753,8 @@ "easy-bem": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/easy-bem/-/easy-bem-1.1.1.tgz", - "integrity": "sha512-GJRqdiy2h+EXy6a8E6R+ubmqUM08BK0FWNq41k24fup6045biQ8NXxoXimiwegMQvFFV3t1emADdGNL1TlS61A==" + "integrity": "sha512-GJRqdiy2h+EXy6a8E6R+ubmqUM08BK0FWNq41k24fup6045biQ8NXxoXimiwegMQvFFV3t1emADdGNL1TlS61A==", + "dev": true }, "ee-first": { "version": "1.1.1", @@ -23542,6 +30800,15 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, "enhanced-resolve": { "version": "5.11.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.11.0.tgz", @@ -23653,6 +30920,36 @@ "is-symbol": "^1.0.2" } }, + "esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "requires": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -23724,48 +31021,47 @@ } }, "eslint": { - "version": "8.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz", - "integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==", - "requires": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.15.0", - "grapheme-splitter": "^1.0.4", + "globals": "^13.19.0", + "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "dependencies": { @@ -23810,9 +31106,9 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, "globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "requires": { "type-fest": "^0.20.2" } @@ -24040,8 +31336,7 @@ "eslint-plugin-react-hooks": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "requires": {} + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==" }, "eslint-plugin-testing-library": { "version": "5.9.1", @@ -24052,9 +31347,9 @@ } }, "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "requires": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -24076,9 +31371,9 @@ } }, "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==" + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==" }, "eslint-webpack-plugin": { "version": "3.2.0", @@ -24153,13 +31448,13 @@ } }, "espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "requires": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" } }, "esprima": { @@ -24168,9 +31463,9 @@ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "requires": { "estraverse": "^5.1.0" } @@ -24188,6 +31483,73 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" }, + "estree-util-attach-comments": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-2.1.1.tgz", + "integrity": "sha512-+5Ba/xGGS6mnwFbXIuQiDPTbuTxuMCooq3arVv7gPZtYpjp+VXH/NkHAP35OOefPhNG/UGqU3vt/LTABwcHX0w==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0" + } + }, + "estree-util-build-jsx": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-2.2.2.tgz", + "integrity": "sha512-m56vOXcOBuaF+Igpb9OPAy7f9w9OIkb5yhjsZuaPm7HoGi4oTOQi0h2+yZ+AtKklYFZ+rPC4n0wYCJCEU1ONqg==", + "dev": true, + "requires": { + "@types/estree-jsx": "^1.0.0", + "estree-util-is-identifier-name": "^2.0.0", + "estree-walker": "^3.0.0" + }, + "dependencies": { + "estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0" + } + } + } + }, + "estree-util-is-identifier-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-2.1.0.tgz", + "integrity": "sha512-bEN9VHRyXAUOjkKVQVvArFym08BTWB0aJPppZZr0UNyAqWsLaVfAqP7hbaTJjzHifmB5ebnR8Wm7r7yGN/HonQ==", + "dev": true + }, + "estree-util-to-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-1.2.0.tgz", + "integrity": "sha512-IzU74r1PK5IMMGZXUVZbmiu4A1uhiPgW5hm1GjcOfr4ZzHaMPpLNJjR7HjXiIOzi25nZDrgFTobHTkV5Q6ITjA==", + "dev": true, + "requires": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + } + }, + "estree-util-value-to-estree": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-1.3.0.tgz", + "integrity": "sha512-Y+ughcF9jSUJvncXwqRageavjrNPAI+1M/L3BI3PyLp1nmgYTGUXU6t5z1Y7OWuThoDdhPME07bQU+d5LxdJqw==", + "dev": true, + "requires": { + "is-plain-obj": "^3.0.0" + } + }, + "estree-util-visit": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-1.2.1.tgz", + "integrity": "sha512-xbgqcrkIVbIG+lI/gzbvd9SGTJL4zqJKBFttUl5pP27KhAjtMKbX/mQXJ7qgyXpMgVy/zvpm0xoQQaGL8OloOw==", + "dev": true, + "requires": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^2.0.0" + } + }, "estree-walker": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", @@ -24234,14 +31596,69 @@ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==" }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha512-AFASGfIlnIbkKPQwX1yHaDjFvh/1gyKJODme52V6IORh69uEYgZp0o9C+qsIGNVEiuuhQU0CSSl++Rlegg1qvA==", + "dev": true, "requires": { - "homedir-polyfill": "^1.0.1" + "fill-range": "^2.1.0" + }, + "dependencies": { + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } } }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true + }, "expect": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", @@ -24311,24 +31728,19 @@ } } }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } + "is-extendable": "^0.1.0" } }, "fast-deep-equal": { @@ -24336,6 +31748,12 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true + }, "fast-glob": { "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", @@ -24376,6 +31794,15 @@ "reusify": "^1.0.4" } }, + "fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "dev": true, + "requires": { + "format": "^0.2.0" + } + }, "faye-websocket": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", @@ -24392,12 +31819,14 @@ "bser": "2.1.1" } }, - "figures": { + "fetch-blob": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, "requires": { - "escape-string-regexp": "^1.0.5" + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" } }, "file-entry-cache": { @@ -24512,20 +31941,6 @@ "pkg-dir": "^4.1.0" } }, - "find-node-modules": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.1.3.tgz", - "integrity": "sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg==", - "requires": { - "findup-sync": "^4.0.0", - "merge": "^2.1.1" - } - }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" - }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -24535,17 +31950,6 @@ "path-exists": "^4.0.0" } }, - "findup-sync": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", - "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^4.0.2", - "resolve-dir": "^1.0.1" - } - }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -24570,16 +31974,33 @@ } }, "flowbite-react": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/flowbite-react/-/flowbite-react-0.3.5.tgz", - "integrity": "sha512-X2unmmXmS7DZbGTk+C7jf0qrtpPu4i/yo8OeHHd1XO5V5TAUjhAyYIcy3RwKjKS+WC+Fj/rMHe5QKBF7E0ryoA==", - "requires": { - "@floating-ui/react-dom": "^1.0.0", - "@floating-ui/react-dom-interactions": "^0.9.1", - "classnames": "^2.3.2", - "commitizen": "^4.2.5", - "react-icons": "^4.6.0", - "react-indiana-drag-scroll": "^2.2.0" + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/flowbite-react/-/flowbite-react-0.7.2.tgz", + "integrity": "sha512-Qq+yKW3955ZWjABzNq02NAS91rE86AHjCVRTXHXZ3dkcoGghqViIpsDCxOBo0NO7xnKkshYU3DocmczcP4pYTA==", + "dev": true, + "requires": { + "@floating-ui/react": "^0.26.2", + "contentlayer": "^0.3.4", + "flowbite": "^2.0.0", + "markdown-toc": "^1.2.0", + "next-contentlayer": "^0.3.4", + "react-icons": "^4.11.0", + "react-indiana-drag-scroll": "^2.2.0", + "react-markdown": "^9.0.0", + "sharp": "^0.32.6", + "tailwind-merge": "^2.0.0" + }, + "dependencies": { + "flowbite": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/flowbite/-/flowbite-2.2.1.tgz", + "integrity": "sha512-iiZyBTtriEDRHrqXZgpKHaxl4B2J8HZUP8Yn1RXozUDKszWHDVj4GxQqMMB9AJHRWOgXV/4E/LJZ/zqQgBUhWA==", + "dev": true, + "requires": { + "@popperjs/core": "^2.9.3", + "mini-svg-data-uri": "^1.4.3" + } + } } }, "follow-redirects": { @@ -24595,6 +32016,12 @@ "is-callable": "^1.1.3" } }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "dev": true + }, "fork-ts-checker-webpack-plugin": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", @@ -24708,6 +32135,21 @@ "mime-types": "^2.1.12" } }, + "format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "dev": true + }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "requires": { + "fetch-blob": "^3.1.2" + } + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -24723,6 +32165,12 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, "fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -24733,10 +32181,28 @@ "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==", + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.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==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", + "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==" }, "fs.realpath": { "version": "1.0.0", @@ -24770,6 +32236,22 @@ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" }, + "gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -24894,6 +32376,12 @@ } } }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true + }, "glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -24920,15 +32408,6 @@ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" }, - "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", - "optional": true, - "requires": { - "ini": "^1.3.4" - } - }, "global-modules": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", @@ -24999,10 +32478,33 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + }, + "gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "dev": true, + "requires": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + } + }, + "gulp-header": { + "version": "1.8.12", + "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-1.8.12.tgz", + "integrity": "sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==", + "dev": true, + "requires": { + "concat-with-sourcemaps": "*", + "lodash.template": "^4.4.0", + "through2": "^2.0.0" + } }, "gzip-size": { "version": "6.0.0", @@ -25040,6 +32542,12 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, + "has-own-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", + "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", + "dev": true + }, "has-property-descriptors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", @@ -25061,19 +32569,582 @@ "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": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "hash-wasm": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/hash-wasm/-/hash-wasm-4.11.0.tgz", + "integrity": "sha512-HVusNXlVqHe0fzIzdQOGolnFN6mX/fqcrSAOcTBXdvzrXVHwTz11vXeKRmkR5gTuwVpvHZEIyKoePDvuAR+XwQ==", + "dev": true + }, + "hast-util-from-parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz", + "integrity": "sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw==", + "dev": true, + "requires": { + "@types/hast": "^2.0.0", + "@types/unist": "^2.0.0", + "hastscript": "^7.0.0", + "property-information": "^6.0.0", + "vfile": "^5.0.0", + "vfile-location": "^4.0.0", + "web-namespaces": "^2.0.0" + } + }, + "hast-util-parse-selector": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz", + "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==", + "dev": true, + "requires": { + "@types/hast": "^2.0.0" + } + }, + "hast-util-raw": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-7.2.3.tgz", + "integrity": "sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg==", + "dev": true, + "requires": { + "@types/hast": "^2.0.0", + "@types/parse5": "^6.0.0", + "hast-util-from-parse5": "^7.0.0", + "hast-util-to-parse5": "^7.0.0", + "html-void-elements": "^2.0.0", + "parse5": "^6.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + } + }, + "hast-util-to-estree": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-2.3.3.tgz", + "integrity": "sha512-ihhPIUPxN0v0w6M5+IiAZZrn0LH2uZomeWwhn7uP7avZC6TE7lIiEh2yBMPr5+zi1aUCXq6VoYRgs2Bw9xmycQ==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0", + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^2.0.0", + "@types/unist": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "estree-util-attach-comments": "^2.0.0", + "estree-util-is-identifier-name": "^2.0.0", + "hast-util-whitespace": "^2.0.0", + "mdast-util-mdx-expression": "^1.0.0", + "mdast-util-mdxjs-esm": "^1.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.1", + "unist-util-position": "^4.0.0", + "zwitch": "^2.0.0" + } + }, + "hast-util-to-html": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-8.0.4.tgz", + "integrity": "sha512-4tpQTUOr9BMjtYyNlt0P50mH7xj0Ks2xpo8M943Vykljf99HW6EzulIoJP1N3eKOSScEHzyzi9dm7/cn0RfGwA==", + "dev": true, + "requires": { + "@types/hast": "^2.0.0", + "@types/unist": "^2.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-raw": "^7.0.0", + "hast-util-whitespace": "^2.0.0", + "html-void-elements": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + } + }, + "hast-util-to-jsx-runtime": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz", + "integrity": "sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "dependencies": { + "@types/hast": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.3.tgz", + "integrity": "sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ==", + "dev": true, + "requires": { + "@types/unist": "*" + } + }, + "@types/mdast": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", + "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "dev": true, + "requires": { + "@types/unist": "*" + } + }, + "@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dev": true + }, + "estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "dev": true + }, + "hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "requires": { + "@types/hast": "^3.0.0" + } + }, + "inline-style-parser": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.2.tgz", + "integrity": "sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ==", + "dev": true + }, + "mdast-util-from-markdown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", + "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", + "dev": true, + "requires": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + } + }, + "mdast-util-mdx-expression": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz", + "integrity": "sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==", + "dev": true, + "requires": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + } + }, + "mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "dev": true, + "requires": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + } + }, + "mdast-util-phrasing": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.0.0.tgz", + "integrity": "sha512-xadSsJayQIucJ9n053dfQwVu1kuXg7jCTdYsMK8rqzKZh52nLfSH/k0sAxE0u+pj/zKZX+o5wB+ML5mRayOxFA==", + "dev": true, + "requires": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + } + }, + "mdast-util-to-markdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", + "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", + "dev": true, + "requires": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + } + }, + "mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dev": true, + "requires": { + "@types/mdast": "^4.0.0" + } + }, + "micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "dev": true, + "requires": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-core-commonmark": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", + "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", + "dev": true, + "requires": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-factory-destination": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "dev": true, + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-factory-label": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "dev": true, + "requires": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "dev": true, + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-factory-title": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "dev": true, + "requires": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-factory-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "dev": true, + "requires": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.0.1.tgz", + "integrity": "sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==", + "dev": true, + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-chunked": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "dev": true, + "requires": { + "micromark-util-symbol": "^2.0.0" + } + }, + "micromark-util-classify-character": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "dev": true, + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-combine-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "dev": true, + "requires": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-decode-numeric-character-reference": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "dev": true, + "requires": { + "micromark-util-symbol": "^2.0.0" + } + }, + "micromark-util-decode-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "dev": true, + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true + }, + "micromark-util-html-tag-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "dev": true + }, + "micromark-util-normalize-identifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "dev": true, + "requires": { + "micromark-util-symbol": "^2.0.0" + } + }, + "micromark-util-resolve-all": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "dev": true, + "requires": { + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "micromark-util-subtokenize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", + "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", + "dev": true, + "requires": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "dev": true + }, + "micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "dev": true + }, + "style-to-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.5.tgz", + "integrity": "sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ==", + "dev": true, + "requires": { + "inline-style-parser": "0.2.2" + } + }, + "unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0" + } + }, + "unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0" + } + }, + "unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0" + } + }, + "unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + } + }, + "unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + } + } + } + }, + "hast-util-to-parse5": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-7.1.0.tgz", + "integrity": "sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw==", + "dev": true, + "requires": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + } + }, + "hast-util-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", + "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==", + "dev": true + }, + "hastscript": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", + "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", + "dev": true, + "requires": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^3.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + } + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "requires": { - "parse-passwd": "^1.0.0" - } - }, "hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -25151,6 +33222,18 @@ "terser": "^5.10.0" } }, + "html-url-attributes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.0.tgz", + "integrity": "sha512-/sXbVCWayk6GDVg3ctOX6nxaVj7So40FcFAnWlWGNAB1LpYKcV5Cd10APjPjW80O7zYW2MsjBV4zZ7IZO5fVow==", + "dev": true + }, + "html-void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz", + "integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==", + "dev": true + }, "html-webpack-plugin": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", @@ -25253,8 +33336,7 @@ "icss-utils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "requires": {} + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==" }, "idb": { "version": "7.1.1", @@ -25272,13 +33354,20 @@ "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + "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==" }, + "imagescript": { + "version": "1.2.17", + "resolved": "https://registry.npmjs.org/imagescript/-/imagescript-1.2.17.tgz", + "integrity": "sha512-gUibUVTqbd2AeakephgVshjTL9zqh7k4Ea9yk9z8O9jjn11qU3vQWbMRDWGVzQl1t/cLeHYjclefjx8HblXo9Q==", + "dev": true + }, "immer": { "version": "9.0.16", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz", @@ -25319,6 +33408,12 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" }, + "inflection": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-2.0.1.tgz", + "integrity": "sha512-wzkZHqpb4eGrOKBl34xy3umnYHx8Si5R1U4fwmdxLo5gdH6mEK8gclckTj/qWqy4Je0bsDYe/qazZYuO7xe3XQ==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -25338,72 +33433,11 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, - "inquirer": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", - "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", - "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.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.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==", - "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==", - "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==", - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } + "inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", + "dev": true }, "internal-slot": { "version": "1.0.3", @@ -25420,6 +33454,22 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==" }, + "is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "dev": true + }, + "is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dev": true, + "requires": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + } + }, "is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -25459,6 +33509,12 @@ "has-tostringtag": "^1.0.0" } }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true + }, "is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -25480,11 +33536,23 @@ "has-tostringtag": "^1.0.0" } }, + "is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "dev": true + }, "is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -25508,10 +33576,11 @@ "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==" + "is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "dev": true }, "is-map": { "version": "2.0.2", @@ -25556,11 +33625,29 @@ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" }, + "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-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" }, + "is-reference": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", + "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "dev": true, + "requires": { + "@types/estree": "*" + } + }, "is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -25631,16 +33718,6 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, - "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==" - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==" - }, "is-weakmap": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", @@ -25663,11 +33740,6 @@ "get-intrinsic": "^1.1.1" } }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" - }, "is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -25686,6 +33758,12 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true + }, "istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", @@ -26428,8 +34506,7 @@ "jest-pnp-resolver": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "requires": {} + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==" }, "jest-regex-util": { "version": "27.5.1", @@ -27188,11 +35265,6 @@ } } }, - "js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==" - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -27207,6 +35279,12 @@ "esprima": "^4.0.0" } }, + "jsbi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.3.0.tgz", + "integrity": "sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==", + "dev": true + }, "jsdom": { "version": "16.7.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", @@ -27294,6 +35372,11 @@ "object.assign": "^4.1.3" } }, + "kareem": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", + "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==" + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -27322,6 +35405,15 @@ "language-subtag-registry": "~0.3.2" } }, + "lazy-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", + "integrity": "sha512-7vp2Acd2+Kz4XkzxGxaB1FWOi8KjWIWsgdfD5MCb86DWvlLqhRPM+d6Pro3iNEL5VT9mstz5hKAlcd+QR6H3aA==", + "dev": true, + "requires": { + "set-getter": "^0.1.0" + } + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -27346,6 +35438,44 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "list-item": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/list-item/-/list-item-1.1.1.tgz", + "integrity": "sha512-S3D0WZ4J6hyM8o5SNKWaMYB1ALSacPZ2nHGEuCjmHZ+dc03gFeNZoNDcqfcnO4vDhTZmNrqrpYZCdXsRh22bzw==", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "extend-shallow": "^2.0.1", + "is-number": "^2.1.0", + "repeat-string": "^1.5.2" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -27374,22 +35504,23 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==", + "dev": true + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "optional": true - }, - "lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==" - }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -27400,145 +35531,815 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, - "lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "optional": true - }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" }, + "lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0" + } + }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" }, - "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==", + "long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "dev": true + }, + "longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "requires": { + "tslib": "^2.0.3" + } + }, + "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==", + "requires": { + "yallist": "^4.0.0" + } + }, + "lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==" + }, + "magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "requires": { + "sourcemap-codec": "^1.4.8" + } + }, + "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==", + "requires": { + "semver": "^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==", + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "requires": { + "tmpl": "1.0.5" + } + }, + "markdown-extensions": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz", + "integrity": "sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==", + "dev": true + }, + "markdown-link": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/markdown-link/-/markdown-link-0.1.1.tgz", + "integrity": "sha512-TurLymbyLyo+kAUUAV9ggR9EPcDjP/ctlv9QAFiqUH7c+t6FlsbivPo9OKTU8xdOx9oNd2drW/Fi5RRElQbUqA==", + "dev": true + }, + "markdown-toc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/markdown-toc/-/markdown-toc-1.2.0.tgz", + "integrity": "sha512-eOsq7EGd3asV0oBfmyqngeEIhrbkc7XVP63OwcJBIhH2EpG2PzFcbZdhy1jutXSlRBBVMNXHvMtSr5LAxSUvUg==", + "dev": true, + "requires": { + "concat-stream": "^1.5.2", + "diacritics-map": "^0.1.0", + "gray-matter": "^2.1.0", + "lazy-cache": "^2.0.2", + "list-item": "^1.1.1", + "markdown-link": "^0.1.1", + "minimist": "^1.2.0", + "mixin-deep": "^1.1.3", + "object.pick": "^1.2.0", + "remarkable": "^1.7.1", + "repeat-string": "^1.6.1", + "strip-color": "^0.1.0" + }, + "dependencies": { + "gray-matter": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-2.1.1.tgz", + "integrity": "sha512-vbmvP1Fe/fxuT2QuLVcqb2BfK7upGhhbLIt9/owWEvPYrZZEkelLcq2HqzxosV+PQ67dUFLaAeNpH7C4hhICAA==", + "dev": true, + "requires": { + "ansi-red": "^0.1.1", + "coffee-script": "^1.12.4", + "extend-shallow": "^2.0.1", + "js-yaml": "^3.8.1", + "toml": "^2.3.2" + } + }, + "toml": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/toml/-/toml-2.3.6.tgz", + "integrity": "sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ==", + "dev": true + } + } + }, + "math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "dev": true + }, + "mdast-util-definitions": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", + "integrity": "sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + } + }, + "mdast-util-frontmatter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-1.0.1.tgz", + "integrity": "sha512-JjA2OjxRqAa8wEG8hloD0uTU0kdn8kbtOWpPP94NBkfAlbxn4S8gCGf/9DwFtEeGPXrDcNXdiDjVaRdUFqYokw==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0", + "micromark-extension-frontmatter": "^1.0.0" + } + }, + "mdast-util-mdx": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-2.0.1.tgz", + "integrity": "sha512-38w5y+r8nyKlGvNjSEqWrhG0w5PmnRA+wnBvm+ulYCct7nsGYhFVb0lljS9bQav4psDAS1eGkP2LMVcZBi/aqw==", + "dev": true, + "requires": { + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-mdx-expression": "^1.0.0", + "mdast-util-mdx-jsx": "^2.0.0", + "mdast-util-mdxjs-esm": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" + }, + "dependencies": { + "mdast-util-mdx-jsx": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-2.1.4.tgz", + "integrity": "sha512-DtMn9CmVhVzZx3f+optVDF8yFgQVt7FghCRNdlIaS3X5Bnym3hZwPbg/XW86vdpKjlc1PVj26SpnLGeJBXD3JA==", + "dev": true, + "requires": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "ccount": "^2.0.0", + "mdast-util-from-markdown": "^1.1.0", + "mdast-util-to-markdown": "^1.3.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-remove-position": "^4.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + } + }, + "unist-util-remove-position": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-4.0.2.tgz", + "integrity": "sha512-TkBb0HABNmxzAcfLf4qsIbFbaPDvMO6wa3b3j4VcEzFVaw1LBKwnW4/sRJ/atSLSzoIg41JWEdnE7N6DIhGDGQ==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + } + } + } + }, + "mdast-util-mdx-expression": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-1.3.2.tgz", + "integrity": "sha512-xIPmR5ReJDu/DHH1OoIT1HkuybIfRGYRywC+gJtI7qHjCJp/M9jrmBEJW22O8lskDWm562BX2W8TiAwRTb0rKA==", + "dev": true, + "requires": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" + } + }, + "mdast-util-mdx-jsx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.0.0.tgz", + "integrity": "sha512-XZuPPzQNBPAlaqsTTgRrcJnyFbSOBovSadFgbFu8SnuNgm+6Bdx1K+IWoitsmj6Lq6MNtI+ytOqwN70n//NaBA==", + "dev": true, + "requires": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-remove-position": "^5.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "dependencies": { + "@types/hast": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.3.tgz", + "integrity": "sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ==", + "dev": true, + "requires": { + "@types/unist": "*" + } + }, + "@types/mdast": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", + "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "dev": true, + "requires": { + "@types/unist": "*" + } + }, + "@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dev": true + }, + "mdast-util-from-markdown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", + "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", + "dev": true, + "requires": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + } + }, + "mdast-util-phrasing": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.0.0.tgz", + "integrity": "sha512-xadSsJayQIucJ9n053dfQwVu1kuXg7jCTdYsMK8rqzKZh52nLfSH/k0sAxE0u+pj/zKZX+o5wB+ML5mRayOxFA==", + "dev": true, + "requires": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + } + }, + "mdast-util-to-markdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", + "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", + "dev": true, + "requires": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + } + }, + "mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dev": true, + "requires": { + "@types/mdast": "^4.0.0" + } + }, + "micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "dev": true, + "requires": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-core-commonmark": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", + "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", + "dev": true, + "requires": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-factory-destination": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "dev": true, + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-factory-label": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "dev": true, + "requires": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "dev": true, + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-factory-title": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "dev": true, + "requires": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-factory-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "dev": true, + "requires": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.0.1.tgz", + "integrity": "sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==", + "dev": true, + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-chunked": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "dev": true, + "requires": { + "micromark-util-symbol": "^2.0.0" + } + }, + "micromark-util-classify-character": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "dev": true, + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-combine-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "dev": true, + "requires": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-decode-numeric-character-reference": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "dev": true, + "requires": { + "micromark-util-symbol": "^2.0.0" + } + }, + "micromark-util-decode-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "dev": true, + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true + }, + "micromark-util-html-tag-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "dev": true + }, + "micromark-util-normalize-identifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "dev": true, + "requires": { + "micromark-util-symbol": "^2.0.0" + } + }, + "micromark-util-resolve-all": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "dev": true, + "requires": { + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "micromark-util-subtokenize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", + "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", + "dev": true, + "requires": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "dev": true + }, + "micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "dev": true + }, + "unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0" + } + }, + "unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0" + } + }, + "unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + } + }, + "unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + } + } + } + }, + "mdast-util-mdxjs-esm": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-1.3.1.tgz", + "integrity": "sha512-SXqglS0HrEvSdUEfoXFtcg7DRl7S2cwOXc7jkuusG472Mmjag34DUDeOJUZtl+BVnyeO1frIgVpHlNRWc2gk/w==", + "dev": true, + "requires": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "mdast-util-to-markdown": "^1.0.0" + } + }, + "mdast-util-phrasing": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz", + "integrity": "sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "unist-util-is": "^5.0.0" + } + }, + "mdast-util-to-hast": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.0.2.tgz", + "integrity": "sha512-U5I+500EOOw9e3ZrclN3Is3fRpw8c19SMyNZlZ2IS+7vLsNzb2Om11VpIVOR+/0137GhZsFEF6YiKD5+0Hr2Og==", + "dev": true, + "requires": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0" + }, + "dependencies": { + "@types/hast": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.3.tgz", + "integrity": "sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ==", + "dev": true, + "requires": { + "@types/unist": "*" + } + }, + "@types/mdast": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", + "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "dev": true, + "requires": { + "@types/unist": "*" + } + }, + "@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dev": true + }, + "micromark-util-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.0.1.tgz", + "integrity": "sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==", + "dev": true, + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true + }, + "micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "dev": true + }, + "micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "dev": true + }, + "unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, "requires": { - "color-convert": "^2.0.1" + "@types/unist": "^3.0.0" } }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@types/unist": "^3.0.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==", + "unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, "requires": { - "color-name": "~1.1.4" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" } }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "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==", + "unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, "requires": { - "has-flag": "^4.0.0" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" } } } }, - "longest": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-2.0.1.tgz", - "integrity": "sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==" - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "requires": { - "tslib": "^2.0.3" - } - }, - "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==", - "requires": { - "yallist": "^4.0.0" - } - }, - "lz-string": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", - "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==" - }, - "magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "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==", + "mdast-util-to-markdown": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.5.0.tgz", + "integrity": "sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==", + "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==" - } + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^3.0.0", + "mdast-util-to-string": "^3.0.0", + "micromark-util-decode-string": "^1.0.0", + "unist-util-visit": "^4.0.0", + "zwitch": "^2.0.0" } }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "optional": true - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "dev": true, "requires": { - "tmpl": "1.0.5" + "@types/mdast": "^3.0.0" } }, "mdn-data": { @@ -27546,23 +36347,40 @@ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" }, + "mdx-bundler": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/mdx-bundler/-/mdx-bundler-9.2.1.tgz", + "integrity": "sha512-hWEEip1KU9MCNqeH2rqwzAZ1pdqPPbfkx9OTJjADqGPQz4t9BO85fhI7AP9gVYrpmfArf9/xJZUN0yBErg/G/Q==", + "dev": true, + "requires": { + "@babel/runtime": "^7.16.3", + "@esbuild-plugins/node-resolve": "^0.1.4", + "@fal-works/esbuild-plugin-global-externals": "^2.1.2", + "@mdx-js/esbuild": "^2.0.0", + "gray-matter": "^4.0.3", + "remark-frontmatter": "^4.0.1", + "remark-mdx-frontmatter": "^1.1.1", + "uuid": "^8.3.2", + "vfile": "^5.3.2" + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, "memfs": { - "version": "3.4.12", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.12.tgz", - "integrity": "sha512-BcjuQn6vfqP+k100e0E9m61Hyqa//Brp+I3f0OBmN0ATHlFA8vx3Lt8z57R3u2bPqe3WGDBC+nF72fTH7isyEw==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", "requires": { - "fs-monkey": "^1.0.3" + "fs-monkey": "^1.0.4" } }, - "merge": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz", - "integrity": "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==" + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" }, "merge-descriptors": { "version": "1.0.1", @@ -27584,6 +36402,406 @@ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" }, + "micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "dev": true, + "requires": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "dev": true, + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "micromark-extension-frontmatter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-1.1.1.tgz", + "integrity": "sha512-m2UH9a7n3W8VAH9JO9y01APpPKmNNNs71P0RbknEmYSaZU5Ghogv38BYO94AI5Xw6OYfxZRdHZZ2nYjs/Z+SZQ==", + "dev": true, + "requires": { + "fault": "^2.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-extension-mdx-expression": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-1.0.8.tgz", + "integrity": "sha512-zZpeQtc5wfWKdzDsHRBY003H2Smg+PUi2REhqgIhdzAa5xonhP03FcXxqFSerFiNUr5AWmHpaNPQTBVOS4lrXw==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0", + "micromark-factory-mdx-expression": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-events-to-acorn": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-extension-mdx-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-1.0.5.tgz", + "integrity": "sha512-gPH+9ZdmDflbu19Xkb8+gheqEDqkSpdCEubQyxuz/Hn8DOXiXvrXeikOoBA71+e8Pfi0/UYmU3wW3H58kr7akA==", + "dev": true, + "requires": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "estree-util-is-identifier-name": "^2.0.0", + "micromark-factory-mdx-expression": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0", + "vfile-message": "^3.0.0" + }, + "dependencies": { + "vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + } + } + } + }, + "micromark-extension-mdx-md": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-1.0.1.tgz", + "integrity": "sha512-7MSuj2S7xjOQXAjjkbjBsHkMtb+mDGVW6uI2dBL9snOBCbZmoNgDAeZ0nSn9j3T42UE/g2xVNMn18PJxZvkBEA==", + "dev": true, + "requires": { + "micromark-util-types": "^1.0.0" + } + }, + "micromark-extension-mdxjs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-1.0.1.tgz", + "integrity": "sha512-7YA7hF6i5eKOfFUzZ+0z6avRG52GpWR8DL+kN47y3f2KhxbBZMhmxe7auOeaTBrW2DenbbZTf1ea9tA2hDpC2Q==", + "dev": true, + "requires": { + "acorn": "^8.0.0", + "acorn-jsx": "^5.0.0", + "micromark-extension-mdx-expression": "^1.0.0", + "micromark-extension-mdx-jsx": "^1.0.0", + "micromark-extension-mdx-md": "^1.0.0", + "micromark-extension-mdxjs-esm": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-extension-mdxjs-esm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-1.0.5.tgz", + "integrity": "sha512-xNRBw4aoURcyz/S69B19WnZAkWJMxHMT5hE36GtDAyhoyn/8TuAeqjFJQlwk+MKQsUD7b3l7kFX+vlfVWgcX1w==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0", + "micromark-core-commonmark": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-events-to-acorn": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-position-from-estree": "^1.1.0", + "uvu": "^0.5.0", + "vfile-message": "^3.0.0" + }, + "dependencies": { + "vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + } + } + } + }, + "micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "dev": true, + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "dev": true, + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-factory-mdx-expression": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-1.0.9.tgz", + "integrity": "sha512-jGIWzSmNfdnkJq05c7b0+Wv0Kfz3NJ3N4cBjnbO4zjXIlxJr+f8lk+5ZmwFvqdAbUy2q6B5rCY//g0QAAaXDWA==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-events-to-acorn": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-position-from-estree": "^1.0.0", + "uvu": "^0.5.0", + "vfile-message": "^3.0.0" + }, + "dependencies": { + "vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + } + } + } + }, + "micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "dev": true, + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "dev": true, + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "dev": true, + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "dev": true, + "requires": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "dev": true, + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "dev": true, + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "dev": true, + "requires": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "dev": true, + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "dev": true, + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "dev": true + }, + "micromark-util-events-to-acorn": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-1.2.3.tgz", + "integrity": "sha512-ij4X7Wuc4fED6UoLWkmo0xJQhsktfNh1J0m8g4PbIMPlx+ek/4YdW5mvbye8z/aZvAPUoxgXHrwVlXAPKMRp1w==", + "dev": true, + "requires": { + "@types/acorn": "^4.0.0", + "@types/estree": "^1.0.0", + "@types/unist": "^2.0.0", + "estree-util-visit": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0", + "vfile-message": "^3.0.0" + }, + "dependencies": { + "vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + } + } + } + }, + "micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "dev": true + }, + "micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "dev": true, + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "dev": true, + "requires": { + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "dev": true, + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "dev": true, + "requires": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "dev": true + }, + "micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "dev": true + }, "micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -27616,6 +36834,12 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true + }, "min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -27689,6 +36913,51 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" }, + "minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, "mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -27697,6 +36966,95 @@ "minimist": "^1.2.6" } }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "mongodb": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.2.0.tgz", + "integrity": "sha512-d7OSuGjGWDZ5usZPqfvb36laQ9CPhnWkAGHT61x5P95p/8nMVeH8asloMwW6GcYFeB0Vj4CB/1wOTDG2RA9BFA==", + "requires": { + "@mongodb-js/saslprep": "^1.1.0", + "bson": "^6.2.0", + "mongodb-connection-string-url": "^2.6.0" + } + }, + "mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "requires": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + }, + "dependencies": { + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "requires": { + "punycode": "^2.1.1" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" + }, + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + } + } + }, + "mongoose": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.0.0.tgz", + "integrity": "sha512-PzwkLgm1Jhj0NQdgGfnFsu0QP9V1sBFgbavEgh/IPAUzKAagzvEhuaBuAQOQGjczVWnpIU9tBqyd02cOTgsPlA==", + "requires": { + "bson": "^6.2.0", + "kareem": "2.5.1", + "mongodb": "6.2.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "16.0.1" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==" + }, + "mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "requires": { + "debug": "4.x" + } + }, + "mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -27711,15 +37069,16 @@ "thunky": "^1.0.2" } }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" + }, + "napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true }, "natural-compare": { "version": "1.4.0", @@ -27741,6 +37100,16 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, + "next-contentlayer": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/next-contentlayer/-/next-contentlayer-0.3.4.tgz", + "integrity": "sha512-UtUCwgAl159KwfhNaOwyiI7Lg6sdioyKMeh+E7jxx0CJ29JuXGxBEYmCI6+72NxFGIFZKx8lvttbbQhbnYWYSw==", + "dev": true, + "requires": { + "@contentlayer/core": "0.3.4", + "@contentlayer/utils": "0.3.4" + } + }, "no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -27750,6 +37119,55 @@ "tslib": "^2.0.3" } }, + "node-abi": { + "version": "3.52.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.52.0.tgz", + "integrity": "sha512-JJ98b02z16ILv7859irtXn4oUaFWADtvkzy2c0IAatNVX2Mc9Yoh8z6hZInn3QwvMEYhHuQloYi+TTQy67SIdQ==", + "dev": true, + "requires": { + "semver": "^7.3.5" + } + }, + "node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true + }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, "node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -27765,6 +37183,14 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "requires": { + "abbrev": "1" + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -27788,6 +37214,17 @@ "path-key": "^3.0.0" } }, + "npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, "nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -27881,6 +37318,15 @@ "es-abstract": "^1.20.4" } }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, "object.values": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", @@ -27925,6 +37371,12 @@ "mimic-fn": "^2.1.0" } }, + "oo-ascii-tree": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/oo-ascii-tree/-/oo-ascii-tree-1.93.0.tgz", + "integrity": "sha512-zbmrGCL/UsvxV2WlnsSrqdkdxEggxH7eA1HOk+hmimLQu+eLO4Y3VGqwt0VK04Nfe6iG6GnzRL5/XjH0j1v8bQ==", + "dev": true + }, "open": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", @@ -27936,84 +37388,18 @@ } }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "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==", - "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==", - "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==", - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } + "type-check": "^0.4.0" } }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==" - }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -28061,6 +37447,22 @@ "callsites": "^3.0.0" } }, + "parse-entities": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", + "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "character-entities": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + } + }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -28072,11 +37474,6 @@ "lines-and-columns": "^1.1.6" } }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==" - }, "parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -28131,6 +37528,28 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, + "periscopic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", + "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + }, + "dependencies": { + "estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0" + } + } + } + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -28258,11 +37677,11 @@ } }, "postcss": { - "version": "8.4.19", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz", - "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "requires": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } @@ -28278,8 +37697,7 @@ "postcss-browser-comments": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", - "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", - "requires": {} + "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==" }, "postcss-calc": { "version": "8.2.4", @@ -28377,26 +37795,22 @@ "postcss-discard-comments": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", - "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", - "requires": {} + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==" }, "postcss-discard-duplicates": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", - "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", - "requires": {} + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==" }, "postcss-discard-empty": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", - "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", - "requires": {} + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==" }, "postcss-discard-overridden": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", - "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", - "requires": {} + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==" }, "postcss-double-position-gradients": { "version": "3.1.2", @@ -28418,8 +37832,7 @@ "postcss-flexbugs-fixes": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", - "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", - "requires": {} + "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==" }, "postcss-focus-visible": { "version": "6.0.4", @@ -28440,14 +37853,12 @@ "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==", - "requires": {} + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==" }, "postcss-gap-properties": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", - "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", - "requires": {} + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==" }, "postcss-image-set-function": { "version": "4.0.7", @@ -28470,8 +37881,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==", - "requires": {} + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==" }, "postcss-js": { "version": "4.0.0", @@ -28512,14 +37922,12 @@ "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==", - "requires": {} + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==" }, "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==", - "requires": {} + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==" }, "postcss-merge-longhand": { "version": "5.1.7", @@ -28580,8 +37988,7 @@ "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==", - "requires": {} + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==" }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -28639,8 +38046,7 @@ "postcss-normalize-charset": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", - "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", - "requires": {} + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==" }, "postcss-normalize-display-values": { "version": "5.1.0", @@ -28733,8 +38139,7 @@ "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==", - "requires": {} + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==" }, "postcss-place": { "version": "7.0.5", @@ -28828,8 +38233,7 @@ "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==", - "requires": {} + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==" }, "postcss-selector-not": { "version": "6.0.1", @@ -28910,6 +38314,59 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "dev": true, + "requires": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "dependencies": { + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + } + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -28985,6 +38442,32 @@ } } }, + "property-information": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.0.tgz", + "integrity": "sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ==", + "dev": true + }, + "protobufjs": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", + "dev": true, + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + } + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -29011,6 +38494,16 @@ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -29039,6 +38532,12 @@ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, + "queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true + }, "quick-lru": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", @@ -29052,6 +38551,25 @@ "performance-now": "^2.1.0" } }, + "randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "dev": true, + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -29091,6 +38609,26 @@ } } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true + } + } + }, "react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -29213,15 +38751,15 @@ "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, "react-icons": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.6.0.tgz", - "integrity": "sha512-rR/L9m9340yO8yv1QT1QurxWQvWpbNHqVX0fzMln2HEb9TEIrQRGsqiNFQfiv9/JEUbyHmHPlNTB2LWm2Ttz0g==", - "requires": {} + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz", + "integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==" }, "react-indiana-drag-scroll": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/react-indiana-drag-scroll/-/react-indiana-drag-scroll-2.2.0.tgz", "integrity": "sha512-+W/3B2OQV0FrbdnsoIo4dww/xpH0MUQJz6ziQb7H+oBko3OCbXuzDFYnho6v6yhGrYDNWYPuFUewb89IONEl/A==", + "dev": true, "requires": { "classnames": "^2.2.6", "debounce": "^1.2.0", @@ -29233,6 +38771,407 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "react-markdown": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", + "integrity": "sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==", + "dev": true, + "requires": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "dependencies": { + "@types/hast": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.3.tgz", + "integrity": "sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ==", + "dev": true, + "requires": { + "@types/unist": "*" + } + }, + "@types/mdast": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", + "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "dev": true, + "requires": { + "@types/unist": "*" + } + }, + "@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dev": true + }, + "is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true + }, + "mdast-util-from-markdown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", + "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", + "dev": true, + "requires": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + } + }, + "mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dev": true, + "requires": { + "@types/mdast": "^4.0.0" + } + }, + "micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "dev": true, + "requires": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-core-commonmark": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", + "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", + "dev": true, + "requires": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-factory-destination": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "dev": true, + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-factory-label": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "dev": true, + "requires": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "dev": true, + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-factory-title": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "dev": true, + "requires": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-factory-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "dev": true, + "requires": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.0.1.tgz", + "integrity": "sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==", + "dev": true, + "requires": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-chunked": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "dev": true, + "requires": { + "micromark-util-symbol": "^2.0.0" + } + }, + "micromark-util-classify-character": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "dev": true, + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-combine-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "dev": true, + "requires": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-decode-numeric-character-reference": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "dev": true, + "requires": { + "micromark-util-symbol": "^2.0.0" + } + }, + "micromark-util-decode-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "dev": true, + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true + }, + "micromark-util-html-tag-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "dev": true + }, + "micromark-util-normalize-identifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "dev": true, + "requires": { + "micromark-util-symbol": "^2.0.0" + } + }, + "micromark-util-resolve-all": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "dev": true, + "requires": { + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "requires": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "micromark-util-subtokenize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", + "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", + "dev": true, + "requires": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "dev": true + }, + "micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "dev": true + }, + "remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "dev": true, + "requires": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + } + }, + "remark-rehype": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.0.0.tgz", + "integrity": "sha512-vx8x2MDMcxuE4lBmQ46zYUDfcFMmvg80WYX+UNLeG6ixjdCCLcw1lrgAukwBTuOFsS78eoAedHGn9sNM0w7TPw==", + "dev": true, + "requires": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + } + }, + "unified": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz", + "integrity": "sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + } + }, + "unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0" + } + }, + "unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0" + } + }, + "unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + } + }, + "unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + } + }, + "vfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", + "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + } + } + } + }, "react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -29432,11 +39371,130 @@ } } }, + "rehype-stringify": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-9.0.4.tgz", + "integrity": "sha512-Uk5xu1YKdqobe5XpSskwPvo1XeHUUucWEQSl8hTrXt5selvca1e8K1EZ37E6YoZ4BT8BCqCdVfQW7OfHfthtVQ==", + "dev": true, + "requires": { + "@types/hast": "^2.0.0", + "hast-util-to-html": "^8.0.0", + "unified": "^10.0.0" + } + }, "relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" }, + "remark-frontmatter": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-4.0.1.tgz", + "integrity": "sha512-38fJrB0KnmD3E33a5jZC/5+gGAC2WKNiPw1/fdXJvijBlhA7RCsvJklrYJakS0HedninvaCYW8lQGf9C918GfA==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-frontmatter": "^1.0.0", + "micromark-extension-frontmatter": "^1.0.0", + "unified": "^10.0.0" + } + }, + "remark-mdx": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-2.3.0.tgz", + "integrity": "sha512-g53hMkpM0I98MU266IzDFMrTD980gNF3BJnkyFcmN+dD873mQeD5rdMO3Y2X+x8umQfbSE0PcoEDl7ledSA+2g==", + "dev": true, + "requires": { + "mdast-util-mdx": "^2.0.0", + "micromark-extension-mdxjs": "^1.0.0" + } + }, + "remark-mdx-frontmatter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/remark-mdx-frontmatter/-/remark-mdx-frontmatter-1.1.1.tgz", + "integrity": "sha512-7teX9DW4tI2WZkXS4DBxneYSY7NHiXl4AKdWDO9LXVweULlCT8OPWsOjLEnMIXViN1j+QcY8mfbq3k0EK6x3uA==", + "dev": true, + "requires": { + "estree-util-is-identifier-name": "^1.0.0", + "estree-util-value-to-estree": "^1.0.0", + "js-yaml": "^4.0.0", + "toml": "^3.0.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "estree-util-is-identifier-name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-1.1.0.tgz", + "integrity": "sha512-OVJZ3fGGt9By77Ix9NhaRbzfbDV/2rx9EP7YIDJTmsZSEc5kYn2vWcNccYyahJL2uAQZK2a5Or2i0wtIKTPoRQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + } + } + }, + "remark-parse": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", + "integrity": "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "unified": "^10.0.0" + } + }, + "remark-rehype": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-10.1.0.tgz", + "integrity": "sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==", + "dev": true, + "requires": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-to-hast": "^12.1.0", + "unified": "^10.0.0" + }, + "dependencies": { + "mdast-util-to-hast": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", + "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", + "dev": true, + "requires": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-definitions": "^5.0.0", + "micromark-util-sanitize-uri": "^1.1.0", + "trim-lines": "^3.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + } + } + } + }, + "remarkable": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.7.4.tgz", + "integrity": "sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==", + "dev": true, + "requires": { + "argparse": "^1.0.10", + "autolinker": "~0.28.0" + } + }, "renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", @@ -29449,6 +39507,18 @@ "strip-ansi": "^6.0.1" } }, + "repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -29482,61 +39552,11 @@ "resolve-from": "^5.0.0" } }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", - "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "dependencies": { - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } - }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.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==" }, - "resolve-global": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", - "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", - "optional": true, - "requires": { - "global-dirs": "^0.1.1" - } - }, "resolve-url-loader": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", @@ -29575,15 +39595,6 @@ "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==" }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, "retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -29654,11 +39665,6 @@ } } }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" - }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -29667,12 +39673,13 @@ "queue-microtask": "^1.2.2" } }, - "rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, "requires": { - "tslib": "^2.1.0" + "mri": "^1.1.0" } }, "safe-buffer": { @@ -29740,6 +39747,16 @@ "ajv-keywords": "^3.5.2" } }, + "section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + } + }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -29754,9 +39771,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -29882,11 +39899,49 @@ "send": "0.18.0" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "set-getter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.1.tgz", + "integrity": "sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==", + "dev": true, + "requires": { + "to-object-path": "^0.3.0" + } + }, "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "sharp": { + "version": "0.32.6", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", + "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", + "dev": true, + "requires": { + "color": "^4.2.3", + "detect-libc": "^2.0.2", + "node-addon-api": "^6.1.0", + "prebuild-install": "^7.1.1", + "semver": "^7.5.4", + "simple-get": "^4.0.1", + "tar-fs": "^3.0.4", + "tunnel-agent": "^0.6.0" + }, + "dependencies": { + "node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true + } + } + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -29915,11 +39970,50 @@ "object-inspect": "^1.9.0" } }, + "sift": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", + "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" + }, "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==" }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true + }, + "simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "requires": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -29986,6 +40080,20 @@ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" }, + "space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true + }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "requires": { + "memory-pager": "^1.0.2" + } + }, "spdy": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", @@ -30046,6 +40154,16 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, + "streamx": { + "version": "2.15.6", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.6.tgz", + "integrity": "sha512-q+vQL4AAz+FdfT137VF69Cc/APqUbxy+MDOImRrMvchJpigHj9GksgDU2LYbO9rx7RX6osWgxJB2WxhYv4SZAw==", + "dev": true, + "requires": { + "fast-fifo": "^1.1.0", + "queue-tick": "^1.0.1" + } + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -30120,6 +40238,16 @@ "es-abstract": "^1.20.4" } }, + "stringify-entities": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz", + "integrity": "sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==", + "dev": true, + "requires": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + } + }, "stringify-object": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", @@ -30143,6 +40271,18 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" }, + "strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "dev": true + }, + "strip-color": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/strip-color/-/strip-color-0.1.0.tgz", + "integrity": "sha512-p9LsUieSjWNNAxVCXLeilaDlmuUOrDS5/dF9znM1nZc7EGX5+zEFC0bEevsNIaldjlks+2jns5Siz6F9iK6jwA==", + "dev": true + }, "strip-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", @@ -30178,8 +40318,16 @@ "style-loader": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", - "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", - "requires": {} + "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==" + }, + "style-to-object": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", + "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", + "dev": true, + "requires": { + "inline-style-parser": "0.1.1" + } }, "stylehacks": { "version": "5.1.1", @@ -30308,6 +40456,21 @@ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, + "tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "dev": true + }, + "tailwind-merge": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.1.0.tgz", + "integrity": "sha512-l11VvI4nSwW7MtLSLYT4ldidDEUwQAMWuSHk7l4zcXZDgnCRa0V3OdCwFfM7DCzakVXMNRwAeje9maFFXT71dQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.23.5" + } + }, "tailwindcss": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz", @@ -30350,6 +40513,48 @@ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" }, + "tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + } + } + }, + "tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "requires": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, + "tar-stream": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz", + "integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==", + "dev": true, + "requires": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, "temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", @@ -30432,24 +40637,53 @@ "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==" }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "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" + } + }, + "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 + }, + "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" + } + } + } }, "thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "requires": { - "os-tmpdir": "~1.0.2" - } - }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -30460,6 +40694,32 @@ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -30473,6 +40733,12 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, + "toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", + "dev": true + }, "tough-cookie": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", @@ -30499,6 +40765,12 @@ "punycode": "^2.1.1" } }, + "trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true + }, "trim-repeated": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", @@ -30508,45 +40780,22 @@ "escape-string-regexp": "^1.0.2" } }, + "trough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", + "dev": true + }, "tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "optional": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "dependencies": { - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "optional": true - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "optional": true - } - } + "ts-pattern": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ts-pattern/-/ts-pattern-4.3.0.tgz", + "integrity": "sha512-pefrkcd4lmIVR0LA49Imjf9DYLK8vtWhqBPA3Ya1ir8xCW0O2yjL9dsCVvI7pCodLC5q7smNpEtDR2yVulQxOg==", + "dev": true }, "tsconfig-paths": { "version": "3.14.1", @@ -30594,6 +40843,21 @@ } } }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "typanion": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/typanion/-/typanion-3.14.0.tgz", + "integrity": "sha512-ZW/lVMRabETuYCd9O9ZvMhAh8GslSqaUjxmK/JLPCh6l73CvLBiuXswj/+7LdnWOgYsQ130FqLzFz5aGT4I3Ug==", + "dev": true + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -30621,6 +40885,12 @@ "mime-types": "~2.1.24" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -30630,9 +40900,10 @@ } }, "typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==" + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true }, "unbox-primitive": { "version": "1.0.2", @@ -30669,6 +40940,29 @@ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" }, + "unified": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "bail": "^2.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^5.0.0" + }, + "dependencies": { + "is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true + } + } + }, "unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -30677,6 +40971,117 @@ "crypto-random-string": "^2.0.0" } }, + "unist-util-generated": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", + "integrity": "sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==", + "dev": true + }, + "unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-position": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", + "integrity": "sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-position-from-estree": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-1.1.2.tgz", + "integrity": "sha512-poZa0eXpS+/XpoQwGwl79UUdea4ol2ZuCYguVaJS4qzIOMDzbqz8a3erUCOmubSZkaOuGamb3tX790iwOIROww==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "dependencies": { + "@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dev": true + }, + "unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0" + } + }, + "unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + } + }, + "unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + } + } + } + }, + "unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + } + }, + "unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + } + }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -30754,11 +41159,25 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "optional": true + "uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "dev": true, + "requires": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "dependencies": { + "kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true + } + } }, "v8-to-istanbul": { "version": "8.1.1", @@ -30775,6 +41194,67 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, + "vfile": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", + "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + }, + "dependencies": { + "vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + } + } + } + }, + "vfile-location": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-4.1.0.tgz", + "integrity": "sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "vfile": "^5.0.0" + } + }, + "vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "dependencies": { + "@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", + "dev": true + }, + "unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "requires": { + "@types/unist": "^3.0.0" + } + } + } + }, "w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -30816,13 +41296,17 @@ "minimalistic-assert": "^1.0.0" } }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "requires": { - "defaults": "^1.0.3" - } + "web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "dev": true + }, + "web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "dev": true }, "web-vitals": { "version": "2.1.4", @@ -31009,8 +41493,7 @@ "ws": { "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "requires": {} + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==" } } }, @@ -31141,6 +41624,14 @@ "is-typed-array": "^1.1.10" } }, + "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==", + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -31468,8 +41959,7 @@ "ws": { "version": "7.5.9", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "requires": {} + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==" }, "xml-name-validator": { "version": "3.0.0", @@ -31520,16 +42010,22 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "optional": true - }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "dev": true + }, + "zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true } } } diff --git a/SwiftParcel.Web/frontend/package.json b/SwiftParcel.Web/frontend/package.json index 93d78d0..a7d66c0 100644 --- a/SwiftParcel.Web/frontend/package.json +++ b/SwiftParcel.Web/frontend/package.json @@ -7,6 +7,7 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "@types/bcrypt": "^5.0.1", "@types/google-map-react": "^2.1.9", "@types/jest": "^27.5.2", "@types/node": "^16.18.3", @@ -14,16 +15,18 @@ "@types/react-dom": "^18.0.9", "@types/react-router-dom": "^5.3.3", "axios": "^1.2.0", + "bcrypt": "^5.1.1", + "bcryptjs": "^2.4.3", + "eslint": "^8.56.0", "flowbite": "^1.5.4", - "flowbite-react": "^0.3.5", "google-map-react": "^2.2.0", + "mongoose": "^8.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^4.6.0", "react-router-dom": "^6.4.3", "react-scripts": "5.0.1", "tailwindcss": "^3.2.4", - "typescript": "^5.2.2", "web-vitals": "^2.1.4" }, "scripts": { @@ -53,6 +56,8 @@ ] }, "devDependencies": { - "gh-pages": "^4.0.0" + "flowbite-react": "^0.7.2", + "gh-pages": "^4.0.0", + "typescript": "^5.3.3" } } diff --git a/SwiftParcel.Web/frontend/src/App.tsx b/SwiftParcel.Web/frontend/src/App.tsx index e975d2f..ea8e64f 100644 --- a/SwiftParcel.Web/frontend/src/App.tsx +++ b/SwiftParcel.Web/frontend/src/App.tsx @@ -1,6 +1,6 @@ import { HashRouter as Router, Routes, Route } from "react-router-dom"; import { RolesAuthRoute } from "./utils/others"; -import React, { Suspense } from "react"; +import React, { Suspense, useEffect } from "react"; import { Loader } from "./components/loader"; import Parcels from "./pages/parcels/parcels"; import ManageParcels from "./pages/parcels/manage"; @@ -11,13 +11,34 @@ import Deliveries from "./pages/deliveries"; import ManageCouriers from "./pages/couriers/manage"; import ManageCars from "./pages/cars/manage"; import ManageParcelsCar from "./pages/cars/parcels"; +import Inquiry from "./pages/inquiry"; +import Inquiries from "./pages/inquiries"; + export function App() { + + return ( }> } /> + + + + } + /> + + + + } + /> + } @@ -45,7 +66,7 @@ export function App() { + } @@ -53,7 +74,7 @@ export function App() { + } @@ -61,7 +82,7 @@ export function App() { + } @@ -69,7 +90,7 @@ export function App() { + } @@ -77,7 +98,7 @@ export function App() { + } diff --git a/SwiftParcel.Web/frontend/src/components/footer.tsx b/SwiftParcel.Web/frontend/src/components/footer.tsx index 8f55494..d27b41d 100644 --- a/SwiftParcel.Web/frontend/src/components/footer.tsx +++ b/SwiftParcel.Web/frontend/src/components/footer.tsx @@ -9,7 +9,7 @@ export function Footer() { - Parcel delivery + Swift Parcel
    @@ -26,7 +26,7 @@ export function Footer() {

- + ); } diff --git a/SwiftParcel.Web/frontend/src/components/header.tsx b/SwiftParcel.Web/frontend/src/components/header.tsx index 3ec8b73..63e3966 100644 --- a/SwiftParcel.Web/frontend/src/components/header.tsx +++ b/SwiftParcel.Web/frontend/src/components/header.tsx @@ -25,6 +25,10 @@ export function Header(props: { setUserToken(getUserInfo()); }, [showLoginModal]); + React.useEffect(() => { + console.log('User token updated:', userToken); + }, [userToken]); + React.useMemo(() => { if (userToken) { getProfile() @@ -40,9 +44,9 @@ export function Header(props: { } }) .catch(() => { - saveUserInfo(null); - setUserToken(null); - setIsCourier(false); + // saveUserInfo(null); + // setUserToken(null); + // setIsCourier(false); }) .finally(() => { props.setLoading(false); @@ -67,7 +71,7 @@ export function Header(props: { - Parcel delivery + Swift Parcel
@@ -93,7 +97,7 @@ export function Header(props: { Sign in )} - {userToken?.user?.role === "Admin" || userToken?.courier !== null ? ( + {userToken?.user?.role === "admin" || userToken?.courier !== null ? ( ) : null}
@@ -106,7 +110,7 @@ export function Header(props: { ) : null} - {userToken?.user?.role === "Admin" ? ( + {userToken?.user?.role === "admin" ? ( <> diff --git a/SwiftParcel.Web/frontend/src/components/modals/loginModal.tsx b/SwiftParcel.Web/frontend/src/components/modals/loginModal.tsx index 330ca09..2da8c97 100644 --- a/SwiftParcel.Web/frontend/src/components/modals/loginModal.tsx +++ b/SwiftParcel.Web/frontend/src/components/modals/loginModal.tsx @@ -19,14 +19,14 @@ interface LoginModalProps { export function LoginModal(props: LoginModalProps) { const close = () => { - setUsername(""); + setEmail(""); setPassword(""); setError(""); setIsLoading(false); props.setShow(false); }; - const [username, setUsername] = React.useState(""); + const [email, setEmail] = React.useState(""); const [password, setPassword] = React.useState(""); const [isLoading, setIsLoading] = React.useState(false); const [error, setError] = React.useState(""); @@ -36,14 +36,18 @@ export function LoginModal(props: LoginModalProps) { setError(""); setIsLoading(true); - login(username, password) + login(email, password) .then(async (res) => { - if (res.status === 201) { - saveUserInfo(res.data); - close(); - } else { - throw new Error("Something went wrong"); - } + console.log(res) + saveUserInfo(res); + close(); + // if (res.status === 200) { + // saveUserInfo(res.data); + // close(); + // } else { + // console.log("The error with login! The res.status =", res) + // throw new Error("Something went wrong"); + // } }) .catch((err) => { setError(err?.response?.data?.message || "Something went wrong"); @@ -70,14 +74,22 @@ export function LoginModal(props: LoginModalProps) { ) : null}
-
- setUsername(e.target.value)} required={true} + /> */} + setEmail(e.target.value)} + required={true} /> +
diff --git a/SwiftParcel.Web/frontend/src/components/parsing/booleanToString.tsx b/SwiftParcel.Web/frontend/src/components/parsing/booleanToString.tsx new file mode 100644 index 0000000..c53c0a1 --- /dev/null +++ b/SwiftParcel.Web/frontend/src/components/parsing/booleanToString.tsx @@ -0,0 +1,12 @@ +const booleanToString = (value : any) => { + if (typeof value === 'boolean') { + if (value == true) { + return 'true' + } else if (value == false) { + return 'false'; + } + } + return 'false'; +}; + +export default booleanToString; diff --git a/SwiftParcel.Web/frontend/src/components/parsing/stringToBoolean.tsx b/SwiftParcel.Web/frontend/src/components/parsing/stringToBoolean.tsx new file mode 100644 index 0000000..ac00ef6 --- /dev/null +++ b/SwiftParcel.Web/frontend/src/components/parsing/stringToBoolean.tsx @@ -0,0 +1,12 @@ +const stringToBoolean = (value : any) => { + if (typeof value === 'string') { + if (value.toLowerCase() === 'true') { + return true; + } else if (value.toLowerCase() === 'false') { + return false; + } + } + return false; +}; + +export default stringToBoolean; diff --git a/SwiftParcel.Web/frontend/src/pages/home.tsx b/SwiftParcel.Web/frontend/src/pages/home.tsx index 4752c9d..960b168 100644 --- a/SwiftParcel.Web/frontend/src/pages/home.tsx +++ b/SwiftParcel.Web/frontend/src/pages/home.tsx @@ -1,4 +1,4 @@ -import { Badge, Button, Spinner, TextInput } from "flowbite-react"; +import { Badge, Button, Datepicker, Select, Spinner, TextInput } from "flowbite-react"; import React from "react"; import { BsBoxSeam } from "react-icons/bs"; import { HiExclamation } from "react-icons/hi"; @@ -6,6 +6,7 @@ import { Footer } from "../components/footer"; import { Header } from "../components/header"; import { Loader } from "../components/loader"; import { getParcel } from "../utils/api"; +import { Link } from "react-router-dom"; export default function Home() { const [loading, setLoading] = React.useState(true); @@ -17,6 +18,10 @@ export default function Home() { const [parcel, setParcel] = React.useState(null); const [loadingParcel, setLoadingParcel] = React.useState(false); + const handleAnonymousInquirySubmit = (e: React.FormEvent) => { + e.preventDefault(); + } + const onSubmit = (e: React.FormEvent) => { e.preventDefault(); setError(false); @@ -50,11 +55,33 @@ export default function Home() { }, 1000); }; + + return ( <> {loading ? : null}
+
+

+ Post Your Parcel +

+

+ Give your requirements and get many options to choose the best one. +

+ + + +
+

@@ -190,4 +217,4 @@ export default function Home() {

); -} +} \ No newline at end of file diff --git a/SwiftParcel.Web/frontend/src/pages/inquiries.tsx b/SwiftParcel.Web/frontend/src/pages/inquiries.tsx new file mode 100644 index 0000000..8f6cb9e --- /dev/null +++ b/SwiftParcel.Web/frontend/src/pages/inquiries.tsx @@ -0,0 +1,93 @@ +import { Table, Pagination } from "flowbite-react"; +import React from "react"; +import { Header } from "../components/header"; +import { Footer } from "../components/footer"; +import { getInquiries } from "../utils/api"; +import { Loader } from "../components/loader"; +import { ParcelDetails } from "../components/details/parcel"; + +export default function Inquiries() { + const [page, setPage] = React.useState(1); + const [inquiries, setInquiries] = React.useState(null); + + const [loadingHeader, setLoadingHeader] = React.useState(true); + const [loadingParcels, setLoadingParcels] = React.useState(true); + + React.useEffect(() => { + getInquiries() + .then((res) => { + if (res.status === 200) { + setInquiries(res?.data); + } else { + throw new Error(); + } + }) + .catch((err) => { + setInquiries(null); + }) + .finally(() => { + setLoadingParcels(false); + }); + }, [page]); + + const onPageChange = (page: number) => { + setPage(page); + }; + + return ( + <> + {loadingHeader || loadingParcels ? : null} +
+
+

+ Parcels +

+ + + # + Sender + Receiver + Weight + Price + + Deliver + + + + {inquiries != null && inquiries?.results?.length > 0 ? ( + inquiries?.results.map((parcel: any) => ( + + )) + ) : ( + + + + )} + +
+ No parcels found +
+ {inquiries != null && inquiries?.total_pages > 1 ? ( + + ) : null} +
+
+ + ); +} diff --git a/SwiftParcel.Web/frontend/src/pages/inquiry.tsx b/SwiftParcel.Web/frontend/src/pages/inquiry.tsx new file mode 100644 index 0000000..5abad8a --- /dev/null +++ b/SwiftParcel.Web/frontend/src/pages/inquiry.tsx @@ -0,0 +1,489 @@ +import { + Alert, + Button, + Checkbox, + Label, + Spinner, + TextInput, + } from "flowbite-react"; + import React from "react"; + import { HiInformationCircle, HiCheckCircle } from "react-icons/hi"; + import { Footer } from "../components/footer"; + import { Header } from "../components/header"; + import { Loader } from "../components/loader"; + import { createInquiry, register } from "../utils/api"; +import stringToBoolean from "../components/parsing/stringToBoolean"; +import booleanToString from "../components/parsing/booleanToString"; + +export default function Inquiry() { + const [loading, setLoading] = React.useState(true); + + const [description, setDescription] = React.useState(""); + const [packageWidth, setPackageWidth] = React.useState(0); + const [packageHeight, setPackageHeight] = React.useState(0); + const [packageDepth, setPackageDepth] = React.useState(0); + const [packageWeight, setPackageWeight] = React.useState(0); + + const [sourceAddressStreet, setSourceAddressStreet] = React.useState(""); + const [sourceAddressBuildingNumber, setSourceAddressBuildingNumber] = React.useState(""); + const [sourceAddressApartmentNumber, setSourceAddressApartmentNumber] = React.useState(""); + const [sourceAddressCity, setSourceAddressCity] = React.useState(""); + const [sourceAddressZipCode, setSourceAddressZipCode] = React.useState(""); + const [sourceAddressCountry, setSourceAddressCountry] = React.useState(""); + + const [destinationAddressStreet, setDestinationAddressStreet] = React.useState(""); + const [destinationAddressBuildingNumber, setDestinationAddressBuildingNumber] = React.useState(""); + const [destinationAddressApartmentNumber, setDestinationAddressApartmentNumber] = React.useState(""); + const [destinationAddressCity, setDestinationAddressCity] = React.useState(""); + const [destinationAddressZipCode, setDestinationAddressZipCode] = React.useState(""); + const [destinationAddressCountry, setDestinationAddressCountry] = React.useState(""); + + const [pickupDate, setPickupDate] = React.useState(""); + const [deliveryDate, setDeliveryDate] = React.useState(""); + const [priority, setPriority] = React.useState("low"); + const [atWeekend, setAtWeekend] = React.useState(false); + const [isCompany, setIsCompany] = React.useState(false); + const [vipPackage, setVipPackage] = React.useState(false); + + const [error, setError] = React.useState(""); + const [success, setSuccess] = React.useState(""); + + const [inquiryLoading, setInquiryLoading] = React.useState(false); + + const onSubmit = (e: any) => { + e.preventDefault(); + if (inquiryLoading) return; + setError(""); + setSuccess(""); + setInquiryLoading(true); + + createInquiry(description, packageWidth, packageHeight, packageDepth, packageWeight, + sourceAddressStreet, sourceAddressBuildingNumber, sourceAddressApartmentNumber, + sourceAddressCity, sourceAddressZipCode, sourceAddressCountry, + destinationAddressStreet, destinationAddressBuildingNumber, destinationAddressApartmentNumber, + destinationAddressCity, destinationAddressZipCode, destinationAddressCountry, priority, atWeekend, + `${pickupDate}T00:00:00.000Z`, `${deliveryDate}T00:00:00.000Z`, isCompany, vipPackage) + .then((res) => { + setSuccess( + res?.data?.message || "Inquiry created successfully!" + ); + setDescription(""); + setPackageWidth(0); + setPackageHeight(0); + setPackageDepth(0); + setPackageWeight(0); + setSourceAddressCity(""); + setSourceAddressBuildingNumber(""); + setSourceAddressApartmentNumber(""); + setSourceAddressCity(""); + setSourceAddressZipCode(""); + setSourceAddressCountry(""); + setDestinationAddressCity(""); + setDestinationAddressBuildingNumber(""); + setDestinationAddressApartmentNumber(""); + setDestinationAddressCity(""); + setDestinationAddressZipCode(""); + setDestinationAddressCountry(""); + setPickupDate(""); + setDeliveryDate(""); + setPriority("low"); + setAtWeekend(false); + setIsCompany(false); + setVipPackage(false); + }) + .catch((err) => { + setError(err?.response?.data?.message || "Something went wrong!"); + }) + .finally(() => { + setInquiryLoading(false); + }); + }; + + return ( + <> + {loading ? : null} +
+
+

+ Create an inquiry +

+

+ Please fill all fields below and click blue button located at the bottom of this page. +

+ + + +
+ +
+
+ +
+
+
+ setPackageWidth(parseFloat(e.target.value))} + /> +
+
+
+
+ setPackageHeight(parseFloat(e.target.value))} + /> +
+
+
+
+ setPackageDepth(parseFloat(e.target.value))} + /> +
+
+
+
+ setPackageWeight(parseFloat(e.target.value))} + /> +
+
+ +
+ +
+
+ +
+ +
+
+
+
+ setSourceAddressStreet(e.target.value)} + /> +
+
+
+
+ setSourceAddressBuildingNumber(e.target.value)} + /> +
+
+
+
+ setSourceAddressApartmentNumber(e.target.value)} + /> +
+
+ +
+
+
+
+ setSourceAddressCity(e.target.value)} + /> +
+
+
+
+ setSourceAddressZipCode(e.target.value)} + /> +
+
+
+
+ setSourceAddressCountry(e.target.value)} + /> +
+
+
+
+ +
+ +
+
+ +
+
+
+
+
+ setDestinationAddressStreet(e.target.value)} + /> +
+
+
+
+ setDestinationAddressBuildingNumber(e.target.value)} + /> +
+
+
+
+ setDestinationAddressApartmentNumber(e.target.value)} + /> +
+
+ +
+
+
+
+ setDestinationAddressCity(e.target.value)} + /> +
+
+
+
+ setDestinationAddressZipCode(e.target.value)} + /> +
+
+
+
+ setDestinationAddressCountry(e.target.value)} + /> +
+
+
+
+ +
+
+
+
+ setPickupDate(e.target.value)} + /> +
+
+
+
+ setDeliveryDate(e.target.value)} + /> +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+ setDescription(e.target.value)} + /> +
+ + {inquiryLoading ? ( + + ) : ( + + )} + + + {error ? ( + + + Error! {error} + + + ) : null} + {success ? ( + + + Success! {success} + + + ) : null} +
+
+ + ); + } + \ No newline at end of file diff --git a/SwiftParcel.Web/frontend/src/utils/api.tsx b/SwiftParcel.Web/frontend/src/utils/api.tsx index c019155..eee97db 100644 --- a/SwiftParcel.Web/frontend/src/utils/api.tsx +++ b/SwiftParcel.Web/frontend/src/utils/api.tsx @@ -1,17 +1,52 @@ import axios from "axios"; import { getUserInfo, saveUserInfo } from "./storage"; + +const API_BASE_URL = 'http://localhost:6001'; + + const api = axios.create({ - baseURL: "https://parcel-delivery-system-ng.herokuapp.com/api", + baseURL: "http://localhost:5292", + withCredentials: true, }); +api.interceptors.response.use( + response => response, + error => { + if (axios.isAxiosError(error) && error.response?.status === 401) { + //// saveUserInfo(null); + window.location.href = '/login'; + } + return Promise.reject(error); + } +); + +// Helper function to get the authorization header +const getAuthHeader = () => { + const token = getUserInfo()?.token; + if (!token) throw new Error('No token found'); + return { Authorization: `Bearer ${token}` }; +}; + const defaultPageLimit = 10; -export const login = async (username: string, password: string) => { - return await api.post("/auth/login", { - username, - password, - }); +export const login = async (email: string, password: string) => { + // const response = await api.post('/sign-in', { email, password }); + // return response.data; + try { + const response = await api.post('/identity/sign-in', { email, password }); + const { accessToken, refreshToken, role, expires } = response.data; + + saveUserInfo({ token: accessToken, refreshToken, role, expires }); + return response.data; + } catch (error) { + if (axios.isAxiosError(error)) { + console.error('Error during login:', error.response?.data || error.message); + } else { + console.error('Error during login:', error); + } + throw error; + } }; export const register = async ( @@ -19,41 +54,53 @@ export const register = async ( password: string, email: string ) => { - return await api.post("/auth/register", { - username, - password, - email, - }); + try { + const response = await api.post(`/identity/sign-up`, { + username, + password, + email, + }); + return response.data; + } catch (error) { + if (axios.isAxiosError(error)) { + console.error('Error during registration (Axios error):', error.response?.data || error.message); + } else { + console.error('Error during registration:', error); + } + throw error; + } }; export const logout = async () => { - const token = getUserInfo()?.token; - saveUserInfo(null); - if (!token) return Promise.reject(); - - await api.get("/auth/logout", { - headers: { - Authorization: `Bearer ${token}`, - }, - }); -}; - -export const getProfile = async () => { - const token = getUserInfo()?.token; - if (!token) return Promise.reject(); - - const res = await api.get("/auth/me", { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - - if (res.status === 401) { + try { + const headers = getAuthHeader(); + await api.get('/identity/logout', { headers }); + } catch (error) { + console.error('Logout failed:', error); + } finally { saveUserInfo(null); - return Promise.reject(); + window.location.href = '/login'; } +}; - return res; +export const getProfile = async () => { + // const token = getUserInfo()?.token; + // if (!token) return Promise.reject(); + + // const res = await api.get("/auth/me", { + // headers: { + // Authorization: `Bearer ${token}`, + // }, + // }); + + // if (res.status === 401) { + // // saveUserInfo(null); + // return Promise.reject(); + // } + + // return res; + const response = await api.get("/me", { headers: getAuthHeader() }); + return response.data; }; export const getUsers = async ( @@ -61,19 +108,95 @@ export const getUsers = async ( perPage: number = defaultPageLimit, extra = "" ) => { - const token = getUserInfo()?.token; - const res = await api.get(`/users?page=${page}&size=${perPage}${extra}`, { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - if (res.status === 401) { - saveUserInfo(null); + // in the case for the test reason: using the backend in the node + // const token = getUserInfo()?.token; + // const res = await api.get(`/users?page=${page}&size=${perPage}${extra}`, { + // headers: { + // Authorization: `Bearer ${token}`, + // }, + // }); + + const response = await api.get(`/users?page=${page}&size=${perPage}`, { headers: getAuthHeader() }); + + if (response.status === 401) { + //// saveUserInfo(null); return Promise.reject(); } - return res; + return response; +}; + +export const createInquiry = async ( + description: string, + width: number, + height: number, + depth: number, + weight: number, + sourceStreet: string, + sourceBuildingNumber: string, + sourceApartmentNumber: string, + sourceCity: string, + sourceZipCode: string, + sourceCountry: string, + destinationStreet: string, + destinationBuildingNumber: string, + destinationApartmentNumber: string, + destinationCity: string, + destinationZipCode: string, + destinationCountry: string, + priority: string, + atWeekend: boolean, + pickupDate: string, + deliveryDate: string, + isCompany: boolean, + vipPackage: boolean +) => { + try { + const response = await api.post(`/parcels`, { + description, + width, + height, + depth, + weight, + sourceStreet, + sourceBuildingNumber, + sourceApartmentNumber, + sourceCity, + sourceZipCode, + sourceCountry, + destinationStreet, + destinationBuildingNumber, + destinationApartmentNumber, + destinationCity, + destinationZipCode, + destinationCountry, + priority, + atWeekend, + pickupDate, + deliveryDate, + isCompany, + vipPackage + }); + return response.data; + } catch (error) { + if (axios.isAxiosError(error)) { + console.error('Error during inquiry creation (Axios error):', error.response?.data || error.message); + } else { + console.error('Error during inquiry creation:', error); + } + throw error; + } +}; + +export const getInquiries = async () => { + try { + const response = await api.get(`/parcels`); + return response.data; + } catch (error) { + console.error('Error during getting inquiries:', error); + throw error; + } }; export const createCar = async (data: any) => { @@ -85,7 +208,7 @@ export const createCar = async (data: any) => { }); if (res.status === 401) { - saveUserInfo(null); + // // saveUserInfo(null); return Promise.reject(); } @@ -101,7 +224,7 @@ export const updateCar = async (carId: number, data: any) => { }); if (res.status === 401) { - saveUserInfo(null); + // saveUserInfo(null); return Promise.reject(); } @@ -117,7 +240,7 @@ export const deleteCar = async (carId: number) => { }); if (res.status === 401) { - saveUserInfo(null); + //// saveUserInfo(null); return Promise.reject(); } @@ -133,7 +256,7 @@ export const getCar = async (carId: number) => { }); if (res.status === 401) { - saveUserInfo(null); + // saveUserInfo(null); return Promise.reject(); } @@ -153,7 +276,7 @@ export const getCarPersonal = async () => { }); if (res.status === 401) { - saveUserInfo(null); + // saveUserInfo(null); return Promise.reject(); } @@ -173,7 +296,7 @@ export const getCars = async ( }); if (res.status === 401) { - saveUserInfo(null); + // saveUserInfo(null); return Promise.reject(); } @@ -189,7 +312,7 @@ export const createParcel = async (data: any) => { }); if (res.status === 401) { - saveUserInfo(null); + // saveUserInfo(null); return Promise.reject(); } @@ -205,7 +328,7 @@ export const updateParcel = async (parcelId: string, data: any) => { }); if (res.status === 401) { - saveUserInfo(null); + // saveUserInfo(null); return Promise.reject(); } @@ -221,7 +344,7 @@ export const deleteParcel = async (parcelId: string) => { }); if (res.status === 401) { - saveUserInfo(null); + // saveUserInfo(null); return Promise.reject(); } @@ -237,7 +360,7 @@ export const getParcel = async (parcelId: string) => { }); if (res.status === 401) { - saveUserInfo(null); + // saveUserInfo(null); return Promise.reject(); } @@ -257,7 +380,7 @@ export const getParcels = async ( }); if (res.status === 401) { - saveUserInfo(null); + // saveUserInfo(null); return Promise.reject(); } @@ -280,7 +403,7 @@ export const getParcelsForCourier = async ( ); if (res.status === 401) { - saveUserInfo(null); + // saveUserInfo(null); return Promise.reject(); } @@ -303,7 +426,7 @@ export const getParcelsForCar = async ( ); if (res.status === 401) { - saveUserInfo(null); + // saveUserInfo(null); return Promise.reject(); } @@ -319,7 +442,7 @@ export const createCourier = async (data: any) => { }); if (res.status === 401) { - saveUserInfo(null); + // saveUserInfo(null); return Promise.reject(); } @@ -335,7 +458,7 @@ export const updateCourier = async (courierId: number, data: any) => { }); if (res.status === 401) { - saveUserInfo(null); + // saveUserInfo(null); return Promise.reject(); } @@ -351,7 +474,7 @@ export const deleteCourier = async (courierId: number) => { }); if (res.status === 401) { - saveUserInfo(null); + // saveUserInfo(null); return Promise.reject(); } @@ -367,7 +490,7 @@ export const getCourier = async (courierId: number) => { }); if (res.status === 401) { - saveUserInfo(null); + // saveUserInfo(null); return Promise.reject(); } @@ -383,7 +506,7 @@ export const getCouriers = async (page: number, perPage: number = 10) => { }); if (res.status === 401) { - saveUserInfo(null); + // saveUserInfo(null); return Promise.reject(); } diff --git a/SwiftParcel.Web/frontend/src/utils/others.tsx b/SwiftParcel.Web/frontend/src/utils/others.tsx index 87d0089..c537352 100644 --- a/SwiftParcel.Web/frontend/src/utils/others.tsx +++ b/SwiftParcel.Web/frontend/src/utils/others.tsx @@ -2,10 +2,9 @@ import { Fragment, ReactNode } from "react"; import { getUserInfo } from "./storage"; import { Navigate } from "react-router-dom"; -export function RolesAuthRoute({ - children, - role, -}: { + + +export function RolesAuthRoute({children, role,}: { children: ReactNode; role: any; }) { diff --git a/SwiftParcel.Web/frontend/src/utils/storage.tsx b/SwiftParcel.Web/frontend/src/utils/storage.tsx index 80507d1..d8ce34c 100644 --- a/SwiftParcel.Web/frontend/src/utils/storage.tsx +++ b/SwiftParcel.Web/frontend/src/utils/storage.tsx @@ -1,12 +1,10 @@ export const saveUserInfo = (userInfo: any) => { localStorage.setItem("userInfo", JSON.stringify(userInfo)); + console.log("User info in saveuserInfo", JSON.stringify(userInfo)); }; export const getUserInfo = () => { const userInfo = localStorage.getItem("userInfo"); - if (userInfo) { - return JSON.parse(userInfo); - } - return null; + return userInfo ? JSON.parse(userInfo) : null; }; \ No newline at end of file diff --git a/SwiftParcel.Web/frontend/src/utils/userPopulation.ts b/SwiftParcel.Web/frontend/src/utils/userPopulation.ts new file mode 100644 index 0000000..de7047c --- /dev/null +++ b/SwiftParcel.Web/frontend/src/utils/userPopulation.ts @@ -0,0 +1,41 @@ +// import mongoose from 'mongoose'; +// import bcrypt from 'bcrypt'; + +// const userSchema = new mongoose.Schema({ +// _id: mongoose.Schema.Types.ObjectId, +// email: String, +// role: String, +// password: String, +// createdAt: Date, +// permissions: [String], +// }); + +// const User = mongoose.model('User', userSchema); + +// export const populateAdminUser = async () => { +// try { +// await mongoose.connect(process.env.CONNECTION_STRING || ''); + +// const existingAdmin = await User.findOne({ role: 'admin' }).exec(); +// if (existingAdmin) { +// console.log('Admin user already exists.'); +// return; +// } + +// const hashedPassword = await bcrypt.hash('superadmin', 10); + +// const adminUser = new User({ +// _id: new mongoose.Types.ObjectId(), +// email: 'admin@email.com', +// role: 'admin', +// password: hashedPassword, +// createdAt: new Date(), +// permissions: [], // Add necessary permissions here +// }); + +// await adminUser.save(); +// console.log('Admin user created successfully.'); +// } catch (error) { +// console.error('Error creating admin user:', error); +// } +// }; diff --git a/SwiftParcel.Web/frontend/tsconfig.json b/SwiftParcel.Web/frontend/tsconfig.json index a273b0c..4a501cc 100644 --- a/SwiftParcel.Web/frontend/tsconfig.json +++ b/SwiftParcel.Web/frontend/tsconfig.json @@ -10,7 +10,7 @@ "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, - "strict": true, + "strict": false, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "module": "esnext", diff --git a/SwiftParcel.Web/wireframes/admin-view/cars-real.png b/SwiftParcel.Web/wireframes/admin-view/cars-real.png deleted file mode 100644 index 5a209fb..0000000 Binary files a/SwiftParcel.Web/wireframes/admin-view/cars-real.png and /dev/null differ diff --git a/SwiftParcel.Web/wireframes/admin-view/cars-wireframe.png b/SwiftParcel.Web/wireframes/admin-view/cars-wireframe.png deleted file mode 100644 index 7aa14e7..0000000 Binary files a/SwiftParcel.Web/wireframes/admin-view/cars-wireframe.png and /dev/null differ diff --git a/SwiftParcel.Web/wireframes/admin-view/couriers-real.png b/SwiftParcel.Web/wireframes/admin-view/couriers-real.png deleted file mode 100644 index 55700e3..0000000 Binary files a/SwiftParcel.Web/wireframes/admin-view/couriers-real.png and /dev/null differ diff --git a/SwiftParcel.Web/wireframes/admin-view/couriers-wireframe.png b/SwiftParcel.Web/wireframes/admin-view/couriers-wireframe.png deleted file mode 100644 index 183feb9..0000000 Binary files a/SwiftParcel.Web/wireframes/admin-view/couriers-wireframe.png and /dev/null differ diff --git a/SwiftParcel.Web/wireframes/admin-view/parcels-real.png b/SwiftParcel.Web/wireframes/admin-view/parcels-real.png deleted file mode 100644 index e8c17c0..0000000 Binary files a/SwiftParcel.Web/wireframes/admin-view/parcels-real.png and /dev/null differ diff --git a/SwiftParcel.Web/wireframes/admin-view/parcels-wireframe.png b/SwiftParcel.Web/wireframes/admin-view/parcels-wireframe.png deleted file mode 100644 index 929089a..0000000 Binary files a/SwiftParcel.Web/wireframes/admin-view/parcels-wireframe.png and /dev/null differ diff --git a/SwiftParcel.Web/wireframes/courier-view/deliveries-real.png b/SwiftParcel.Web/wireframes/courier-view/deliveries-real.png deleted file mode 100644 index 15a98ea..0000000 Binary files a/SwiftParcel.Web/wireframes/courier-view/deliveries-real.png and /dev/null differ diff --git a/SwiftParcel.Web/wireframes/courier-view/deliveries-wireframe.png b/SwiftParcel.Web/wireframes/courier-view/deliveries-wireframe.png deleted file mode 100644 index dcdf936..0000000 Binary files a/SwiftParcel.Web/wireframes/courier-view/deliveries-wireframe.png and /dev/null differ diff --git a/SwiftParcel.Web/wireframes/courier-view/parcels-real.png b/SwiftParcel.Web/wireframes/courier-view/parcels-real.png deleted file mode 100644 index a87a977..0000000 Binary files a/SwiftParcel.Web/wireframes/courier-view/parcels-real.png and /dev/null differ diff --git a/SwiftParcel.Web/wireframes/courier-view/parcels-wireframe.png b/SwiftParcel.Web/wireframes/courier-view/parcels-wireframe.png deleted file mode 100644 index a5b12ba..0000000 Binary files a/SwiftParcel.Web/wireframes/courier-view/parcels-wireframe.png and /dev/null differ diff --git a/SwiftParcel.Web/wireframes/home-real.png b/SwiftParcel.Web/wireframes/home-real.png deleted file mode 100644 index 3e8720b..0000000 Binary files a/SwiftParcel.Web/wireframes/home-real.png and /dev/null differ diff --git a/SwiftParcel.Web/wireframes/home-wireframe.png b/SwiftParcel.Web/wireframes/home-wireframe.png deleted file mode 100644 index 984e2c3..0000000 Binary files a/SwiftParcel.Web/wireframes/home-wireframe.png and /dev/null differ diff --git a/SwiftParcel.Web/wireframes/login-real.png b/SwiftParcel.Web/wireframes/login-real.png deleted file mode 100644 index 8a67966..0000000 Binary files a/SwiftParcel.Web/wireframes/login-real.png and /dev/null differ diff --git a/SwiftParcel.Web/wireframes/login-wireframe.png b/SwiftParcel.Web/wireframes/login-wireframe.png deleted file mode 100644 index f4ae335..0000000 Binary files a/SwiftParcel.Web/wireframes/login-wireframe.png and /dev/null differ diff --git a/SwiftParcel.Web/wireframes/register-real.png b/SwiftParcel.Web/wireframes/register-real.png deleted file mode 100644 index 103b820..0000000 Binary files a/SwiftParcel.Web/wireframes/register-real.png and /dev/null differ diff --git a/SwiftParcel.Web/wireframes/register-wireframe.png b/SwiftParcel.Web/wireframes/register-wireframe.png deleted file mode 100644 index 0b48f98..0000000 Binary files a/SwiftParcel.Web/wireframes/register-wireframe.png and /dev/null differ diff --git a/SwiftParcel/README.md b/SwiftParcel/README.md new file mode 100644 index 0000000..9947beb --- /dev/null +++ b/SwiftParcel/README.md @@ -0,0 +1,33 @@ +here the very nice and super proferrional html and css of the projec t name goes ... +# 🚀 Swift Parcel 📦 + + + +# Solution start + +To start the sollution of the current project: + +1. Open the d-docker-composer directory: + +```bash +cd d-docker-composer directory +``` + +2. To run the infrastructure of the project in the background run: + +```bash +docker-compose -f micro-infrastructure.yml up +``` + +3. After this you can: + + - 3.1.Start each of the project independently: + - By going to the scripts directory of the project directory you want to run up and execute: + + ```bash + ./start.sh # remember about the chmod +x ./start.sh if the previlages of execution are not in the state of presence + ``` + - 3.2. Start all the project by running the contatintes defined with the images with the help of execution: + ```bash + docker-compose -f micro-services-local.yml up + ``` \ No newline at end of file diff --git a/SwiftParcel/d-docker-compose/micro-infrastructure.yml b/SwiftParcel/d-docker-compose/micro-infrastructure.yml new file mode 100644 index 0000000..bf65ea3 --- /dev/null +++ b/SwiftParcel/d-docker-compose/micro-infrastructure.yml @@ -0,0 +1,149 @@ +version: "1.0" + +services: + + consul: + image: consul:1.9.5 + container_name: consul + restart: unless-stopped + networks: + - swiftparcel + ports: + - 8500:8500 + # volumes: + # - consul:/consul/data + + fabio: + image: fabiolb/fabio + container_name: fabio + restart: unless-stopped + environment: + - FABIO_REGISTRY_CONSUL_ADDR=consul:8500 + networks: + - swiftparcel + ports: + - 9998:9998 + - 9999:9999 + + grafana: + image: grafana/grafana + container_name: grafana + restart: unless-stopped + networks: + - swiftparcel + ports: + - 3000:3000 + # volumes: + # - grafana:/var/lib/grafana + + jaeger: + image: jaegertracing/all-in-one + container_name: jaeger + restart: unless-stopped + networks: + - swiftparcel + ports: + - 5775:5775/udp + - 5778:5778 + - 6831:6831/udp + - 6832:6832/udp + - 9411:9411 + - 14268:14268 + - 16686:16686 + + mongo: + image: mongo + container_name: mongo + restart: unless-stopped + # environment: + # - MONGO_INITDB_ROOT_USERNAME=root + # - MONGO_INITDB_ROOT_PASSWORD=secret + networks: + - swiftparcel + ports: + - 27017:27017 + volumes: + - mongo:/data/db + + prometheus: + build: ./prometheus + container_name: prometheus + restart: unless-stopped + networks: + - swiftparcel + ports: + - 9090:9090 + # volumes: + # - prometheus:/prometheus + + rabbitmq: + build: ./rabbitmq + container_name: rabbitmq + restart: unless-stopped + networks: + - swiftparcel + ports: + - 5672:5672 + - 15672:15672 + - 15692:15692 + # volumes: + # - rabbitmq:/var/lib/rabbitmq + + redis: + image: redis + container_name: redis + restart: unless-stopped + networks: + - swiftparcel + ports: + - 6379:6379 + volumes: + - redis:/data + + seq: + image: datalust/seq + container_name: seq + restart: unless-stopped + environment: + - ACCEPT_EULA=Y + networks: + - swiftparcel + ports: + - 5341:80 + # volumes: + # - seq:/data + + vault: + image: vault:1.9.0 + container_name: vault + restart: unless-stopped + environment: + - VAULT_ADDR=http://127.0.0.1:8200 + - VAULT_DEV_ROOT_TOKEN_ID=secret + cap_add: + - IPC_LOCK + networks: + - swiftparcel + ports: + - 8200:8200 + +networks: + swiftparcel: + name: swiftparcel-network + +volumes: + # consul: + # driver: local + # grafana: + # driver: local + mongo: + driver: local + # prometheus: + # driver: local + # rabbitmq: + # driver: local + redis: + driver: local + # seq: + # driver: local + diff --git a/SwiftParcel/d-docker-compose/micro-services-local.yml b/SwiftParcel/d-docker-compose/micro-services-local.yml new file mode 100644 index 0000000..1b183bd --- /dev/null +++ b/SwiftParcel/d-docker-compose/micro-services-local.yml @@ -0,0 +1,35 @@ +version: "2.1" + +services: + + identity-service: + build: ../../SwifttParcel.Services.Identity + container_name: apa/identity-service + restart: unless-stopped + ports: + - 6001:80 + networks: + - swiftparcel + + availability-service: + build: ../../SwiftParcel.Services.Availability + container_name: apa/availability-service + restart: unless-stopped + ports: + - 6002:80 + networks: + - swiftparcel + web: + build: ../../SwiftParcel.Web + container_name: apa/swiftparcel-web + restart: unless-stopped + ports: + - 3001:80 + networks: + - swiftparcel + + +networks: + swiftparcel: + name: swiftparcel-network + external: true \ No newline at end of file diff --git a/SwiftParcel/d-docker-compose/services.yml b/SwiftParcel/d-docker-compose/services.yml new file mode 100644 index 0000000..c6497ec --- /dev/null +++ b/SwiftParcel/d-docker-compose/services.yml @@ -0,0 +1,35 @@ +version: "1.0" + +services: + + identity-service: + image: apa/swiftparcel.services.identity + container_name: identity-service + restart: unless-stopped + ports: + - 6001:80 + networks: + - swiftparcel + + availability-service: + image: apa/swiftparcel.services.availability + container_name: availability-service + restart: unless-stopped + ports: + - 6002:80 + networks: + - swiftparcel + web: + image: apa/swiftparcel.web + container_name: swiftparcel-web + restart: unless-stopped + ports: + - 3001:80 + networks: + - swiftparcel + + +networks: + swiftparcel: + name: swiftparcel-network + external: true \ No newline at end of file diff --git a/SwiftParcel/docker-compose/vault-consul.yml b/SwiftParcel/d-docker-compose/vault-consul-fabio.yml similarity index 100% rename from SwiftParcel/docker-compose/vault-consul.yml rename to SwiftParcel/d-docker-compose/vault-consul-fabio.yml diff --git a/SwiftParcel/services.yml b/SwiftParcel/services.yml index a718453..400b11e 100644 --- a/SwiftParcel/services.yml +++ b/SwiftParcel/services.yml @@ -3,4 +3,8 @@ apps: script: dotnet run cwd: ../SwiftParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api max_restarts: 3 + - name: availability + script: dotnet run + cwd: ../SwiftParcel.Services.Availability/src/SwiftParcel.Services.Availability.Api/SwiftParcel.Services.Availability.Api + max_restarts: 3 \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/Dockerfile b/SwifttParcel.Services.Identity/Dockerfile index d37b476..4d85181 100644 --- a/SwifttParcel.Services.Identity/Dockerfile +++ b/SwifttParcel.Services.Identity/Dockerfile @@ -6,6 +6,15 @@ WORKDIR /app # Copy csproj and restore as distinct layers. COPY *.sln . COPY src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/*.csproj ./src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/ +# Copy the Application, Core, and Infrastructure projects as well. +COPY src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/*.csproj ./src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/ +COPY src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/*.csproj ./src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/ +COPY src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/*.csproj ./src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/ +# Copy tests projects as well. +COPY tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/*.csproj ./tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/ +COPY tests/SwiftParcel.Services.Identity.Infrastructure.UnitTests/Infrastructure.UnitTests/*.csproj ./tests/SwiftParcel.Services.Identity.Infrastructure.UnitTests/Infrastructure.UnitTests/ + +# Restore the NuGet packages RUN dotnet restore # Copy everything else and build the app. diff --git a/SwifttParcel.Services.Identity/SwiftParcel.Services.Identity.sln b/SwifttParcel.Services.Identity/SwiftParcel.Services.Identity.sln index 4b233e0..8c5935f 100644 --- a/SwifttParcel.Services.Identity/SwiftParcel.Services.Identity.sln +++ b/SwifttParcel.Services.Identity/SwiftParcel.Services.Identity.sln @@ -7,28 +7,35 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5F2EF2C7-579 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Identity.Api", "SwiftParcel.Services.Identity.Api", "{B85B3054-38B0-4EA1-8943-30DB1A33C2B1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Identity.Api", "src\SwiftParcel.Services.Identity.Api\SwiftParcel.Services.Identity.Api\SwiftParcel.Services.Identity.Api.csproj", "{2ADD2415-5EA3-4004-A416-9C131FFB8173}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwiftParcel.Services.Identity.Api", "src\SwiftParcel.Services.Identity.Api\SwiftParcel.Services.Identity.Api\SwiftParcel.Services.Identity.Api.csproj", "{2ADD2415-5EA3-4004-A416-9C131FFB8173}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Identity.Application", "SwiftParcel.Services.Identity.Application", "{0D20F0A4-91C5-468A-9337-CDDA3313D9DF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Identity.Application", "src\SwiftParcel.Services.Identity.Application\SwiftParcel.Services.Identity.Application\SwiftParcel.Services.Identity.Application.csproj", "{883F6B8D-839D-4071-870C-DD5F0CFE937E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwiftParcel.Services.Identity.Application", "src\SwiftParcel.Services.Identity.Application\SwiftParcel.Services.Identity.Application\SwiftParcel.Services.Identity.Application.csproj", "{883F6B8D-839D-4071-870C-DD5F0CFE937E}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Identity.Core", "SwiftParcel.Services.Identity.Core", "{B9FBBB29-CC89-44BC-B845-FEB03B468FD9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Identity.Core", "src\SwiftParcel.Services.Identity.Core\SwiftParcel.Services.Identity.Core\SwiftParcel.Services.Identity.Core.csproj", "{F72C4B04-5CE8-41C4-AA61-E3F415D82C80}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwiftParcel.Services.Identity.Core", "src\SwiftParcel.Services.Identity.Core\SwiftParcel.Services.Identity.Core\SwiftParcel.Services.Identity.Core.csproj", "{F72C4B04-5CE8-41C4-AA61-E3F415D82C80}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SwiftParcel.Services.Identity.Infrastructure", "SwiftParcel.Services.Identity.Infrastructure", "{BCF8AC11-5D37-4198-B089-0CB0519193A8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftParcel.Services.Identity.Infrastructure", "src\SwiftParcel.Services.Identity.Infrastructure\SwiftParcel.Services.Identity.Infrastructure\SwiftParcel.Services.Identity.Infrastructure.csproj", "{843424FB-FBC0-4F36-80E4-D839EE0837C0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwiftParcel.Services.Identity.Infrastructure", "src\SwiftParcel.Services.Identity.Infrastructure\SwiftParcel.Services.Identity.Infrastructure\SwiftParcel.Services.Identity.Infrastructure.csproj", "{843424FB-FBC0-4F36-80E4-D839EE0837C0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0D99CB76-AD5F-4C96-A7AA-418BC0048C83}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Services.Identity.Application.UnitTests", "Services.Identity.Application.UnitTests", "{4BCEB813-D70B-4338-ABAE-6556283F93E3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application.UnitTests", "tests\SwiftParcel.Services.Identity.Application.UnitTests\Application.UnitTests\Application.UnitTests.csproj", "{EDF2F21A-7A1B-43E9-BBB4-642261242FB3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Services.Identity.Infrastructure.UnitTests", "Services.Identity.Infrastructure.UnitTests", "{F00145B8-A4D2-4D7E-A618-7C3C4B1ED853}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.UnitTests", "tests\SwiftParcel.Services.Identity.Infrastructure.UnitTests\Infrastructure.UnitTests\Infrastructure.UnitTests.csproj", "{CBB9BB09-93BF-4802-9A83-F01056230254}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {2ADD2415-5EA3-4004-A416-9C131FFB8173}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2ADD2415-5EA3-4004-A416-9C131FFB8173}.Debug|Any CPU.Build.0 = Debug|Any CPU @@ -46,6 +53,17 @@ Global {843424FB-FBC0-4F36-80E4-D839EE0837C0}.Debug|Any CPU.Build.0 = Debug|Any CPU {843424FB-FBC0-4F36-80E4-D839EE0837C0}.Release|Any CPU.ActiveCfg = Release|Any CPU {843424FB-FBC0-4F36-80E4-D839EE0837C0}.Release|Any CPU.Build.0 = Release|Any CPU + {EDF2F21A-7A1B-43E9-BBB4-642261242FB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EDF2F21A-7A1B-43E9-BBB4-642261242FB3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EDF2F21A-7A1B-43E9-BBB4-642261242FB3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EDF2F21A-7A1B-43E9-BBB4-642261242FB3}.Release|Any CPU.Build.0 = Release|Any CPU + {CBB9BB09-93BF-4802-9A83-F01056230254}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CBB9BB09-93BF-4802-9A83-F01056230254}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CBB9BB09-93BF-4802-9A83-F01056230254}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CBB9BB09-93BF-4802-9A83-F01056230254}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {B85B3054-38B0-4EA1-8943-30DB1A33C2B1} = {5F2EF2C7-5798-4569-85F7-8985655984CC} @@ -56,5 +74,12 @@ Global {F72C4B04-5CE8-41C4-AA61-E3F415D82C80} = {B9FBBB29-CC89-44BC-B845-FEB03B468FD9} {BCF8AC11-5D37-4198-B089-0CB0519193A8} = {5F2EF2C7-5798-4569-85F7-8985655984CC} {843424FB-FBC0-4F36-80E4-D839EE0837C0} = {BCF8AC11-5D37-4198-B089-0CB0519193A8} + {4BCEB813-D70B-4338-ABAE-6556283F93E3} = {0D99CB76-AD5F-4C96-A7AA-418BC0048C83} + {EDF2F21A-7A1B-43E9-BBB4-642261242FB3} = {4BCEB813-D70B-4338-ABAE-6556283F93E3} + {F00145B8-A4D2-4D7E-A618-7C3C4B1ED853} = {0D99CB76-AD5F-4C96-A7AA-418BC0048C83} + {CBB9BB09-93BF-4802-9A83-F01056230254} = {F00145B8-A4D2-4D7E-A618-7C3C4B1ED853} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {313FC8F3-55A5-4607-8A49-193999D5D813} EndGlobalSection EndGlobal diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/.env b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/.env new file mode 100644 index 0000000..07ad993 --- /dev/null +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/.env @@ -0,0 +1,2 @@ +GOOGLE_CLIENT_ID=396949671840-7j196l369ai3b5tj3a7if5cuhp4c80f7.apps.googleusercontent.com +GOOGLE_CLIENT_SECRET=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 diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/Program.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/Program.cs index 28595b1..4db33e2 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/Program.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/Program.cs @@ -16,6 +16,10 @@ using Convey.Logging; using Convey.Secrets.Vault; using SwiftParcel.Services.Identity.Infrastructure; +using MongoDB.Driver; +using Swashbuckle.AspNetCore.Swagger; +using Microsoft.AspNetCore.Authentication.Google; + namespace src.SwiftParcel.Services.Identity.Api { public class Program @@ -23,21 +27,12 @@ public class Program public static async Task Main(string[] args) => await WebHost.CreateDefaultBuilder(args) .ConfigureServices(services => services - .AddCors(options => - { - options.AddPolicy("AllowAll", builder => - builder.AllowAnyOrigin() - .AllowAnyMethod() - .AllowAnyHeader()); - }) .AddConvey() .AddWebApi() .AddApplication() .AddInfrastructure() - .Build() - ) + .Build()) .Configure(app => app - .UseCors("AllowAll") .UseInfrastructure() .UseEndpoints(endpoints => endpoints .Get("", ctx => ctx.Response.WriteAsync(ctx.RequestServices.GetService().Name)) @@ -63,6 +58,11 @@ public static async Task Main(string[] args) await ctx.RequestServices.GetService().SignUpAsync(cmd); await ctx.Response.Created("identity/me"); }) + .Post("google-sign-up", async (cmd, ctx) => + { + await ctx.RequestServices.GetService().SignUpWithGoogleAsync(cmd); + await ctx.Response.Created("identity/me"); + }) .Post("access-tokens/revoke", async (cmd, ctx) => { await ctx.RequestServices.GetService().DeactivateAsync(cmd.AccessToken); @@ -95,5 +95,6 @@ private static async Task GetUserAsync(Guid id, HttpContext context) await context.Response.WriteJsonAsync(user); } + } } \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/Properties/launchSettings.json b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/Properties/launchSettings.json index 0f606d7..6984d48 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/Properties/launchSettings.json +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/Properties/launchSettings.json @@ -4,7 +4,7 @@ "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:6001", - "sslPort": 44309 + } }, "profiles": { @@ -12,7 +12,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "applicationUrl": "https://localhost:6001", + "applicationUrl": "http://localhost:5004", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -20,6 +20,7 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, + "sslPort": 44300, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api.csproj b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api.csproj index 584643f..c4b5029 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api.csproj +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api.csproj @@ -11,6 +11,9 @@ + + + @@ -20,4 +23,9 @@ + + + + + diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/appsettings.json b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/appsettings.json index f69d6fa..d320b10 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/appsettings.json +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/appsettings.json @@ -10,7 +10,7 @@ "url": "http://localhost:8500", "service": "identity-service", "address": "docker.for.win.localhost", - "port": "5004", + "port": "6001", "pingEnabled": true, "pingEndpoint": "ping", "pingInterval": 3, @@ -62,6 +62,10 @@ } } }, + "GoogleAuth": { + "ClientId": "YourGoogleClientId", + "ClientSecret": "YourGoogleClientSecret" + }, "logger": { "level": "information", "excludePaths": ["/", "/ping", "/metrics"], @@ -100,6 +104,11 @@ }, "jwt": { + "certificate": { + "location": "certs/certificate.pfx", + "password": "", + "rawData": "" + }, "issuerSigningKey": "eyJhbGciOiJIUzI1NiJ9.eyJSb2xlIjoiQWRtaW4iLCJJc3N1ZXIiOiJJc3N1ZXIiLCJVc2VybmFtZSI6IkphdmFJblVzZSIsImV4cCI6MTY5ODYwMTg3NSwiaWF0IjoxNjk4NjAxODc1fQ.nxJlaEy9xNO4noVh84qD9BSoLvjq1xu4LGhwBaEF9Ec", "expiryMinutes": 60, @@ -110,6 +119,11 @@ "allowAnonymousEndpoints": ["/sign-in", "/sign-up"] }, + "GoogleAuthSettings": { + "ClientId": "", + "ClientSecret": "" + }, + "swagger": { "enabled": true, "reDocEnabled": false, @@ -126,33 +140,45 @@ "seed": false }, "rabbitMq": { - "namespace": "identity", + "connectionName": "identity-service", "retries": 3, "retryInterval": 2, + "conventionsCasing": "snakeCase", + "logger": { + "enabled": true + }, "username": "guest", "password": "guest", "virtualHost": "/", "port": 5672, "hostnames": [ - "localhost", - "0.0.0.0" + "localhost" ], - "requestTimeout": "00:00:10", - "publishConfirmTimeout": "00:00:01", - "recoveryInterval": "00:00:10", - "persistentDeliveryMode": true, - "autoCloseConnection": true, - "automaticRecovery": true, - "topologyRecovery": true, + "requestedConnectionTimeout": "00:00:30", + "requestedHeartbeat": "00:01:00", + "socketReadTimeout": "00:00:30", + "socketWriteTimeout": "00:00:30", + "continuationTimeout": "00:00:20", + "handshakeContinuationTimeout": "00:00:10", + "networkRecoveryInterval": "00:00:05", "exchange": { + "declare": true, "durable": true, "autoDelete": false, - "type": "Topic" + "type": "topic", + "name": "identity" }, "queue": { - "autoDelete": false, + "declare": true, "durable": true, - "exclusive": false - } + "exclusive": false, + "autoDelete": false, + "template": "identity-service/{{exchange}}.{{message}}" + }, + "context": { + "enabled": true, + "header": "message_context" + }, + "spanContextHeader": "span_context" } } diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/certificate.crt b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/certificate.crt new file mode 100644 index 0000000..fdfc35c --- /dev/null +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/certificate.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKzCCAhMCFFlhkNkHE2vgGQgKQWZr/VPSfA/LMA0GCSqGSIb3DQEBCwUAMFIx +CzAJBgNVBAYTAlBMMQ8wDQYDVQQIDAZXYXJzYXcxDzANBgNVBAcMBldhcnNhdzEh +MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTIzMTIwODIzMTUw +M1oXDTI0MTIwNzIzMTUwM1owUjELMAkGA1UEBhMCUEwxDzANBgNVBAgMBldhcnNh +dzEPMA0GA1UEBwwGV2Fyc2F3MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0 +eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6cstzBdJUF7/l +s40HY/wDa6CUVlxNC2uZl5YoZvpWN63hj89v73VWM5lAXgS+v2GcO+kxoXmS6FJ7 +pt7RWeKo+4+OZATU352npet3fEiF1CTiq8Sg/z5bhQNqxZ06eZwPGVKvAZ6Ps4AB +dZJP0lekUEC+tg5vyORLragfBNqxCamRGTydBBQgRS4zj2hbjh66BH68OgUfPK3G +Xun8TLoV5Elv6VhKlUVBtBkFzypHDIiey1+mNKOKAGWuuVDqgOK+h+lLYZNnD5Az +yZ5Pmzi9Xll/NS3hkBFVrTBl9o7QN4cHE6+CnlsdYlWZYpbItwDtLSU10W337Sse +GFHlI4uJAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAAPcBqtL3VQJC8pEmS8u7g/y +j2gtRCHZTshMxVOramZcwhnim903iF3GOyTK8+sslVuKHyqrC1LAghhoQsJ89w5C +J2c1P0UEcC+hGGg4AV59PxldA17aTFoH2IhOuN0tgyOTzV4a8n/hjvWIWiw4kHQ6 +JebLJXuP6/ZgcrZuUVwxfIxFgRcb5h5jMIjRy56v3opBSxcGyG3YdP3zlD9IC1z2 +Byd1w7AegQfqbD6x9u6XsaJUnjmxYD/JAywtHN0Ion50quEwO+bUPkguy6kxb1/S +wbtA8EA0IH0b9xYuArmwyqk1FUvVUdjbaxRS73TBFtFzira4ZUc73KDUT3YXn40= +-----END CERTIFICATE----- diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/certificate.csr b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/certificate.csr new file mode 100644 index 0000000..3e005b1 --- /dev/null +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/certificate.csr @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIClzCCAX8CAQAwUjELMAkGA1UEBhMCUEwxDzANBgNVBAgMBldhcnNhdzEPMA0G +A1UEBwwGV2Fyc2F3MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6cstzBdJUF7/ls40HY/wD +a6CUVlxNC2uZl5YoZvpWN63hj89v73VWM5lAXgS+v2GcO+kxoXmS6FJ7pt7RWeKo ++4+OZATU352npet3fEiF1CTiq8Sg/z5bhQNqxZ06eZwPGVKvAZ6Ps4ABdZJP0lek +UEC+tg5vyORLragfBNqxCamRGTydBBQgRS4zj2hbjh66BH68OgUfPK3GXun8TLoV +5Elv6VhKlUVBtBkFzypHDIiey1+mNKOKAGWuuVDqgOK+h+lLYZNnD5AzyZ5Pmzi9 +Xll/NS3hkBFVrTBl9o7QN4cHE6+CnlsdYlWZYpbItwDtLSU10W337SseGFHlI4uJ +AgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEALDuJ4fFhfnDKdl75ZeClMWK7I2uk +nsZlo8uINmG0W6ifKabSySNiQ9O+HaM9ubL99LxwTn9tKGhZNJERTMytkCBKdcuh +ffmO1f1AdNI0epI/0U7z4RWO6Xg2xi1jrvgwAROIOXyHVeyYP6Yj/FnU9Ug9zte6 +IOhbxo/udQDx29LjQgtlIWFYCLhUZNxWVc0NiKEhhygB0gx2P0oRVfcvDMGQr48F ++BzrThKt7oyVak2urWxVuEfesJ882nkRxzXXUBtFm6+73oflSfQXJVEIr2Kuh+U3 +vowIF4HjaNtqhCmSXADqPR365dZWWZXXKN7NNq23nmUjgsVe0O+/MRrKpQ== +-----END CERTIFICATE REQUEST----- diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/localhost.cer b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/localhost.cer new file mode 100644 index 0000000..8cbe23d --- /dev/null +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/localhost.cer @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDlTCCAn0CFD8DXxTdI0Uh1r5I5n1zhQxBTaaKMA0GCSqGSIb3DQEBCwUAMIGG +MQswCQYDVQQGEwJQbDEPMA0GA1UECAwGV2Fyc2F3MQ8wDQYDVQQHDAZXYXJzYXcx +GDAWBgNVBAoMD01pTklTV0lGVFBBUkNFTDENMAsGA1UECwwELk5FVDEsMCoGCSqG +SIb3DQEJARYddm96bmVzZW5za2lqYW5kcmVqNUBnbWFpbC5jb20wHhcNMjMxMTA0 +MTM0NDU2WhcNMjQxMTAzMTM0NDU2WjCBhjELMAkGA1UEBhMCUGwxDzANBgNVBAgM +BldhcnNhdzEPMA0GA1UEBwwGV2Fyc2F3MRgwFgYDVQQKDA9NaU5JU1dJRlRQQVJD +RUwxDTALBgNVBAsMBC5ORVQxLDAqBgkqhkiG9w0BCQEWHXZvem5lc2Vuc2tpamFu +ZHJlajVAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +vV2A1ITideCTlcICCr8fwIYKNQafAELhpwFI7uoOH8IUa16y/gGLgdmaOfLPYGoM +lrKLysqG0b2NhxIXl/IDVFVihrNvueIOd/UX7iGMMwSog3eIqgRq0vX5blUnnWXW +56E9QB8koEad67mv+/bTmZFs3vgR6N919nrG9251jXxsdVfkgvh8zKk9qc024q1h +WCL1zReI+SmwAJdQ+r/eJvtFG/zMTf341gMFTJbyZjAKnxViPBNuAEbsL3LzYSyo +PpRaKFvG/MvrFRwS1LCQ7g/qoBc4yaR/dHKIcUgfVvEX7u6Eo68nn/hWkDpGuH0N +Fmj1UE2QU1aV+ScDSapbywIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQApSG4IGnyL +8svEKdT8ujsoVPwY6ygTZpHPby136/6nU8GvhPad+0F+r4Qw5+oCYsJPCQzDcHZU +zlxVf7Yw+iSzUaJn1iT38OTi7jKwA7cyjlaDGcyKSF3aKzML8PD6fSwDKbG9EIGU +6c9cbH3reYAWenlRaQ40sqG2RKvjtZ6tlI+GLN6GlpAR28h/GlvyD+KqYAZaggWr +MJZduMjEVyV7ESUUAwCxdDm38W8wHzTjr6dnw6as7luxD/cO0b41ARy/xyXwrsI6 +EAUGsRUWAYY4TMNQWgfNY0Djx+vx/Phg66+Ld+Ir0AzH1SsugNS/Hm+7SMjSQ3St +uiIN/EnjtPyq +-----END CERTIFICATE----- diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/localhost.csr b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/localhost.csr new file mode 100644 index 0000000..2dee550 --- /dev/null +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/localhost.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICzDCCAbQCAQAwgYYxCzAJBgNVBAYTAlBsMQ8wDQYDVQQIDAZXYXJzYXcxDzAN +BgNVBAcMBldhcnNhdzEYMBYGA1UECgwPTWlOSVNXSUZUUEFSQ0VMMQ0wCwYDVQQL +DAQuTkVUMSwwKgYJKoZIhvcNAQkBFh12b3puZXNlbnNraWphbmRyZWo1QGdtYWls +LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL1dgNSE4nXgk5XC +Agq/H8CGCjUGnwBC4acBSO7qDh/CFGtesv4Bi4HZmjnyz2BqDJayi8rKhtG9jYcS +F5fyA1RVYoazb7niDnf1F+4hjDMEqIN3iKoEatL1+W5VJ51l1uehPUAfJKBGneu5 +r/v205mRbN74EejfdfZ6xvdudY18bHVX5IL4fMypPanNNuKtYVgi9c0XiPkpsACX +UPq/3ib7RRv8zE39+NYDBUyW8mYwCp8VYjwTbgBG7C9y82EsqD6UWihbxvzL6xUc +EtSwkO4P6qAXOMmkf3RyiHFIH1bxF+7uhKOvJ5/4VpA6Rrh9DRZo9VBNkFNWlfkn +A0mqW8sCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQCMVaoMMvlixogyD4KQ170q +WovRn920ZJ7VzVrst/8/S0l6DuYOA6/VV/FYSqDcGaVW0e8XfG2qObEq/lu4aBYs +2WNWUV8nv36VO/8w8CdYFUcCX0HtETxQWQ0vvogLAjPVMgy9+O/AeN4mrZVnTAOo +/2GN1AQgEaDCMOzCjEfZQ6yhLK1icvHP5sNKatvF6ecKdfYZdAX5GlUFVSwThoB+ +wUMhoOP4+dTcZiWtWw089cmKCHXiZB02tA84Qs2RcmgpMhKgbsq09b8T6yyurIOE +q/rIflm8k82vMZ8lOoGwjt8rWBelqBwDOhu48hHEjwjiVqHfKF7jXI7RF/5VP00v +-----END CERTIFICATE REQUEST----- diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/localhost.key b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/localhost.key new file mode 100644 index 0000000..704dc6a --- /dev/null +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/localhost.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC9XYDUhOJ14JOV +wgIKvx/Ahgo1Bp8AQuGnAUju6g4fwhRrXrL+AYuB2Zo58s9gagyWsovKyobRvY2H +EheX8gNUVWKGs2+54g539RfuIYwzBKiDd4iqBGrS9fluVSedZdbnoT1AHySgRp3r +ua/79tOZkWze+BHo33X2esb3bnWNfGx1V+SC+HzMqT2pzTbirWFYIvXNF4j5KbAA +l1D6v94m+0Ub/MxN/fjWAwVMlvJmMAqfFWI8E24ARuwvcvNhLKg+lFooW8b8y+sV +HBLUsJDuD+qgFzjJpH90cohxSB9W8Rfu7oSjryef+FaQOka4fQ0WaPVQTZBTVpX5 +JwNJqlvLAgMBAAECggEAMUH6yo08eSwvR8PdEx2PLV3QluV/g6piFzaS2a7heKpb +9GgASGlveBn577v07J/5YVPVYC1Zb4KOp4VxxLEL46qIRAC1/dTLLbIABQ4N8ZFz +Iit8TblzbifUt7oH4X4Frl/3VlAjj1t/Jbu6BuE4PpihCMjfo8NI5JIgYmmFw4UY +MyczluKbmUQmZiMv5UniR/mByMl5Wpe/9VyHF6ekjswbIdsIlVitOgJqZ5K6vaO2 +F+NOz9x4OjiAWNzXIefBD8JzWPGdpOud7f62SjDmdoClMxpZJEvHkd3m68QWejF1 +0whs2iRRK4PwmU8a1KCNu3446grTxn4DcehrR5m1WQKBgQD4UM15tqWnkaVdS/hA +HE8I4n9Uh4j413ShKuPUYxb0I/2/3Djj4eACPH+8QbdDoRBoW0Fh8UPtrHnlR8z8 +dNUz/94t1tCxf2m+dq5h3LOPgfn1Z8aKKukxD5MyJz+3i17gVe+z4isI9FMeb3j5 +WntE3UMDhT7YuTHyO3dGsYKA6QKBgQDDOa+YZUKGXSUNwC0RQepkS4FKPpuSsOgR +yK6ETQPUnRGCTm0/Gfp4RmZdN8HU+34rRRN1m+PPbQLBnJGUR/+uVmaJfgQrzeZm +aNqLVJRV8EcRSGPXBzw2h7B+wdJ9wronff3GBJJEvaJKC/3AIU9/GvzOpGpcP+63 +Ce/H4wHmkwKBgBeZ7n20C3iq0jHU56qIRkbM6FdLboxgRhiPcAoexO69gP+8Wbfn +Hxh3UZi+EkIeS+XvTn6VpOQxMYDGW0hffmgr2J04vqswNgfBVf+9L1/jdbOKgodd +Xr6oAVCSBG9YktL1uAu74mU+e6v34mKo4/B+q0DpaDjHGWUxm7ufYY0JAoGBAJHW +wCR6UK4LWyjgjUo9EZqHBTiveIym91aBsk1HMztbcr8DU302LPQ4szOj+XrfvSsZ +2SWcpRxJdSupiN9awdg81zCTIQ6QLDQI1GoNbxqb+psgluUFlKevyduHc3VbSLFD +35ZTx+juORRTzOYAfGCZio3cKVBsuimmlKegH8VdAoGBAKKBxGzAg5AuIGPbzZnc +3lTC3NEaGLkocL8eBcS3UH/g/6rMZ4sA0Y2pICeoGSSxcNtFG6HvdvwnOgfcBhf+ +7E5EjQZBeV61i3QWCmo/D7IhF35i0cepa+PRmonNF+gZJFs4iZTrpclGheSfcWQb +x3KquHfWcvu4iylT0WxOyqo5 +-----END PRIVATE KEY----- diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/private.key b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/private.key new file mode 100644 index 0000000..c5ed826 --- /dev/null +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Api/SwiftParcel.Services.Identity.Api/certs/private.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC6cstzBdJUF7/l +s40HY/wDa6CUVlxNC2uZl5YoZvpWN63hj89v73VWM5lAXgS+v2GcO+kxoXmS6FJ7 +pt7RWeKo+4+OZATU352npet3fEiF1CTiq8Sg/z5bhQNqxZ06eZwPGVKvAZ6Ps4AB +dZJP0lekUEC+tg5vyORLragfBNqxCamRGTydBBQgRS4zj2hbjh66BH68OgUfPK3G +Xun8TLoV5Elv6VhKlUVBtBkFzypHDIiey1+mNKOKAGWuuVDqgOK+h+lLYZNnD5Az +yZ5Pmzi9Xll/NS3hkBFVrTBl9o7QN4cHE6+CnlsdYlWZYpbItwDtLSU10W337Sse +GFHlI4uJAgMBAAECggEAEkCze+XXoMmn7Mz6ex0d0nhmgqsfM2AxNxq81aT6MDdY +d5bwdfr9c9ROlht2L7XU8Jp5YnVWTWxeydHs6ZYY/1XMSKJwSiY/nQZcYkjJoUdi +2ol6ERBX2pYPnvPsZ+l4no5KNs2L6DHIj+wCiDu1foWoRvtT97vVJLcCGNJM9gyL +NBxY3Lg4qN5Z+JNCwFhr6akoWGOb2CY3epxBPOdEoXNQJLqPj6cVcrK9blmGwjp2 +UJBkd4mOphHSYggYhjoDl0Fd2lBvOHCBDg7iI/zOv2M+xwH6fIbwsFmRSrfD6xxf +4iWqEdVaXl/VLvR2/C2byJ0F1cGMziSaSIi0GkqXIQKBgQDdnwvyFOXjieEuo0Xn +Kdoa3jPCQoPYeVsIprc+tiAr9cYWvSoVlD2ZhALz2dcQ0VhMXfJ/RT+/M2DL9Puq +PoAMMEK2CN83GQ/Ee94rEV78JIRiyn8uuoOZVCKrhZWS+SpwfcCHpbm9zt50Kk59 +vOKVaixHFMZ6iJVDKlq7/nuFKQKBgQDXXvmGuEFT1NmpsIcjgyTt/p3B57SjjX6V +6c3ez1k6XpXT2hEAUYRKiStzn6JJuEpAZFnPd+8DmxHhAS3x/f5NPE9wxR5m8X6i +6YOhNth/7Bmnc86C2HyMEI24bqOA2FdZXBwtrG0kisrm8ZgF41Tbn/B5tP7RjR+J +8JlQpyk/YQKBgQDMpGR/cVSmO1c3nYSa5EaFsNDSfGEKr2SkJG3o9nvWm+gCb0JB +EtWewAuJ6Ily+hwyyD130BYBFAw5yUnp9jR/DMFNHfwgAIBSyp5n3m57Ha0xHBM8 +rvV9y/cHsx5ezL2nijcSCrHs1u0LD1XTPWts1NF2AXUVCLHrh1ypY1/joQKBgQCN +N30t1MshCMPf/BvgMTGZzAXtCtzrEPAbpD6VBihfmX1sd+QF7W1s4hxXqChlZi1p +o2MwIlfaaWeDvZ4+umhwhEcijl2s6lQ6xhrFLo0u88Sug8S5DvI7dO84K3OSHEjK +Exd260R1XXxFicxAsBsIFws7Am7DQGGZtOUSeUkWgQKBgQDBggeHuHxQgwKQIm1d +E3eL4UlswxT4GokbRCu/fknRUQxOiWldFFFQ90F+YdFyBxfp8LRAsM1e9Int0o6E +18EqJL3NrGf7b+6TVZuqq5dr1s76UYmD9aYFx/19PlrutYc4YHgS5U2bBYbSqOlq +IopvkmHS17tBdG2EA+WYhEwQGQ== +-----END PRIVATE KEY----- diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Commands/Handlers/SignUpHandler.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Commands/Handlers/SignUpHandler.cs index cd82520..fb9657d 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Commands/Handlers/SignUpHandler.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Commands/Handlers/SignUpHandler.cs @@ -17,9 +17,18 @@ public SignUpHandler(IIdentityService identityService) } // public Task HandleAsync(SignUp command) => _identityService.SignUpAsync(command); - public Task HandleAsync(SignUp command, CancellationToken cancellationToken = default) + public async Task HandleAsync(SignUp command, CancellationToken cancellationToken = default) { - return _identityService.SignUpAsync(command); + // if (!string.IsNullOrEmpty(command.GoogleToken)) + // { + // // Handle Google sign-up + // await _identityService.SignUpWithGoogleAsync(command); + // } + // else + // { + // Handle regular sign-up + await _identityService.SignUpAsync(command); + //} } } } \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Commands/SignIn.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Commands/SignIn.cs index f235ebc..f8f797c 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Commands/SignIn.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Commands/SignIn.cs @@ -9,7 +9,7 @@ namespace SwiftParcel.Services.Identity.Application.Commands { - // [PublicMessage] + [Contract] public class SignIn : ICommand, IRequest { public string Email { get; set; } diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Commands/SignUp.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Commands/SignUp.cs index bd86994..fa576be 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Commands/SignUp.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Commands/SignUp.cs @@ -17,6 +17,7 @@ public class SignUp : ConveyICommand public string Email { get; } public string Password { get; } public string Role { get; } + //public string GoogleToken { get; set; } public IEnumerable Permissions { get; } public SignUp(Guid userId, string email, string password, string role, IEnumerable permissions) diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Commands/SignUpGoogle.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Commands/SignUpGoogle.cs new file mode 100644 index 0000000..ffd93fd --- /dev/null +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Commands/SignUpGoogle.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using ConveyICommand = Convey.CQRS.Commands.ICommand; + +namespace SwiftParcel.Services.Identity.Application.Commands +{ + public class SignUpGoogle : ConveyICommand + { + public Guid UserId { get; } + public string Email { get; } + public string Password { get; } + public string Role { get; } + public string GoogleToken { get; set; } + public IEnumerable Permissions { get; } + + public SignUpGoogle(Guid userId, string email, string password, string role, IEnumerable permissions) + { + UserId = userId == Guid.Empty ? Guid.NewGuid() : userId; + Email = email; + Password = password; + Role = role; + Permissions = permissions; + } + } +} \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Events/Rejected/SignInRejected.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Events/Rejected/SignInRejected.cs index b815d7e..c233e2f 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Events/Rejected/SignInRejected.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Events/Rejected/SignInRejected.cs @@ -6,6 +6,7 @@ namespace SwiftParcel.Services.Identity.Application.Events.Rejected { + [Contract] public class SignInRejected : IRejectedEvent { public string Email { get; } diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Events/Rejected/SignUpRejected.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Events/Rejected/SignUpRejected.cs index 11168b9..7776750 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Events/Rejected/SignUpRejected.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Events/Rejected/SignUpRejected.cs @@ -6,6 +6,7 @@ namespace SwiftParcel.Services.Identity.Application.Events.Rejected { + [Contract] public class SignUpRejected : IRejectedEvent { public string Email { get; } diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Events/SignedIn.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Events/SignedIn.cs index 52eb1fe..a800e2c 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Events/SignedIn.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Events/SignedIn.cs @@ -8,6 +8,7 @@ namespace SwiftParcel.Services.Identity.Application.Events { public class SignedIn: IEvent { + [Contract] // [PublicMessage] public Guid UserId { get; } public string Role { get; } diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Events/SignedUp.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Events/SignedUp.cs index 26aff8c..f8f222b 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Events/SignedUp.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Events/SignedUp.cs @@ -11,6 +11,7 @@ public class SignedUp : IEvent public Guid UserId { get; } public string Email { get; } public string Role { get; } + public string GoogleToken { get; set; } public SignedUp(Guid userId, string email, string role) { diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Exceptions/UserNotFoundException.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Exceptions/UserNotFoundException.cs index 8ca1435..727e527 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Exceptions/UserNotFoundException.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Exceptions/UserNotFoundException.cs @@ -8,9 +8,13 @@ namespace SwiftParcel.Services.Identity.Application.Exceptions public class UserNotFoundException : AppException { public override string Code { get; } = "user_not_found"; - public Guid UserId { get; } + public string UserId { get; } public UserNotFoundException(Guid userId) : base($"User with ID: '{userId}' was not found.") + { + UserId = userId.ToString(); + } + public UserNotFoundException(string userId) : base($"User with ID: '{userId}' was not found.") { UserId = userId; } diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Extensions.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Extensions.cs index f8d5a06..753c1bb 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Extensions.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Extensions.cs @@ -30,32 +30,5 @@ public static IConveyBuilder AddApplication(this IConveyBuilder builder) .AddEventHandlers() .AddInMemoryCommandDispatcher() .AddInMemoryEventDispatcher(); - // public static IConveyBuilder AddInfrastructureModule(this IConveyBuilder builder) - // { - // builder.Services.AddSingleton(); - // builder.Services.AddSingleton(); - // builder.Services.AddTransient(); - // builder.Services.AddTransient(); - // builder.Services.AddTransient(); - // builder.Services.AddSingleton, PasswordHasher>(); - - // return builder.AddJwt() - // .AddCommandHandlers() - // .AddEventHandlers() - // .AddQueryHandlers() - // .AddRabbitMq() - // .AddMongo() - // .AddMongoRepository("refreshTokens") - // .AddMongoRepository("users"); - - // } - - // public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app) - // { - // // TODO: devide the extenstion in the mongo repositories and the genegral - - // app.UseInitializers().UseRebbitMq(); - // return app; - // } } } \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/IIdentityContext.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/IIdentityContext.cs index aae13b8..b54bbdd 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/IIdentityContext.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/IIdentityContext.cs @@ -10,7 +10,8 @@ public interface IIdentityContext Guid Id { get; } string Role { get; } bool IsAuthenticated { get; } - bool IsAdmin { get; } + bool IsOfficeWorker { get; } + bool IsCourier { get; } IDictionary Claims { get; } } } \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Services/IIdentityService.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Services/IIdentityService.cs index 1545832..e3ead85 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Services/IIdentityService.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Services/IIdentityService.cs @@ -15,5 +15,6 @@ public interface IIdentityService Task GetAsync(Guid id); Task SignInAsync(SignIn command); Task SignUpAsync(SignUp command); + Task SignUpWithGoogleAsync(SignUpGoogle command); } } \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Services/IMessageBroker.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Services/IMessageBroker.cs index 9879b13..1f3beac 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Services/IMessageBroker.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Services/IMessageBroker.cs @@ -12,8 +12,5 @@ namespace SwiftParcel.Services.Identity.Application.Services public interface IMessageBroker { Task PublishAsync(T @event) where T : class, IEvent; - // Task GetAsync(Guid id); - // Task SignInAsync(SignIn command); - // Task SignUpAsync(SignUp command); } } \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Services/IdentityService.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Services/IdentityService.cs index d55fefe..3518466 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Services/IdentityService.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/Services/IdentityService.cs @@ -19,7 +19,8 @@ using Microsoft.Extensions.Logging; using SwiftParcel.Services.Identity.Application.UserDTO; using System.Text.RegularExpressions; - +using SwiftParcel.Services.Identity.Application.Exceptions; +using Google.Apis.Auth; @@ -30,7 +31,6 @@ public class IdentityService : IIdentityService private readonly IUserRepository _userRepository; private readonly IPasswordService _passwordService; private readonly IJwtProvider _jwtProvider; - private readonly IJwtHandler _jwtHandler; private readonly IMessageBroker _messageBroker; private readonly IRefreshTokenService _refreshTokenService; private readonly ILogger _logger; @@ -117,5 +117,94 @@ public async Task SignUpAsync(SignUp command) _logger.LogInformation($"Created an account for the user with id: {user.Id}."); await _messageBroker.PublishAsync(new SignedUp(user.Id, user.Email, user.Role)); } + + public async Task SignInWithGoogleAsync(SignUpGoogle command) + { + // Example pseudocode for validating the Google token and retrieving user info + + var googleToken = command.GoogleToken; + if (string.IsNullOrWhiteSpace(googleToken)) + { + throw new ArgumentException("Google token is required for Google sign-up."); + } + + var googleUserData = await ValidateGoogleTokenAndGetUserData(googleToken); + + var user = await _userRepository.GetAsync(googleUserData.Email); + if (user == null) + { + // Optionally handle auto-registration or throw an exception + throw new UserNotFoundException(googleUserData.GoogleId); + } + + var auth = _jwtProvider.Create(user.Id, user.Role, claims: null); // Update as needed + auth.RefreshToken = await _refreshTokenService.CreateAsync(user.Id); + + // Log the sign-in event + _logger.LogInformation($"User with id: {user.Id} signed in with Google."); + await _messageBroker.PublishAsync(new SignedIn(user.Id, user.Role)); + + return auth; + } + + public async Task SignUpWithGoogleAsync(SignUpGoogle command) + { + // Extract Google token from the SignUp command + var googleToken = command.GoogleToken; + if (string.IsNullOrWhiteSpace(googleToken)) + { + throw new ArgumentException("Google token is required for Google sign-up."); + } + + var googleUserData = await ValidateGoogleTokenAndGetUserData(googleToken); + + var existingUser = await _userRepository.GetAsync(googleUserData.Email); + if (existingUser != null) + { + throw new EmailInUseException(googleUserData.Email); + } + + // Create a new user entity from Google data + var newUser = new User(Guid.NewGuid(), googleUserData.Email, "", "user", DateTime.UtcNow, new List()); + await _userRepository.AddAsync(newUser); + + var auth = _jwtProvider.Create(newUser.Id, newUser.Role, claims: null); + auth.RefreshToken = await _refreshTokenService.CreateAsync(newUser.Id); + + _logger.LogInformation($"User with id: {newUser.Id} signed up with Google."); + await _messageBroker.PublishAsync(new SignedUp(newUser.Id, newUser.Email, newUser.Role)); + + return auth; + } + + private async Task ValidateGoogleTokenAndGetUserData(string googleToken) + { + GoogleJsonWebSignature.Payload payload = null; + try + { + // Validate the token and get the payload + payload = await GoogleJsonWebSignature.ValidateAsync(googleToken); + } + catch (InvalidJwtException ex) + { + _logger.LogError($"Invalid JWT token: {ex.Message}"); + throw; // Or handle the exception as you see fit + } + + if (payload == null) + { + throw new InvalidOperationException("Unable to validate Google token and get payload."); + } + + // Create and return a DTO with the user's information + return new GoogleUserDto + { + Email = payload.Email, + Name = payload.Name, + ProfilePictureUrl = payload.Picture, + GoogleId = payload.Subject + + }; + } } } \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application.csproj b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application.csproj index 887af60..7997271 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application.csproj +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application.csproj @@ -16,6 +16,7 @@ + diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/UserDTO/GoogleUserDto.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/UserDTO/GoogleUserDto.cs new file mode 100644 index 0000000..52484f7 --- /dev/null +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/UserDTO/GoogleUserDto.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Identity.Application.UserDTO +{ + public class GoogleUserDto + { + public string Email { get; set; } + public string Name { get; set; } + public string ProfilePictureUrl { get; set; } + public string GivenName { get; set; } + public string FamilyName { get; set; } + + // If you need the Google Id + public string GoogleId { get; set; } + } +} \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/UserDTO/UserDTO.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/UserDTO/UserDTO.cs index 9c82337..d511b12 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/UserDTO/UserDTO.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Application/SwiftParcel.Services.Identity.Application/UserDTO/UserDTO.cs @@ -20,8 +20,6 @@ public UserDto() { } - - public UserDto(User user) { Id = user.Id; diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Entities/AggregateRoot.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Entities/AggregateRoot.cs index 57554bf..dc9bb8b 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Entities/AggregateRoot.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Entities/AggregateRoot.cs @@ -7,7 +7,7 @@ namespace SwiftParcel.Services.Identity.Core.Entities { public class AggregateRoot { - private readonly List _events = new List(); + private readonly List _events = new List(); public IEnumerable Events => _events; public AggregateId Id { get; protected set; } = new AggregateId(); // Initialize Id here public int Version { get; protected set; } diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Entities/ErrorCodes.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Entities/ErrorCodes.cs index e60a397..61b0815 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Entities/ErrorCodes.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Entities/ErrorCodes.cs @@ -10,7 +10,7 @@ public class ErrorCodes public static string UserNotFound => "user_not_found"; public static string EmailInUse => "email_in_use"; public static string InvalidCredentials => "invalid_credentials"; - public static string InvalidEmail => "invalid_email"; + public static string InvalidEmail => "invalid_email"; public static string InvalidPassword => "invalid_password"; public static string InvalidCurrentPassword => "invalid_current_password"; public static string InvalidRole => "invalid_role"; diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Entities/Role.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Entities/Role.cs index 019c673..33274cb 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Entities/Role.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Entities/Role.cs @@ -8,8 +8,7 @@ namespace SwiftParcel.Services.Identity.Core.Entities public class Role { public const string User = "user"; - public const string Admin = "admin"; - + public const string OfficeWorker = "officeworker"; public const string Courier = "courier"; public static bool IsValid(string role) @@ -21,7 +20,7 @@ public static bool IsValid(string role) role = role.ToLowerInvariant(); - return role == User || role == Courier ||role == Admin; + return role == User || role == Courier || role == OfficeWorker; } } } \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Exceptions/DamainException.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Exceptions/DomainException.cs similarity index 100% rename from SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Exceptions/DamainException.cs rename to SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Exceptions/DomainException.cs diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Repositories/IUserRepository.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Repositories/IUserRepository.cs index 8b84f2c..6941837 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Repositories/IUserRepository.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Core/SwiftParcel.Services.Identity.Core/Repositories/IUserRepository.cs @@ -11,6 +11,5 @@ public interface IUserRepository Task GetAsync(Guid id); Task GetAsync(string email); Task AddAsync(User user); - // Task UpdateAsync(User user); } } \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Auth/GoogleAuthSettings.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Auth/GoogleAuthSettings.cs new file mode 100644 index 0000000..769bfec --- /dev/null +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Auth/GoogleAuthSettings.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SwiftParcel.Services.Identity.Infrastructure.Auth +{ + public class GoogleAuthSettings + { + public string ClientId { get; set; } + public string ClientSecret { get; set; } + } +} \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Auth/Rgen.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Auth/Rgen.cs index b8e7b2b..d0683a9 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Auth/Rgen.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Auth/Rgen.cs @@ -14,8 +14,9 @@ public class Rgen: IRgen public string Generate(int length = 50, bool removeSpecialChars = true) { + using var rng = new RNGCryptoServiceProvider(); var bytes = new byte[length]; - RandomNumberGenerator.Fill(bytes); // Using the recommended RandomNumberGenerator static method + rng.GetBytes(bytes); var result = Convert.ToBase64String(bytes); return removeSpecialChars diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Contexts/IdentityContext.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Contexts/IdentityContext.cs index 0a97c81..e9efebc 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Contexts/IdentityContext.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Contexts/IdentityContext.cs @@ -12,7 +12,8 @@ public class IdentityContext : IIdentityContext public Guid Id { get; } public string Role { get; } = string.Empty; public bool IsAuthenticated { get; } - public bool IsAdmin { get; } + public bool IsOfficeWorker { get; } + public bool IsCourier { get; } public IDictionary Claims { get; } = new Dictionary(); internal IdentityContext() @@ -29,7 +30,8 @@ internal IdentityContext(string id, string role, bool isAuthenticated, IDictiona Id = Guid.TryParse(id, out var userId) ? userId : Guid.Empty; Role = role ?? string.Empty; IsAuthenticated = isAuthenticated; - IsAdmin = Role.Equals("admin", StringComparison.InvariantCultureIgnoreCase); + IsOfficeWorker = Role.Equals("officeworker", StringComparison.InvariantCultureIgnoreCase); + IsCourier = Role.Equals("courier", StringComparison.InvariantCultureIgnoreCase); Claims = claims ?? new Dictionary(); } diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs index 3140244..dd30fdf 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -34,10 +34,10 @@ public OutboxCommandHandlerDecorator(ICommandHandler handler, IMessage // ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(command)) // : _handler.HandleAsync(command); - public Task HandleAsync(TCommand command, CancellationToken cancellationToken = default) + public Task HandleAsync(TCommand command, CancellationToken cancellationToken) => _enabled - ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(command, cancellationToken)) - : _handler.HandleAsync(command, cancellationToken); + ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(command)) + : _handler.HandleAsync(command); } } \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs index 443225d..91b5cfc 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -34,10 +34,10 @@ public OutboxEventHandlerDecorator(IEventHandler handler, IMessageOutbox // ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(@event)) // : _handler.HandleAsync(@event); - public Task HandleAsync(TEvent @event, CancellationToken cancellationToken = default) + public Task HandleAsync(TEvent @event, CancellationToken cancellationToken) => _enabled - ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(@event, cancellationToken)) - : _handler.HandleAsync(@event, cancellationToken); + ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(@event)) + : _handler.HandleAsync(@event); } } \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Extensions.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Extensions.cs index 9a0b13a..0614411 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Extensions.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Extensions.cs @@ -34,7 +34,6 @@ using SwiftParcel.Services.Identity.Infrastructure.Contexts; using SwiftParcel.Services.Identity.Infrastructure.Decorators; using SwiftParcel.Services.Identity.Infrastructure.Exceptions; -using SwiftParcel.Services.Identity.Infrastructure.MessageBrokers; using SwiftParcel.Services.Identity.Infrastructure.Mongo.Documents; using SwiftParcel.Services.Identity.Infrastructure.Mongo.Repositories; using SwiftParcel.Services.Identity.Infrastructure.Persistence.Mongo.Repository; @@ -48,6 +47,10 @@ using Convey.WebApi.CQRS; using Convey.MessageBrokers.CQRS; using SwiftParcel.Services.Identity.Application; +using Convey.HTTP; +using SwiftParcel.Services.Identity.Infrastructure.Mongo; +using SwiftParcel.Services.Identity.Infrastructure.Services; +using Microsoft.AspNetCore.Authentication.Google; namespace SwiftParcel.Services.Identity.Infrastructure { @@ -55,6 +58,14 @@ public static class Extensions { public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) { + + var googleAuthSettings = new GoogleAuthSettings + { + ClientId = Environment.GetEnvironmentVariable("GOOGLE_CLIENT_ID"), + ClientSecret = Environment.GetEnvironmentVariable("GOOGLE_CLIENT_SECRET") + }; + + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton, PasswordHasher>(); @@ -68,13 +79,23 @@ public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) builder.Services.AddTransient(ctx => ctx.GetRequiredService().Create()); builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>)); builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>)); + + + builder.Services.AddAuthentication() + .AddGoogle(options => + { + options.ClientId = ""; + options.ClientSecret = ""; + + }); + return builder .AddErrorHandler() .AddQueryHandlers() .AddInMemoryQueryDispatcher() .AddJwt() - //.AddHttpClient() + .AddHttpClient() .AddConsul() .AddFabio() .AddExceptionToMessageMapper() @@ -97,7 +118,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app .UseJaeger() .UseConvey() .UseAccessTokenValidator() - //.UseMongo() + .UseMongo() .UsePublicContracts() .UseMetrics() .UseAuthentication() diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/MessageBrokers/MessageBroker.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/MessageBrokers/MessageBroker.cs deleted file mode 100644 index 2d27d9d..0000000 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/MessageBrokers/MessageBroker.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using SwiftParcel.Services.Identity.Application; -using SwiftParcel.Services.Identity.Application.Services; - - -namespace SwiftParcel.Services.Identity.Infrastructure.MessageBrokers -{ - public class MessageBroker: IMessageBroker - { - private readonly IBusPublisher _busPublisher; - private readonly ICorrelationContextAccessor _contextAccessor; - - public MessageBroker(IBusPublisher busPublisher, ICorrelationContextAccessor contextAccessor) - { - _busPublisher = busPublisher; - _contextAccessor = contextAccessor; - } - - public Task PublishAsync(T @event) where T : class, IEvent - => _busPublisher.PublishAsync(@event, _contextAccessor.CorrelationContext.ToString()); - - } -} \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Mongo/Documents/Extensions.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Mongo/Documents/Extensions.cs index 8805ff1..49eef09 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Mongo/Documents/Extensions.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Mongo/Documents/Extensions.cs @@ -13,6 +13,7 @@ public static User AsEntity(this UserDocument document) => new User(document.Id, document.Email, document.Password, document.Role, document.CreatedAt, document.Permissions); + public static UserDocument AsDocument(this User entity) => new UserDocument { diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Mongo/Query/GetUserHandler.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Mongo/Query/GetUserHandler.cs index 80c5742..6234988 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Mongo/Query/GetUserHandler.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Mongo/Query/GetUserHandler.cs @@ -23,7 +23,7 @@ public async Task HandleAsync(GetUser query, CancellationToken cancella { var user = await _userRepository.GetAsync(query.UserId); - return user.AsDto(); + return user?.AsDto(); } } } \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Mongo/Repositories/RefreshTokenRepository.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Mongo/Repositories/RefreshTokenRepository.cs index 0f5ff52..85bbfe0 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Mongo/Repositories/RefreshTokenRepository.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Mongo/Repositories/RefreshTokenRepository.cs @@ -22,7 +22,7 @@ public async Task GetAsync(string token) { var refreshToken = await _repository.GetAsync(x => x.Token == token); - return refreshToken.AsEntity(); + return refreshToken?.AsEntity(); } public Task AddAsync(RefreshToken token) => _repository.AddAsync(token.AsDocument()); diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Mongo/Repositories/UserRepository.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Mongo/Repositories/UserRepository.cs index 9e26569..06fc494 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Mongo/Repositories/UserRepository.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Mongo/Repositories/UserRepository.cs @@ -22,14 +22,14 @@ public async Task GetAsync(Guid id) { var user = await _repository.GetAsync(id); - return user.AsEntity(); + return user?.AsEntity(); } public async Task GetAsync(string email) { var user = await _repository.GetAsync(x => x.Email == email.ToLowerInvariant()); - return user.AsEntity(); + return user?.AsEntity(); } public Task AddAsync(User user) => _repository.AddAsync(user.AsDocument()); diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Services/MessageBroker.cs b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Services/MessageBroker.cs index 8700be5..b044d3b 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Services/MessageBroker.cs +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/Services/MessageBroker.cs @@ -13,7 +13,7 @@ using SwiftParcel.Services.Identity.Application.Services; -namespace SwiftParcel.Services.Identity.Infrastructure.Mongo.Services +namespace SwiftParcel.Services.Identity.Infrastructure.Services { public class MessageBroker : IMessageBroker diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure.csproj b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure.csproj index 99d9b35..3bcb9c8 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure.csproj +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure/SwiftParcel.Services.Identity.Infrastructure.csproj @@ -33,6 +33,8 @@ + + diff --git a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity/Properties/launchSettings.json b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity/Properties/launchSettings.json index 0a3fa63..2586c28 100644 --- a/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity/Properties/launchSettings.json +++ b/SwifttParcel.Services.Identity/src/SwiftParcel.Services.Identity/Properties/launchSettings.json @@ -4,7 +4,7 @@ "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:6001", - "sslPort": 44393 + "sslPort": 443 } }, "profiles": { @@ -12,7 +12,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "applicationUrl": "https://localhost:6001;http://localhost:6010", + "applicationUrl": "http://localhost:6001;http://localhost:6010", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/Application.UnitTests.csproj b/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/Application.UnitTests.csproj new file mode 100644 index 0000000..28be063 --- /dev/null +++ b/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/Application.UnitTests.csproj @@ -0,0 +1,31 @@ + + + + net6.0 + enable + enable + + false + true + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/Commands/Handlers/SignUpHandlerTests.cs b/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/Commands/Handlers/SignUpHandlerTests.cs new file mode 100644 index 0000000..1fbe0d9 --- /dev/null +++ b/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/Commands/Handlers/SignUpHandlerTests.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SwiftParcel.Services.Identity.Application.Commands; +using SwiftParcel.Services.Identity.Application.Commands.Handlers; +using SwiftParcel.Services.Identity.Identity.Application.Services; + +namespace SwiftParcel.Services.Identity.Application.UnitTests.Commands.Handlers +{ + public class SignUpHandlerTests + { + private readonly SignUpHandler _signUpHandler; + private readonly Mock _identityServiceMock; + public SignUpHandlerTests() + { + _identityServiceMock = new Mock(); + _signUpHandler = new SignUpHandler(_identityServiceMock.Object); + } + + [Fact] + public async Task HandleAsync_CallsSignUpAsync() + { + // Arrange + var command = new SignUp(Guid.NewGuid(), "test@email.com", "password", "user", new List()); + + // Act + await _signUpHandler.HandleAsync(command); + + // Assert + _identityServiceMock.Verify(x => x.SignUpAsync(It.Is(cmd => + cmd == command + )), Times.Once); + } + } +} diff --git a/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/GlobalUsings.cs b/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/GlobalUsings.cs new file mode 100644 index 0000000..4d4f405 --- /dev/null +++ b/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/GlobalUsings.cs @@ -0,0 +1,3 @@ +global using Xunit; +global using Moq; +global using FluentAssertions; \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/Services/IdentityServiceTests.cs b/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/Services/IdentityServiceTests.cs new file mode 100644 index 0000000..5efde63 --- /dev/null +++ b/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/Services/IdentityServiceTests.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Convey.Auth; +using Microsoft.Extensions.Logging; +using SwiftParcel.Services.Identity.Application.Commands; +using SwiftParcel.Services.Identity.Application.Events; +using SwiftParcel.Services.Identity.Application.Services; +using SwiftParcel.Services.Identity.Application.UserDTO; +using SwiftParcel.Services.Identity.Core.Entities; +using SwiftParcel.Services.Identity.Core.Exceptions; +using SwiftParcel.Services.Identity.Core.Repositories; +using SwiftParcel.Services.Identity.Identity.Application.Services; +using SwiftParcel.Services.Identity.Identity.Application.UserDTO; + +namespace SwiftParcel.Services.Identity.Application.UnitTests.Services +{ + public class IdentityServiceTests + { + private readonly IdentityService _identityService; + private readonly Mock _mockUserRepository; + private readonly Mock _mockPasswordService; + private readonly Mock _mockJwtProvider; + private readonly Mock _mockMessageBroker; + private readonly Mock _mockRefreshTokenService; + private readonly Mock> _mockLogger; + public IdentityServiceTests() + { + _mockUserRepository = new Mock(); + _mockPasswordService = new Mock(); + _mockJwtProvider = new Mock(); + _mockMessageBroker = new Mock(); + _mockRefreshTokenService = new Mock(); + _mockLogger = new Mock>(); + + _identityService = new IdentityService( + _mockUserRepository.Object, + _mockPasswordService.Object, + _mockJwtProvider.Object, + _mockRefreshTokenService.Object, + _mockMessageBroker.Object, + _mockLogger.Object); + } + + [Fact] + public async Task GetAsync_WithValidId_ReturnsUserDto() + { + //Arrange + var userId = Guid.NewGuid(); + var user = new User(userId, "test@gmail.com", "password", "user", DateTime.UtcNow, new List()); + _mockUserRepository.Setup(x => x.GetAsync(userId)).ReturnsAsync(user); + + //Act + var result = await _identityService.GetAsync(userId); + + //Assert + result.Should().NotBeNull(); + result.Should().BeOfType(); + result.Should().BeEquivalentTo(new UserDto(user)); + } + + [Fact] + public async Task GetAsync_WithInvalidId_ReturnsNull() + { + //Arrange + var userId = Guid.NewGuid(); + _mockUserRepository.Setup(x => x.GetAsync(userId)).ReturnsAsync(() => null); + + //Act + var result = await _identityService.GetAsync(userId); + + //Assert + result.Should().BeNull(); + } + + [Fact] + public async Task SignInAsync_WithValidCredentials_ReturnsAuthDto() + { + //Arrange + var command = new SignIn("validEmail@gmail.com", "password"); + var userId = Guid.NewGuid(); + var user = new User(userId, command.Email, command.Password, "user", DateTime.UtcNow, new List()); + var authDto = new AuthDto(); + _mockUserRepository.Setup(x => x.GetAsync(command.Email)).ReturnsAsync(user); + _mockPasswordService.Setup(x => x.IsValid(user.Password, command.Password)).Returns(true); + _mockJwtProvider.Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>>())) + .Returns(authDto); + _mockRefreshTokenService.Setup(x => x.CreateAsync(user.Id)).ReturnsAsync("refreshToken"); + + //Act + var result = await _identityService.SignInAsync(command); + + //Assert + result.Should().NotBeNull(); + result.Should().BeOfType(); + result.Should().BeEquivalentTo(authDto); + result.RefreshToken.Should().Be("refreshToken"); + _mockMessageBroker.Verify(x => x.PublishAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async Task SignInAsync_WithInvalidEmail_ThrowsInvalidEmailException() + { + //Arrange + var command = new SignIn("invalidEmail", "password"); + + //Act and Assert + await _identityService.Invoking(x => x.SignInAsync(command)).Should().ThrowAsync() + .WithMessage($"Invalid email: {command.Email}."); + } + + [Fact] + public async Task SignInAsync_UserNotFound_ThrowsInvalidCredentialsException() + { + //Arrange + var command = new SignIn("validEmail@gmail.com", "password"); + _mockUserRepository.Setup(x => x.GetAsync(command.Email)).ReturnsAsync(() => null); + + //Act and Assert + await _identityService.Invoking(x => x.SignInAsync(command)).Should().ThrowAsync() + .WithMessage($"Invalid credentials."); + } + + [Fact] + public async Task SignInAsync_WithInvalidPassword_ThrowsInvalidCredentialsException() + { + //Arrange + var command = new SignIn("validEmail@gmail.com", "invalidPassword"); + var userId = Guid.NewGuid(); + var user = new User(userId, command.Email, command.Password, "user", DateTime.UtcNow, new List()); + _mockUserRepository.Setup(x => x.GetAsync(command.Email)).ReturnsAsync(user); + _mockPasswordService.Setup(x => x.IsValid(user.Password, command.Password)).Returns(false); + + //Act and Assert + await _identityService.Invoking(x => x.SignInAsync(command)).Should().ThrowAsync() + .WithMessage($"Invalid credentials."); + } + + [Fact] + public async Task SignUpAsync_WithValidCredentials_CreatesUser() + { + //Arrange + var command = new SignUp( + Guid.NewGuid(), + "validEmail@gmail.com", + "password", + "", + new List()); + User user = null; + SignedUp signedUp = null; + _mockUserRepository.Setup(x => x.GetAsync(command.Email)).ReturnsAsync(() => null); + _mockPasswordService.Setup(x => x.Hash(command.Password)).Returns("hashedPassword"); + _mockUserRepository.Setup(x => x.AddAsync(It.IsAny())).Callback(x => user = x); + _mockMessageBroker.Setup(x => x.PublishAsync(It.IsAny())).Callback(x => signedUp = x); + + //Act + await _identityService.SignUpAsync(command); + + //Assert + user.Should().NotBeNull(); + user.Role.Should().Be("user"); + user.Password.Should().NotBe(command.Password); + user.Password.Should().Be("hashedPassword"); + signedUp.Should().NotBeNull(); + signedUp.Should().BeEquivalentTo(new SignedUp(user.Id, user.Email, user.Role)); + _mockUserRepository.Verify(x => x.AddAsync(It.IsAny()), Times.Once); + _mockMessageBroker.Verify(x => x.PublishAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async Task SignUpAsync_WithInvalidEmail_ThrowsInvalidEmailException() + { + //Arrange + var command = new SignUp( + Guid.NewGuid(), + "invalidEmail", + "password", + "user", + new List()); + + //Act and Assert + await _identityService.Invoking(x => x.SignUpAsync(command)).Should().ThrowAsync() + .WithMessage($"Invalid email: {command.Email}."); + } + + [Fact] + public async Task SignUpAsync_WhenEmailInUse_ThrowsEmailInUseException() + { + //Arrange + var command = new SignUp( + Guid.NewGuid(), + "emailInUse@gmail.com", + "password", + "user", + new List()); + var user = new User(Guid.NewGuid(), command.Email, command.Password, "user", DateTime.UtcNow, new List()); + _mockUserRepository.Setup(x => x.GetAsync(command.Email)).ReturnsAsync(user); + + //Act and Assert + await _identityService.Invoking(x => x.SignUpAsync(command)).Should().ThrowAsync() + .WithMessage($"Email {command.Email} is already in use."); + } + } +} diff --git a/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/Services/RefreshTokenServiceTests.cs b/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/Services/RefreshTokenServiceTests.cs new file mode 100644 index 0000000..b58f642 --- /dev/null +++ b/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Application.UnitTests/Application.UnitTests/Services/RefreshTokenServiceTests.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SwiftParcel.Services.Identity.Application.Services; +using SwiftParcel.Services.Identity.Core.Entities; +using SwiftParcel.Services.Identity.Core.Repositories; +using SwiftParcel.Services.Identity.Core.Exceptions; +using SwiftParcel.Services.Identity.Application.UserDTO; +using SwiftParcel.Services.Identity.Application.Exceptions; + +namespace SwiftParcel.Services.Identity.Application.UnitTests.Services +{ + public class RefreshTokenServiceTests + { + private readonly RefreshTokenService _refreshTokenService; + private readonly Mock _mockRefreshTokenRepository; + private readonly Mock _mockUserRepository; + private readonly Mock _mockJwtProvider; + private readonly Mock _mockRgen; + public RefreshTokenServiceTests() + { + _mockRefreshTokenRepository = new Mock(); + _mockUserRepository = new Mock(); + _mockJwtProvider = new Mock(); + _mockRgen = new Mock(); + + _refreshTokenService = new RefreshTokenService( + _mockRefreshTokenRepository.Object, + _mockUserRepository.Object, + _mockJwtProvider.Object, + _mockRgen.Object + ); + } + + [Fact] + public async Task CreateAsync_ReturnsValidToken() + { + //Arrange + var userId = Guid.NewGuid(); + var expectedToken = "validToken"; + _mockRgen.Setup(x => x.Generate(30, true)).Returns(expectedToken); + + //Act + var result = await _refreshTokenService.CreateAsync(userId); + + //Assert + result.Should().BeEquivalentTo(expectedToken); + _mockRefreshTokenRepository.Verify(x => x.AddAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async Task RevokeAsync_WithValidToken_RevokesToken() + { + //Arrange + var refreshToken = "validToken"; + var token = new RefreshToken(new AggregateId(), Guid.NewGuid(), refreshToken, DateTime.UtcNow); + _mockRefreshTokenRepository.Setup(x => x.GetAsync(refreshToken)).ReturnsAsync(token); + + //Act + await _refreshTokenService.RevokeAsync(refreshToken); + + //Assert + token.Should().NotBeNull(); + token.RevokedAt.Should().NotBeNull(); + token.Revoked.Should().BeTrue(); + _mockRefreshTokenRepository.Verify(x => x.UpdateAsync(token), Times.Once); + } + + [Fact] + public async Task RevokeAsync_WithInvalidToken_ThrowsInvalidRefreshTokenException() + { + //Arrange + var refreshToken = "invalidToken"; + _mockRefreshTokenRepository.Setup(x => x.GetAsync(refreshToken)).ReturnsAsync(() => null); + + //Act and Assert + await _refreshTokenService.Invoking(x => x.RevokeAsync(refreshToken)).Should().ThrowAsync(); + } + + [Fact] + public async Task UseAsync_WithValidToken_ReturnsAuthDto() + { + //Arrange + var refreshToken = "validToken"; + var token = new RefreshToken(new AggregateId(), Guid.NewGuid(), refreshToken, DateTime.UtcNow); + var user = new User(Guid.NewGuid(), "test@gmail.com", "password", "user", DateTime.UtcNow, new List()); + _mockRefreshTokenRepository.Setup(x => x.GetAsync(refreshToken)).ReturnsAsync(token); + _mockUserRepository.Setup(x => x.GetAsync(token.UserId)).ReturnsAsync(user); + _mockJwtProvider.Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>>())) + .Returns(new AuthDto()); + + //Act + var result = await _refreshTokenService.UseAsync(refreshToken); + + //Assert + result.Should().NotBeNull(); + result.RefreshToken.Should().BeEquivalentTo(refreshToken); + } + + [Fact] + public async Task UseAsync_WithInvalidToken_ThrowsInvalidRefreshTokenException() + { + //Arrange + var refreshToken = "invalidToken"; + _mockRefreshTokenRepository.Setup(x => x.GetAsync(refreshToken)).ReturnsAsync(() => null); + + //Act and Assert + await _refreshTokenService.Invoking(x => x.UseAsync(refreshToken)).Should().ThrowAsync(); + } + + [Fact] + public async Task UseAsync_WithRevokedToken_ThrowsRevokedRefreshTokenException() + { + var refreshToken = "revokedToken"; + var token = new RefreshToken(new AggregateId(), Guid.NewGuid(), refreshToken, DateTime.UtcNow, DateTime.UtcNow); + _mockRefreshTokenRepository.Setup(x => x.GetAsync(refreshToken)).ReturnsAsync(token); + + //Act and Assert + await _refreshTokenService.Invoking(x => x.UseAsync(refreshToken)).Should().ThrowAsync(); + } + + [Fact] + public async Task UseAsync_WithInvalidTokenUserId_ThrowsUserNotFoundException() + { + var refreshToken = "tokenWithInvalidUserId"; + var token = new RefreshToken(new AggregateId(), Guid.NewGuid(), refreshToken, DateTime.UtcNow); + _mockRefreshTokenRepository.Setup(x => x.GetAsync(refreshToken)).ReturnsAsync(token); + _mockUserRepository.Setup(x => x.GetAsync(token.UserId)).ReturnsAsync(() => null); + + //Act and Assert + await _refreshTokenService.Invoking(x => x.UseAsync(refreshToken)).Should().ThrowAsync() + .WithMessage($"User with ID: '{token.UserId}' was not found."); + } + } +} diff --git a/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Infrastructure.UnitTests/Infrastructure.UnitTests/GlobalUsings.cs b/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Infrastructure.UnitTests/Infrastructure.UnitTests/GlobalUsings.cs new file mode 100644 index 0000000..4d4f405 --- /dev/null +++ b/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Infrastructure.UnitTests/Infrastructure.UnitTests/GlobalUsings.cs @@ -0,0 +1,3 @@ +global using Xunit; +global using Moq; +global using FluentAssertions; \ No newline at end of file diff --git a/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Infrastructure.UnitTests/Infrastructure.UnitTests/Infrastructure.UnitTests.csproj b/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Infrastructure.UnitTests/Infrastructure.UnitTests/Infrastructure.UnitTests.csproj new file mode 100644 index 0000000..f2d701d --- /dev/null +++ b/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Infrastructure.UnitTests/Infrastructure.UnitTests/Infrastructure.UnitTests.csproj @@ -0,0 +1,31 @@ + + + + net6.0 + enable + enable + + false + true + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Infrastructure.UnitTests/Infrastructure.UnitTests/Services/MessageBrokerTests.cs b/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Infrastructure.UnitTests/Infrastructure.UnitTests/Services/MessageBrokerTests.cs new file mode 100644 index 0000000..82804e3 --- /dev/null +++ b/SwifttParcel.Services.Identity/tests/SwiftParcel.Services.Identity.Infrastructure.UnitTests/Infrastructure.UnitTests/Services/MessageBrokerTests.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Convey.CQRS.Events; +using Convey.MessageBrokers; +using Convey.MessageBrokers.Outbox; +using Convey.MessageBrokers.RabbitMQ; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using OpenTracing; +using SwiftParcel.Services.Identity.Application.Events; +using SwiftParcel.Services.Identity.Application.Services; +using SwiftParcel.Services.Identity.Infrastructure.Services; + +namespace SwiftParcel.Identity.Services.Infrastructure.UnitTests.Services +{ + public class MessageBrokerTests + { + private readonly MessageBroker _messageBroker; + private readonly Mock _mockBusPublisher; + private readonly Mock _mockMessageOutbox; + private readonly Mock _mockContextAccessor; + private readonly Mock _mockHttpContextAccessor; + private readonly Mock _mockMessagePropertiesAccessor; + private readonly Mock _mockTracer; + private readonly Mock> _mockLogger; + + public MessageBrokerTests() + { + _mockBusPublisher = new Mock(); + _mockMessageOutbox = new Mock(); + _mockContextAccessor = new Mock(); + _mockHttpContextAccessor = new Mock(); + _mockMessagePropertiesAccessor = new Mock(); + _mockTracer = new Mock(); + _mockLogger = new Mock>(); + + _messageBroker = new MessageBroker(_mockBusPublisher.Object, _mockMessageOutbox.Object, _mockContextAccessor.Object, + _mockHttpContextAccessor.Object, _mockMessagePropertiesAccessor.Object, new RabbitMqOptions(), + _mockTracer.Object, _mockLogger.Object); + } + + [Fact] + public async Task PublishAsync_WithEventsAndOutboxDisabled_PublishesEvents() + { + //Arrange + var events = new List + { + new SignedIn(Guid.NewGuid(), "user") + }; + _mockMessageOutbox.Setup(x => x.Enabled).Returns(false); + + //Act + await _messageBroker.PublishAsync(events); + + //Assert + _mockMessageOutbox.Verify( + x => x.SendAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny>()), + Times.Never + ); + _mockBusPublisher.Verify( + x => x.PublishAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny>()), Times.Exactly(events.Count) + ); + } + + [Fact] + public async Task PublishAsync_WithEventsAndOutboxEnabled_SendsMessagesToOutbox() + { + //Arrange + var events = new List + { + new SignedIn(Guid.NewGuid(), "user") + }; + _mockMessageOutbox.Setup(x => x.Enabled).Returns(true); + + //Act + await _messageBroker.PublishAsync(events); + + //Assert + _mockMessageOutbox.Verify( + x => x.SendAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny>()), + Times.Exactly(events.Count) + ); + _mockBusPublisher.Verify( + x => x.PublishAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny>()), Times.Never + ); + } + + [Fact] + public async Task PublishAsync_WithoutEvents_Returns() + { + //Arrange + List events = null; + + //Act + await _messageBroker.PublishAsync(events); + + //Assert + _mockMessageOutbox.Verify( + x => x.SendAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny>()), + Times.Never + ); + _mockBusPublisher.Verify( + x => x.PublishAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny>()), Times.Never + ); + } + + [Fact] + public async Task PublishAsync_WithNullEventAndOutboxDisabled_PublishesOneLessEvent() + { + //Arrange + var events = new List + { + new SignedIn(Guid.NewGuid(), "user"), + null + }; + _mockMessageOutbox.Setup(x => x.Enabled).Returns(false); + + //Act + await _messageBroker.PublishAsync(events); + + //Assert + _mockMessageOutbox.Verify( + x => x.SendAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny>()), + Times.Never + ); + _mockBusPublisher.Verify( + x => x.PublishAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny>()), Times.Exactly(events.Count - 1) + ); + } + } +}